canvasで少しずつ作るブロック崩し(3/5)
前回はボールを1個だけ出現させてバーで跳ね返せる機能を作りました。複数のボールを出現する機能とクリックでバーを上下させボールを打ち返せる機能をつけてみます。
[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 width;
var height;
var mouseX;
var mouseY;
var barTimerID;
var pushTimerID;
var popTimerID;
var ball_id = 0;
var ball = [];
var BAR = {
'HEIGHT' : 10,
'WIDTH' : 50,
'UNDER' : 25,
'X' : 0,
'Y' : 0,
'PUSH' : 10,
'Vx' : 0,
'Vx0' : 0,
'Vy' : 0,
'Vy0' : 100,
'M' : 10,
'E' : 0.7,
'E0' : 10,
};
var BALL = {
'ALIVE' : 0,
'X' : 0,
'Y' : 0,
'Vx' : 0, // ボール速度(x成分)
'Vx0' : 100, // ボールの初速
'Vy' : 0, // ボール速度(y成分)
'Vy0' : 600, // ボールの初速(y成分の最低速度)
'RADIUS' : 5,
'M' : 1,
};
var FIELD = {
'GRAVITY' : 588, // 重力加速度(0.9 * FPS)
'FPS' : 60, // frame per second
'E' : 0.7,
'E0' : 10,
};
var Ball = function(id) {
this.id = id;
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.timerID;
};
// 初期化処理
function initialize() {
canvas = document.getElementById('canvas');
if(!canvas && !canvas.getContext) {
return false;
}
ctx = canvas.getContext('2d');
width = ctx.canvas.width ;
height = ctx.canvas.height;
// バーの初期位置は中心
mouseX = width/2;
BAR.X = mouseX;
BAR.Y = height-BAR.UNDER;
canvas.addEventListener('mousemove', getMouseCoordinate, false);
canvas.addEventListener('mousedown', pushBar, false);
canvas.addEventListener('mouseup', popBar, false);
canvas.addEventListener('contextmenu', putBall, false);
setInterval(calcBallP, 1000/FIELD.FPS);
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();
// ボールの初期位置は中心
ball[ball_id] = new Ball(ball_id);
ball[ball_id].Alive = 1;
ball[ball_id].X = BAR.X;
ball[ball_id].Y = height-BAR.UNDER;
ball[ball_id].Vy = ball[ball_id].Vy0 * (-1);
ball_id++;
console.log(ball.length);
};
// バーの収縮
function pushBar(e) {
if (!e.pageX) {
e = event.touches[0];
}
if (e.button == 0) {
//BAR.Y += BAR.PUSH;
clearInterval(popTimerID);
clearInterval(pushTimerID);
pushTimerID = setInterval(pushBarEvent, 1000/FIELD.FPS);
}
};
// バーの反発
function popBar(e) {
if (!e.pageX) {
e = event.touches[0];
}
if (e.button == 0) {
//BAR.Y = height-BAR.UNDER;
clearInterval(popTimerID);
clearInterval(pushTimerID);
pushTimerID = setInterval(popBarEvent, 1);
}
};
// バーの収縮処理
function pushBarEvent() {
if (BAR.Y < height - BAR.UNDER + BAR.PUSH) {
BAR.Vy = BAR.Vy0;
BAR.Y += BAR.Vy * (FIELD.FPS/1000);
} else {
BAR.Vy = 0;
BAR.Y = height - BAR.UNDER + BAR.PUSH;
}
};
// バーの反発処理
function popBarEvent() {
if (BAR.Y > height-BAR.UNDER) {
BAR.Vy = BAR.Vy0 * (-1);
BAR.Y += BAR.Vy * (FIELD.FPS/1000);
} else {
BAR.Vy = 0;
BAR.Y = height-BAR.UNDER;
}
};
// ボール位置計算
function calcBallP() {
for (var i = 0; i < ball.length; i++) {
// 生存しているボールのみ計算
if (ball[i].Alive) {
// 床・天井接触
if (ball[i].Y <= 0 || 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 || width <= ball[i].X) {
if (ball[i].X <= 0) {
ball[i].X = FIELD.E0;
} else {
ball[i].X = 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));
}
}
// 縦計算
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);
}
}
};
// 画面の描画
function drawField() {
drawBack();
drawBall();
drawBar();
};
function drawBack() {
ctx.fillStyle = 'rgba(0, 0, 0, 0.1)';
ctx.fillRect(0, 0, width, height);
};
var hue = 0.5;
function drawBall() {
ctx.save();
// 生存しているボールの数だけ描画
for(var i = 0; i < ball.length; i++) {
if (ball[i].Alive) {
// 円の描画設定
ctx.beginPath();
ctx.arc(ball[i].X, ball[i].Y, ball[i].RADIUS, 0, 2*Math.PI, true);
ctx.closePath();
// 色設定
hue += 0.5;
ctx.strokeStyle = 'hsl(' + hue + ', 50%, 50%)';
ctx.fillStyle = 'hsl(' + hue + ', 50%, 50%)';
ctx.shadowColor = 'hsl(' + hue + ', 50%, 50%)';
ctx.shadowBlur = 10;
}
// 描画実行
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);
};
// 初期化イベント
window.addEventListener('load', initialize, false);
} ) ();
</script>
</head>
<body>
<canvas id='canvas' width=500 height=300></canvas>
</body>
</html>
次回はブロックを作ってゲームっぽくしていきます。
あと、バーで球を打つ時の判定は改善しないとダメそう。
関連ページ
canvasで少しずつ作るブロック崩し(1/5) - Segmentation Fault
canvasで少しずつ作るブロック崩し(2/5) - Segmentation Fault
canvasで少しずつ作るブロック崩し(その4) - Segmentation Fault
canvasで少しずつ作るブロック崩し(5/5) - Segmentation Fault
コメント