さて、カエル王子を引っ張ってピヨ吉を退治する JavaScript だ。
こんな感じにしてみた。
※canvas サイズは 320×400 に固定
※ピヨ吉構造体を用意、HP や X,Y 座標メンバを保持
※ソレを画面に配置する数だけ配列にしておく
※タイマーでカエルが動く毎に for 文で全数チェック
※ピヨ吉にヒットしていれば HP を減らし 0 なら非表示にして通過する
※ピヨ吉すべての HP がゼロになればクリア
とりあえず二回ヒットするとピヨ吉が消える暫定コードに。
カエル王子は 32x32px にしてみたけど iPhone では少し引っ張り辛いかも。
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>ひよこを倒す 01</title> <!-- for Smart Phone --> <meta name="viewport" content=" width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" /> <style> body { margin: 0; overflow: hidden; -webkit-text-size-adjust: 100%; } #ID_CANVAS { background-color: #FF7700; } </style> <script type="text/javascript"><!-- var canvas = null; var context = null; var ball = null; var piyos = []; // Constant const INIT_X = 160; // X origin of the image const INIT_Y = 280; // Y origin of the image const OFFSET = 16; // Half of the image size const INTERVAL = 20; // n/1000 ms const LENGTH = 20; // Distance the image moves const COUNT = 250; // INTERVAL*n ms // struct var piyokichi = function(x, y, hp) { this.x = x; this.y = y; this.hp = hp; this.img = null; } var ballData = function() { this.timer_id = -1; this.timer_count = 0; this.x = 0; this.y = 0; this.rotation = 0; } var BallData = new ballData(); /* * Touch Event Handler **/ var onBallTouchStart = function(e) { e.preventDefault(); } var onBallTouchMove = function(e) { if (BallData.timer_id == -1) { context.clearRect(0, 0, canvas.width, canvas.height); BallData.x = e.touches[0].pageX; BallData.y = e.touches[0].pageY; if (BallData.y > 400 - OFFSET) BallData.y = 400 - OFFSET; context.beginPath(); context.moveTo(INIT_X, INIT_Y); context.lineTo(BallData.x, BallData.y); context.stroke(); moveBall(BallData.x, BallData.y); } } var onBallTouchEnd = function(e) { if (BallData.timer_id == -1) { BallData.rotation = Math.atan2(BallData.y - INIT_Y, BallData.x - INIT_X) BallData.timer_count = COUNT; BallData.timer_id = setInterval(onTimer, INTERVAL); context.clearRect(0, 0, canvas.width, canvas.height); } } /* * Timer Handler **/ var onTimer = function() { // Clear ? var clear = piyos.length; for (var i=0; i<piyos.length; i++) { if (piyos[i].hp == 0) clear -= 1; } if (clear == 0) { context.font = "64pt 'Arial'"; context.fillText("Great!", 10, 100); clearInterval(BallData.timer_id); BallData.timer_id = -1; return; } if (BallData.timer_count > 0) { // Hit the Rival ? if (!getRivalHit()) { // Hit the Wall ? if (BallData.x < OFFSET || BallData.x > canvas.width - OFFSET) BallData.rotation = Math.PI - BallData.rotation; if (BallData.y < OFFSET || BallData.y > canvas.height - OFFSET) BallData.rotation = -BallData.rotation; } // Move Image BallData.x -= Math.cos(BallData.rotation) * LENGTH; BallData.y -= Math.sin(BallData.rotation) * LENGTH; moveBall(BallData.x, BallData.y); BallData.timer_count--; } else { clearInterval(BallData.timer_id); BallData.timer_id = -1; moveBall(INIT_X, INIT_Y); } } /* * Hit Function **/ var getRivalHit = function() { for (var i=0; i<piyos.length; i++) { if (BallData.x > piyos[i].x - OFFSET && BallData.x < piyos[i].x + OFFSET && BallData.y > piyos[i].y - OFFSET && BallData.y < piyos[i].y + OFFSET) { // HP == 0? piyos[i].hp -= 5; if (piyos[i].hp <= 0) { piyos[i].hp = 0; piyos[i].img.style.display = "none"; continue; } if (BallData.x > piyos[i].x - OFFSET && BallData.x < piyos[i].x + OFFSET) BallData.rotation = Math.PI - BallData.rotation; if (BallData.y > piyos[i].y - OFFSET && BallData.y < piyos[i].y + OFFSET) BallData.rotation = -BallData.rotation; return true; } } return false; } /* * Move Function **/ var moveBall = function(x, y) { ball.style.left = x - OFFSET + "px"; ball.style.top = y - OFFSET + "px"; } /* * Initialize **/ var init = function() { if (window.TouchEvent) { canvas = document.getElementById("ID_CANVAS"); canvas.width = window.innerWidth; canvas.height = 400; context = canvas.getContext("2d"); ball = document.getElementById("ID_BALL"); ball.addEventListener("touchstart",onBallTouchStart); ball.addEventListener("touchmove",onBallTouchMove); ball.addEventListener("touchend",onBallTouchEnd); moveBall(INIT_X, INIT_Y); // Back Button var back = document.getElementById("ID_BACK"); back.style.top = canvas.height + "px"; back.addEventListener("touchend", function() { document.location = "."; }); // Create Piyokichi piyos.push(new piyokichi(20, 200, 10)); piyos.push(new piyokichi(50, 30, 10)); piyos.push(new piyokichi(60, 100, 10)); piyos.push(new piyokichi(170, 80, 10)); piyos.push(new piyokichi(280, 120, 10)); for (var i=0; i<piyos.length; i++) { var img = document.createElement("img"); img.alt = "image"; img.src = "kaeru01/hiyo02_32x32.gif"; img.width = 32; img.height = 32; img.style.position = "absolute"; img.style.left = piyos[i].x - OFFSET + "px"; img.style.top = piyos[i].y - OFFSET + "px"; document.body.appendChild(img); piyos[i].img = img; } } } //--> </script> </head> <body onLoad="init()"> <canvas id="ID_CANVAS"></canvas> <img id="ID_BALL" src="kaeru01/kaeru02_32x32.gif" style="position:absolute" alt="ball"> <img id="ID_BACK" src="back.png" style="position:absolute" alt="back"> </body> </html>
敵の攻撃やターン数、障害物その他についてはまた今度。
長くなってきたので次回から[リンク先で Ctrl+U してね!]にするつもり。
他に今回やってみたこと。
canvas に文字列を表示するには fillText
[MS Pゴシック]なんてこのスマートフォン時代に指定しないで下さい。
context.font = "64pt 'Arial'"; context.fillText("Great!", 10, 100);
img タグを動的に作るには createElement
その img を非表示にするには img.style.display = “none”;
var img = document.createElement("img"); document.body.appendChild(img);
後は前回までのコードを少し応用しただけでココまで作れた。
しかしヒット判定は四方チェックしてから振り分けしか手段が無いのかな?
ネットに転がっているブロック崩しのコードを色々見るもイマイチ参考にならないし。
全部グローバル変数で読み辛い人多過ぎだし、構造体やクラスって凄く便利だよ。
今回は[フリー素材 動物アイコン]で適当に検索して以下をお借りしました。
1キロバイトの素材屋さん-無料素材の配布/可愛いアイコン・動く顔文字などのフリー素材集-
カエルとひよこがあって丁度よかった、32x32px に変更しただけ。
本格的に作る時があったらオリジナルキャラにしますが筆者は絵心が…