canvasで少しずつ作るブロック崩し(3/5)
前回はボールを1個だけ出現させてバーで跳ね返せる機能を作りました。複数のボールを出現する機能とクリックでバーを上下させボールを打ち返せる機能をつけてみます。
機能追加版
左クリック: バーを下げる
右クリック: ボール出現
ソースコード
<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