canvasで少しずつ作るブロック崩し(4/5)
前回は右クリックでボールを出現、左クリックでバーを上下させボールを打ち返す機能を加えました。今回はいよいよブロックを作ってボールが当たると消える機能をつけてみます。
[rtoc_mokuji title=”” title_display=”” heading=”h3″ list_h2_type=”” list_h3_type=”” display=”” frame_design=”” animation=””]
目次
完成品
左クリック: バーを下げる
右クリック: ボール出現
ソースコード
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript">
(function() {
var canvas;
var ctx;
var mouseX;
var mouseY;
// バーの情報
var BAR = {
'HEIGHT' : 10,
'WIDTH' : 50,
'UNDER' : 25,
'X' : 0,
'Y' : 0,
'PUSH' : 10,
'Vx' : 0,
'Vx0' : 0,
'Vy' : 0,
'Vy0' : 150,
'M' : 5,
'E' : 0.7,
'E0' : 10,
};
// フィールドの情報
var FIELD = {
'HEIGHT' : 0,
'WIDTH' : 0,
'GRAVITY' : 588, // 重力加速度(0.9 * FPS)
'FPS' : 60, // frame per second
'E' : 0.7,
'E0' : 10,
};
// ボールの情報
var BALL = [];
var Ball = function() {
this.Alive = 0;
this.X = 0;
this.Y = 0;
this.Vy = 0;
this.Vx = 100;
this.Vx0 = 0;
this.Vy0 = 200;
this.RADIUS = 5;
this.M = 1;
this.HUE = 0.5;
};
// ブロックの情報
var BLOCK = [];
var Block = function() {
this.Alive = 0;
this.WIDTH = 30;
this.HEIGHT = 10;
this.X = 0;
this.Y = 0;
};
// 初期化処理
function initialize() {
canvas = document.getElementById('canvas');
if(!canvas && !canvas.getContext) {
return false;
}
// キャンバス作成
ctx = canvas.getContext('2d');
FIELD.WIDTH = ctx.canvas.width ;
FIELD.HEIGHT = ctx.canvas.height;
// バーの設定
mouseX = FIELD.WIDTH/2; // バーの初期位置は中心
BAR.X = mouseX;
BAR.Y = FIELD.HEIGHT-BAR.UNDER;
// ブロックの生成
createBlocks();
// 各種イベント設定
canvas.addEventListener('mousemove', getMouseCoordinate, false);
canvas.addEventListener('mousedown', pushBar, false);
canvas.addEventListener('mouseup', popBar, false);
canvas.addEventListener('contextmenu', putBall, false);
setInterval(drawField, 1000/FIELD.FPS);
};
// マウス座標の更新
function getMouseCoordinate(e) {
var rect = e.target.getBoundingClientRect();
mouseX = Math.floor(e.clientX - rect.left);
mouseY = Math.floor(e.clientY - rect.top);
};
// ボールの生成
function putBall(e) {
e.preventDefault();
// ボールの初期位置は中心
var tail = BALL.length;
BALL[tail] = new Ball();
BALL[tail].Alive = 1;
BALL[tail].X = BAR.X;
BALL[tail].Y = FIELD.HEIGHT-BAR.UNDER;
BALL[tail].Vy = BALL[tail].Vy0 * (-1);
};
// バーの収縮
function pushBar(e) {
if (!e.pageX) {
e = event.touches[0];
}
if (e.button == 0) {
setTimeout(pushBarEvent, 1000/FIELD.FPS);
}
};
// バーの反発
function popBar(e) {
if (!e.pageX) {
e = event.touches[0];
}
if (e.button == 0) {
setTimeout(popBarEvent, 1000/FIELD.FPS);
}
};
// バーの収縮処理
function pushBarEvent() {
if (BAR.Y < FIELD.HEIGHT - BAR.UNDER + BAR.PUSH) {
BAR.Vy = BAR.Vy0;
BAR.Y += BAR.Vy * (FIELD.FPS/1000);
setTimeout(pushBarEvent, 1000/FIELD.FPS);
} else {
BAR.Vy = 0;
BAR.Y = FIELD.HEIGHT - BAR.UNDER + BAR.PUSH;
}
};
var BarTimer;
// バーの反発処理
function popBarEvent() {
if (BAR.Y > FIELD.HEIGHT-BAR.UNDER) {
BAR.Vy = BAR.Vy0 * (-1);
BAR.Y += BAR.Vy * (FIELD.FPS/1000);
setTimeout(popBarEvent, 1000/FIELD.FPS);
} else {
BAR.Y = FIELD.HEIGHT-BAR.UNDER;
setTimeout(resetBarSpeed, 100);
}
};
function resetBarSpeed() {
BAR.Vy = 0;
};
// ブロックの生成
function createBlocks() {
var bxmax = 13;
var bymax = 6;
var btop = 20;
var bleft = 20;
var bidx = 0;
var bint = 5;
for (var x = 0; x < bxmax; x++) {
for (var y = 0; y < bymax; y++) {
BLOCK[bidx] = new Block();
BLOCK[bidx].X = x * BLOCK[bidx].WIDTH + btop + (x * bint);
BLOCK[bidx].Y = y * BLOCK[bidx].HEIGHT + bleft + (y * bint);
BLOCK[bidx].Alive = 1;
bidx++;
}
}
};
// 画面の描画
function drawField() {
calcBallP();
drawBack();
drawBall();
drawBar();
drawBlock();
};
// ボール位置計算
function calcBallP() {
for (var i = 0; i < BALL.length; i++) {
if (BALL[i] == null) {
continue;
}
// 生存しているボールのみ計算
if (BALL[i].Alive == 1) {
// 床・天井接触
if (BALL[i].Y <= 0 || FIELD.HEIGHT <= BALL[i].Y) {
if (BALL[i].Y <= 0) {
// 天井
BALL[i].Y = FIELD.E0;
BALL[i].Vy = BALL[i].Vy * FIELD.E * (-1)
} else {
// 床
// 床に接触したボールは死亡
BALL[i].Alive = 0;
BALL[i].Vx = 0;
BALL[i].Vy = 0;
}
}
// 壁接触
if (BALL[i].X <= 0 || FIELD.WIDTH <= BALL[i].X) {
if (BALL[i].X <= 0) {
BALL[i].X = FIELD.E0;
} else {
BALL[i].X = FIELD.WIDTH - FIELD.E0;
}
BALL[i].Vx = BALL[i].Vx * (-1);
}
// バー接触
if (BAR.X <= BALL[i].X && BALL[i].X <= BAR.X + BAR.WIDTH) {
if (Math.abs(BALL[i].Y - BAR.Y) <= BAR.HEIGHT) {
// バーとボールの境界でバタつきを防ぐための処置
BALL[i].Y = BAR.Y - BAR.E0;
// バー接触後の速度計算
BALL[i].Vy = (BALL[i].M * BALL[i].Vy - BAR.M * BAR.Vy) / BALL[i].M;
// Vyが初速度より減速した場合は、初速度に戻す
BALL[i].Vy = Math.abs(BALL[i].Vy) > BALL[i].Vy0 ? (BALL[i].Vy * BAR.E * (-1)) : (BALL[i].Vy0 * (-1));
}
}
// ブロック接触
for (var bi = 0; bi < BLOCK.length; bi++) {
var xtouch = 0;
var ytouch = 0;
if (BLOCK[bi] == null) {
continue;
}
if (BLOCK[bi].Alive == 0) {
continue;
}
if ((Math.abs(BALL[i].X - BLOCK[bi].X) < BALL[i].RADIUS/2 + BLOCK[bi].WIDTH/2) &&
(Math.abs(BALL[i].Y - BLOCK[bi].Y) < BALL[i].RADIUS/2 + BLOCK[bi].HEIGHT/2)) {
BLOCK[bi].Alive = 0;
BALL[i].Vy *= (-1);
}
}
// 縦計算
BALL[i].Vy += FIELD.GRAVITY * (1/FIELD.FPS);
BALL[i].Y += BALL[i].Vy * (1/FIELD.FPS);
// 横計算
BALL[i].X += BALL[i].Vx * (1/FIELD.FPS);
}
}
deleteAllDeadBall();
};
// 死亡したボールを削除
function deleteAllDeadBall() {
var isDeadBall = 1;
while(isDeadBall != 0) {
isDeadBall = 0;
for (var i = 0; i < BALL.length; i++) {
if (BALL[i] == null) {
continue;
}
if(BALL[i].Alive == 0) {
delete BALL[i];
BALL.splice(i,1);
isDeadBall = 1;
break;
}
}
}
}
function drawBack() {
ctx.fillStyle = 'rgb(0, 0, 0)';
ctx.fillRect(0, 0, FIELD.WIDTH, FIELD.HEIGHT);
};
// ボールの描画
function drawBall() {
ctx.save();
// 生存しているボールの数だけ描画
for(var i = 0; i < BALL.length; i++) {
if (BALL[i] == null) {
continue;
}
if (BALL[i].Alive) {
// 円の描画設定
ctx.beginPath();
ctx.arc(BALL[i].X, BALL[i].Y, BALL[i].RADIUS, 0, 2*Math.PI, true);
ctx.closePath();
// 色設定
BALL[i].HUE += 0.5;
ctx.strokeStyle = 'hsl(' + BALL[i].HUE + ', 50%, 50%)';
ctx.fillStyle = 'hsl(' + BALL[i].HUE + ', 50%, 50%)';
ctx.shadowColor = 'hsl(' + BALL[i].HUE + ', 50%, 50%)';
}
// 描画実行
ctx.stroke();
ctx.fill();
}
ctx.restore();
};
// バーの描画
function drawBar() {
var delay = 1;
BAR.X = (mouseX + delay * BAR.X) / (delay+1);
// 色設定
ctx.fillStyle = 'rgb(255,255,255)';
// バーの描画設定
ctx.fillRect(BAR.X, BAR.Y, BAR.WIDTH, BAR.HEIGHT);
};
// ブロックの描画
function drawBlock() {
// 色設定
ctx.fillStyle = 'rgb(0,255,0)';
for (var i = 0; i < BLOCK.length; i++) {
if (BLOCK[i] == null) {
continue;
}
if (BLOCK[i].Alive) {
ctx.fillRect(BLOCK[i].X, BLOCK[i].Y, BLOCK[i].WIDTH, BLOCK[i].HEIGHT);
}
}
}
// 初期化イベント
window.addEventListener('load', initialize, false);
} ) ();
</script>
</head>
<body>
<canvas id='canvas' width=500 height=300></canvas>
<p>
左クリック: バーを下げる <br />
右クリック: ボール出現
</p>
</body>
</html>
ボールとブロックの当たり判定がイマイチなので変な動きが多いです。
他にも色々ツッコミどころはありますが、改善点含めて次回でまとめて完成としたいです。
関連ページ
canvasで少しずつ作るブロック崩し(1/5) - Segmentation Fault
canvasで少しずつ作るブロック崩し(2/5) - Segmentation Fault
canvasで少しずつ作るブロック崩し(3/5) - Segmentation Fault
canvasで少しずつ作るブロック崩し(5/5) - Segmentation Fault
コメント