敵弾の処理と接触判定
一方的に攻撃したのではゲームにならないので、敵にも弾を発射させましょう。敵の弾は適当に発射するのではなく、自機を狙ってくるようにします。敵弾も専用のオブジェクトを作成し、座標値などをプロパティとして設定します。敵弾はくるくると回転しながら自機に向かってくるようにしたいので、回転角度を示すtama.degreeプロパティを用意しておきます。
var tama = new Object(); tama.ele = document.getElementById("tama"); // 親玉が発射する弾 tama.x = 0; tama.y = 0; tama.dy = 0; tama.degree = 0; tama.flag = false;
敵弾の発射処理は以下のようになります。自機と敵のY座標を元にして、どの程度Y座標を変化させるかを求めます(tama.dy)。敵弾は口の部分から発射するのでtama.yには口付近の座標値を設定します。
tama.dy = (fighter.y - (tempY+65)) / boss.x; tama.y = tempY+65; tama.x = boss.x;
これですべてのキャラクターが動くようになりました。最後に、接触判定を付け加えます。ビームと敵の接触は以下のように座標値を元にして判断します。敵弾と自機の接触判定も同様に座標値から求めます。
if (( (beam.x+20) > boss.x) && (beam.x < (boss.x+62)) && ((beam.y+2) > bossy) && (beam.y < (bossy + 86))){
敵は一発ではやられませんが、ビームが当たるたびに色が変わったほうが分かりやすいでしょう。そこで、敵の色をboss.colorに入れておき、ビームが当たるたびに色を変更します。
boss.color = ["#F83b0F", "#E24000", "#AE4303", "#AE6503" ]; // やられた時の色
塗りつぶしの色はfill属性にカラーコードを設定するだけで変更できます。以下のようにsetAttribute()の第2引数にboss.color[]を指定します。工夫すると色を交互に入れ替えて点滅するようにもできます。
boss.ele.setAttribute("fill", boss.color[boss.power]);
自機が敵弾に接触した場合はアラートダイアログを表示し、ゲームオーバーとします。その後、ページをリロードして最初の状態に戻します。
if (((tama.x+20) > fighter.x) && (tama.x < (fighter.x+20)) && ((tama.y+9) > fighter.y) && (tama.y < (fighter.y + 15))){ alert("ゲームオーバー"); location.reload(); }
以上で、SVGを使ったシューティングゲームの完成です(サンプル7)。
せっかくSVGを利用したゲームなので、画面にあわせて拡大・縮小できるようにしましょう。<svg>タグを以下のように変更すると、どんな画面サイズでもきれいにキャラクターが表示されます。
<svg width="100%" height="100%" viewBox="0 0 400 300" style="border:1px solid black">
■サンプル7[HTML]
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <title>JavaScriptでSVGゲーム</title> </head> <body> <h1>JavaScriptでSVGゲーム</h1> <form> <input type="button" id="gameStart" value="ゲームスタート"> </form> <svg width="400" height="300" style="border:1px solid black"> <g id="fighter"> <polygon display="inline" fill="#231815" points="39.473,14.667 37.806,12.667 29.034,12.228 25.473,8.667 13.227,8.667 10.473,2.333 5.473,0 5.473,8.667 0,8.667 2.139,12.333 4.473,12.333 4.473,14.667 "/> </g> <path id="boss" display="inline" fill="#AE6503" d="M0,0v53h11.333v18.333H0v13h60V0H0z M11.5,21.333c-1.563,0-2.833-2.239-2.833-5 s1.27-5,2.833-5c1.565,0,2.833,2.239,2.833,5S13.065,21.333,11.5,21.333z"/> <g id="tama"> <path display="inline" fill="#00CD00" d="M20.307,3.778c0,2.209-2.807,4-7.5,4c-4.694,0-12.167-1.791-12.167-4s7.472-4,12.167-4 C17.502-0.222,20.307,1.569,20.307,3.778z"/> </g> <g id="beam"> <rect x="0" fill="#FF0000" width="20" height="3"/> </g> </svg> <script type="text/javascript"> // 親玉のデータ設定 var boss = new Object(); boss.ele = document.getElementById("boss"); // 親玉 boss.x = 300; boss.y = 100; boss.degree = 0; boss.power = 3; // 親玉の体力 boss.color = ["#F83b0F", "#E24000", "#AE4303", "#AE6503" ]; // やられた時の色 // 自機のデータ設定 var fighter = new Object(); fighter.ele = document.getElementById("fighter"); // 自機 fighter.x = 0; fighter.y = 100; fighter.ele.setAttribute("transform", "translate("+fighter.x+","+fighter.y+")"); // ビームのデータ設定 var beam = new Object(); beam.ele = document.getElementById("beam"); // 自機が発射するビーム beam.x = 0; beam.y = 2; beam.flag = false; beam.limitX = 400; // ゲーム画面右端の座標 // 敵弾のデータ設定 var tama = new Object(); tama.ele = document.getElementById("tama"); // 親玉が発射する弾 tama.x = 0; tama.y = 0; tama.dy = 0; tama.degree = 0; tama.flag = false; document.getElementById("gameStart").addEventListener("click", function(){ // 親玉の移動処理 setInterval(function(){ boss.degree = boss.degree + 5; var tempY = boss.y + Math.sin(boss.degree* Math.PI /180) * 100; boss.ele.setAttribute("transform", "translate("+boss.x+","+tempY+")"); // 弾の移動&発射処理 if (tama.flag){ tama.x = tama.x - 4; tama.y = tama.y + tama.dy * 4; if (tama.x < 0){ tama.flag = false; } check2(); }else{ tama.dy = (fighter.y - (tempY+65)) / boss.x; tama.y = tempY+65; tama.x = boss.x; tama.flag = true; } tama.degree = tama.degree + 10; tama.ele.setAttribute("transform", "translate("+tama.x+","+tama.y+") rotate("+tama.degree+" 10 10)"); // ビームの移動処理 if (beam.flag){ beam.x = beam.x + 10; if (beam.x > beam.limitX) { beam.flag = false; } check1(tempY); beam.ele.setAttribute("transform", "translate("+beam.x+","+beam.y+")"); } }, 50); }, true); // 自機の移動 function move_fighter(kc){ if ((fighter.y > 2) && (kc == 65)){ // A。つまり上に移動 fighter.y = fighter.y - 2; } if ((fighter.y < 280) && (kc == 90)){ // Z。つまり下に移動 fighter.y = fighter.y + 2; } fighter.ele.setAttribute("transform", "translate("+fighter.x+","+fighter.y+")"); } // 親玉とビームの接触判定 function check1(bossy){ if (( (beam.x+20) > boss.x) && (beam.x < (boss.x+62)) && ((beam.y+2) > bossy) && (beam.y < (bossy + 86))){ if (boss.power == 0){ alert("ミッション成功!!"); location.reload(); // ページを再読み込み return; } boss.power = boss.power - 1; boss.ele.setAttribute("fill", boss.color[boss.power]); beam.flag = false; beam.x = -100; beam.ele.setAttribute("transform", "translate("+beam.x+","+beam.y+")"); } } // 敵の弾と自機の接触判定 function check2(){ if (((tama.x+20) > fighter.x) && (tama.x < (fighter.x+20)) && ((tama.y+9) > fighter.y) && (tama.y < (fighter.y + 15))){ alert("ゲームオーバー"); location.reload(); } } // キー入力イベントの初期化 window.addEventListener("keydown", function(evt){ move_fighter(evt.keyCode); }, true); // マウスイベントの初期化 window.addEventListener("click", function(evt){ if (beam.flag) { return; } // すでに発射済みなら処理しない beam.flag = true; beam.x = fighter.x + 20; beam.y = fighter.y + 10; }, true); </script> </body> </html>
SVGの描画速度が向上すると、もっと多くのキャラクターを表示して処理しても十分ゲームになるでしょう。SVGは古くからありますが、これまではあまり活用されてきませんでした。IE9でサポートされたことで、今後はもっと多く利用されていくことでしょう。