html5 Canvasでシューティングゲーム


http://www.geocities.jp/psipage/html5/html5_canvas_shot.html
書いてみた。
boxの上半分にターゲットが現れ、下半分をクリックで弾を発射。
パラメータの値をいじると面白いかも。

context.shadowBlurについて

 contextに影(ぼかし)を入れるためにshadowBlurを適用したら処理が重くなった。対応してるブラウザも少ない。
shadowBlur プロパティ - Canvasリファレンス - HTML5.JP

気になったこと

 マウスの座標はキャンバスからの相対位置ではなく絶対位置。なのでCanvas領域の上にバナー広告などが入ることを想定すると、相対位置で考えると思い通りに動かない(ことがあるかも)。とりあえずCanvas領域の位置は絶対配置で指定した。

<canvas id="canvassample" width="640" height="320" style="position: absolute; top:80px;"></canvas>

 position: absolute;したら、それ以降の要素すべて絶対配置にする必要あり?よくわかんない。

ソース

<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>Example3(Shooting): HTML5 canvas</title>
<!--[if IE]>
<script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
</head>
<body>
<canvas id="canvassample" width="640" height="320" style="position: absolute; top:80px;"
	onmousedown="mouseClicked()"
	onmousemove="mouseMove(event.clientX, event.clientY)"
	onmouseup="mouseUp()"></canvas>
<script type="text/javascript">
window.onload = function() {
	setInterval('draw()',10);
};
var w_size = 640;
var h_size = 320;
var y = 20;
var oval_size = 10;
;
var mouse_X = 0;
var mouse_Y = 0;
function draw() {
	/* canvas要素のノードオブジェクト */
	var canvas = document.getElementById('canvassample');
	/* canvas要素の存在チェックとCanvas未対応ブラウザの対処 */
	if ( ! canvas || ! canvas.getContext ) {
		document.getElementById('ms').innerHTML = "This browser is not supported."
		return false;
	}
	/* 2Dコンテキスト */
	var ctx = canvas.getContext('2d');
	ctx.clearRect(0,0,w_size,h_size);
	ctx.strokeStyle = "black";
	// 枠
	ctx.strokeRect(0,0,w_size,h_size);
	// 敵との境界
	ctx.moveTo(0, (h_size / 2)+50);
	ctx.lineTo(w_size, (h_size / 2)+50);
	ctx.stroke();
	
	enemy(ctx);
	ctx.clearRect(1, (h_size / 2)+50+1,w_size-2,  (h_size / 2)-52);
	bullet(ctx);
	ctx.beginPath();
	//ctx.arc(mouse_X, mouse_Y, oval_size, 0, Math.PI*2, false);
	ctx.stroke();
	ctx.closePath();
};

// 弾描画
var bullet_array = [];
var end = 0;
function bullet(ctx) {
	ctx.beginPath();
	ctx.strokeStyle = "#a0410d";
	ctx.lineCap = "round";
	ctx.lineWidth = 1;
	for(var i=end;i<bullet_array.length;i++) {
		ctx.moveTo(bullet_array[i][0], bullet_array[i][1] - 10);
		ctx.lineTo(bullet_array[i][0], bullet_array[i][1]);
		ctx.stroke();
		if (bullet_array[i][1] >= -10) {
			bullet_array[i][1] -= 4;
		} else {
			end += 1;
		}
	}
};

// 敵描画&当たり判定 
var enemy_array = [];
var enemy_size = 30;
var enemy_wait_time = 0;
var hit_count = 0;
var enemy_leave = 0;
var enemy_color = [];
var enemy_size_array = [];
var hit = 0;
var purupuru1 = 3;
var _hit = 0;
var enemy_born_interval = 100;
var enemy_num = 10;
function enemy(ctx) {
	if (enemy_wait_time == 0) {
		// create enemy
		var tmp = [];
		tmp[0] = Math.floor(Math.random() * w_size);
		tmp[1] = Math.floor(Math.random() * (h_size / 2));
		enemy_array[enemy_array.length] = tmp;
		setColor(enemy_color.length);
		enemy_size_array[enemy_size_array.length] = 20 +Math.floor(Math.random() * enemy_size);
	}
	var enemy_count = 0;
	for (var i=enemy_leave;i<enemy_array.length;i++) {
		if (!(enemy_size_array[i] <= 0)) {
			for (var ii=end;ii<bullet_array.length;ii++) {
				if (enemy_array[i][0] - enemy_size_array[i] <= bullet_array[ii][0] && bullet_array[ii][0] <= enemy_array[i][0] + enemy_size_array[i]
					&& enemy_array[i][1] - enemy_size_array[i] <= bullet_array[ii][1] && bullet_array[ii][1] <= enemy_array[i][1] + enemy_size_array[i]) {
					enemy_size_array[i] -= 1;
					hit += 1;
					document.getElementById('debug').innerHTML = "Hit!: " + hit;
					if (hit > 1500) {
						document.getElementById('debug').innerHTML += " ★★★";
					} else if(hit > 1000) {
						document.getElementById('debug').innerHTML += " ★★☆";
					} else if(hit > 500) {
						document.getElementById('debug').innerHTML += " ★☆☆";
					}
					ctx.fillStyle = "orange";
					//enemy_array[i][0] = -1;
					//enemy_array[i][1] = -1;
				}
			}
		}
		if (!(enemy_size_array <= 0)) {
			if ((Math.random() * 2) < 1) {
				enemy_array[i][0] -= (Math.random() * purupuru1);
			} else {
				enemy_array[i][0] += (Math.random() * purupuru1);
			}
			if ((Math.random() * 2)  < 1) {
				enemy_array[i][1] -= (Math.random() * purupuru1);
			} else {
				enemy_array[i][1] += (Math.random() * purupuru1);
			}
			// 敵描画
			ctx.beginPath();
			ctx.fillStyle = enemy_color[i];
			ctx.arc(enemy_array[i][0], enemy_array[i][1], enemy_size_array[i], 0, Math.PI*2, false);
			ctx.fill();
			ctx.closePath();
			enemy_count += 1;
		}
	}
	enemy_wait_time += 1;
	if (enemy_wait_time < enemy_born_interval) {
		return;
	} else {
		if (enemy_count > enemy_num) {
			enemy_leave += 1;
		}
		enemy_wait_time = 0;
	}
};

// 弾の移動
var bullect_count = 0;
var bullect_u = 1;
function shot() {
	var tmp = [];
	tmp[0] = mouse_X -7;
	tmp[1] = mouse_Y -67;
	bullet_array[bullect_count] = tmp;
	bullect_count += bullect_u;
};

function setColor(index) {
	var color = Math.floor(Math.random() * 0xFFFFFF).toString(16);	//#RRGGBBを取得
	for(count = color.length; count < 6; count++){
		color = "0" + color;     				//上位に0を補完する
	}
	enemy_color[index] = "#" + color;
};

function setColors() {
	for (var i=enemy_leave;i<enemy_array.length;i++) {
		setColor(i);
	}
};
	
function mouseClicked() {
	if (mouse_Y >= (h_size / 2) + 50 + 87) {
		shot();
	}
};

function mouseUp() {
	trigger = 0;
};

function mouseMove(x,y) {
	mouse_X = x;
	mouse_Y = y;
	document.getElementById('debug2').innerHTML = "MousePos: x="+mouse_X + " y="+mouse_Y;
};

function change() {
	enemy_num = parseInt(document.getElementById('enemy_num').value);
	enemy_size = parseInt(document.getElementById('enemy_size').value);
	enemy_born_interval = parseInt(document.getElementById('enemy_born_interval').value);
	purupuru1 = parseInt(document.getElementById('purupuru1').value);
};

</script>
<div style="position: absolute;top:400px;">.</div>
<div id="debug" style="position: absolute;top:420px;">Hit!: 0</div>

<div id="debug2" style="position: absolute;top:440px;"> </div>
<p style="position: absolute;top:460px;">

<form style="position: absolute; top:460px;">
	目標の最大数:<input type="text" id="enemy_num" value="10"  onchange="change()"></input>
	目標の大きさ:<input type="text" id="enemy_size" value="30" onchange="change()"></input>
	目標が表示される間隔:<input type="text" id="enemy_born_interval" value="100" onchange="change()"></input>
	プルプル係数:<input type="text" id="purupuru1" value="3" onchange="change()"></input>
</form>
</p>
<p style="position: absolute;top:500px;"><a href="http://d.hatena.ne.jp/juntk/20100425/1272201655">HTML5 Canvasでシューティングっぽいもの</a></p>
</body>
</html>