このページの本文へ

PROGRAMMING 古籏一浩のJavaScriptラボ ― 第92回

enchant.jsで懐かしのインベーダーゲームを作ろう

2012年08月29日 11時00分更新

古籏一浩

  • この記事をはてなブックマークに追加
本文印刷
js-labo

 いまから30年以上前、1978年に大ブームを巻き起こしたのがインベーダーゲームです。

スペースインベーダー
http://ja.wikipedia.org/wiki/スペースインベーダー
スペースインベーダー30周年アニメーションPV
http://www.youtube.com/watch?v=e8hqJpE7BL4&t=80s

 インベーダーゲームは、画面の上部から迫ってくる多数の敵キャラクター(インベーダー)を打ち倒すゲームです。敵を全滅させるとゲームクリアになり、逆に、敵が画面下まで到達するか、自機が撃たれるとゲームオーバーになります。インベーダーゲームの成功を受けて、ナムコの「ギャラクシアン」「ギャラガ」「ギャプラス」など、インベーダーゲームをベースにしたゲームが数多く登場しました。インベーダーゲームの成功により、今日のゲームの基礎が築かれたといってもよいかもしれません。

 今回のJavaScriptラボは、前回に引き続き「enchant.js」を使って、インベーダータイプのゲームを作ります。インベーダーと同じように最初にすべての敵が画面に表示され、敵をすべて撃てばクリアになります。クリアすると再度敵が出現し、自機が撃たれるまで延々とゲームを繰り返します。

ブロック崩しからインベーダーゲームへ

 インベーダーゲームでは、前回作成したブロック崩しのプログラムを流用できます。ブロックが敵に置き換わり、自機に向かって攻めてくるとインベーダーゲームになるわけです。

 前回のブロック崩しのキャラクター画像を差し替えたのが以下の図です。キャラクターを変えるだけでだいぶ雰囲気が変わりました。

ブロック崩しのキャラクターを差し替えるだけでインベーダータイプのゲームになる
ブロック崩しのキャラクターを差し替えるだけでインベーダータイプのゲームになる

 ブロック崩しのボールを自機のビームにすれば、インベーダーの骨格はできあがりです。接触判定もそのまま使えますし、自機のビームはブロック崩しとは異なり上に移動するだけなので処理も簡単です。なお、敵や自機、ビームなどの重なり順は_style.zIndexに直接Z座標値を設定しています。enchant.jsでは重なり順を変更する機能が用意されていないため、zIndexの値で直接設定する必要があります。

// ビームの設定
var beam = new Sprite(4, 16);
beam.flag = false;  // ビームが発射されているかどうかのフラグ
beam.x = fighter.x + 14;    // 自機の中央に設置
beam.y = fighter.y - 8;   // 自機より少し上のY座標に設置
beam.image = game.assets["images/beam.png"];
beam._style.zIndex = 2;
game.rootScene.addChild(beam);

 ブロック崩しは上から落下してくるボールを跳ね返すだけでしたが、シューティングゲームではボタンを押してビームを発射します。今回はAボタンをキーボードのZキーに割り当てました。任意のキーをAボタンに割り当てるには以下のようにします(90がZキーのキーコードになります)。

// ZキーをAボタンとして割り当てる
game.keybind(90, "a");

 ビームの移動は、ビームのY座標を減らしていき、画面から完全にはみ出たらフラグをfalseにします。このフラグはビームが発射されているかどうかを示すもので、trueならビームが発射されているのでビームを移動させます。

// ------------ ■ビームを移動させる -----------------
function moveBeam(){
 if (beam.flag){
  beam.y = beam.y - 8// 8を減算するとビームは上に移動する
  // 画面外かどうか調べる
  if (beam.y < -32){ beam.flag = false; }
 }
}

 インベーダーでは、ビームは同時に1つしか発射できませんので、すでにビームが発射されている場合はAボタンを押しても何も処理しません。Aボタンが押されたら自機のX,Y座標をもとにビームの座標を設定します。

// ------------ ■ビームを発射する -----------------
function startBeam(){
 if (!beam.flag){
  // Aボタンが押されたらビームを発射
  if (game.input.a){
   beam.flag = true; // trueにしてビームが発射されている事を示すようにする
   beam.x = fighter.x + 14// 自機の中央から出す
   beam.y = fighter.y - 11// 自機より少し上のY座標から出す
  }
 }
}

 実際のプログラムがサンプル1です。カーソルキーで自機が左右に移動し、Zキー(Aボタン)でビームが発射されます。ビームの発射と移動以外はブロック崩しのプログラムとほぼ同じです。この段階では敵は画面上に止まったままで動きませんので、次ページでは敵を左右に動かしてみましょう。

■サンプル1:main.js(実際のゲームの実行ページ

enchant(); // ライブラリの初期化
window.onload = function(){
 var game = new Game(360, 480); // 360×480画面(Canvas)を作成
 game.fps = 30// フレームレートの設定。30fpsに設定
 // 画像データをあらかじめ読み込ませる
 game.preload("images/droid.png", "images/beam.png", "images/apple.png" );
 game.rootScene.backgroundColor = "black"; // ゲームの背景色を黒色に設定
 game.score = 0// スコアを入れる変数を用意する
 // スコアを表示するラベルを作成
 var scoreLabel = new Label("SCORE : 0");
 scoreLabel.font = "16px Tahoma";
 scoreLabel.color = "white";
 scoreLabel.x = 10// X座標
 scoreLabel.y = 5; // Y座標
 game.rootScene.addChild(scoreLabel);
 // 敵の総数を入れるカウンタ変数
 var count = 0;
 // 敵を格納する配列
 var enemy = new Array();
 // 消した敵の総数を入れる変数
 var total = 0;
 // データの読み込みが完了したら処理
 game.onload = function(){
 // 自機の設定
 var fighter = new Sprite(32, 32);
 fighter.image = game.assets["images/apple.png"];
 fighter.x = game.width/2; // X座標
 fighter.y = game.height - 40; // Y座標
 fighter._style.zIndex = 1;
 game.rootScene.addChild(fighter);
 // ビームの設定
 var beam = new Sprite(4, 16);
 beam.flag = false// ビームが発射されているかどうかのフラグ
 beam.x = fighter.x + 14// 自機の中央に設置
 beam.y = fighter.y - 8// 自機より少し上のY座標に設置
 beam.image = game.assets["images/beam.png"];
 beam._style.zIndex = 2;
 game.rootScene.addChild(beam);
 // Zキーをaボタンとして割り当てる
 game.keybind(90, "a");
 // 敵を描く
 drawEnemy();
 // フレームイベントが発生したら処理
 game.rootScene.addEventListener(Event.ENTER_FRAME, function(){
  startBeam(); // ビームの発射を確認
  moveBeam(); // ビームを移動させる
  moveFighter(); // 自機を移動させる(キーボード対応)
  hitCheck();  // ビームと敵の接触判定
  // =============== 各種処理 ==================
  // ------------ ■ビームを移動させる -----------------
  function moveBeam(){
  if (beam.flag){
   beam.y = beam.y - 8// 8を減算するとビームは上に移動する
   // 画面外かどうか調べる
   if (beam.y < -32){ beam.flag = false; }
  }
  }
  // ------------ ■ビームを発射する -----------------
  function startBeam(){
  if (!beam.flag){
   // Aボタンが押されたらビームを発射
   if (game.input.a){
   beam.flag = true; // trueにしてビームが発射されている事を示すようにする
   beam.x = fighter.x + 14// 自機の中央から出す
   beam.y = fighter.y - 11// 自機より少し上のY座標から出す
   }
  }
  }
  // ------------ ■自機を移動させる -----------------
  function moveFighter(){
  // キーボード操作の場合
  if (game.input.left){
   fighter.x = fighter.x - 4// パドルを左に移動
   if (fighter.x < 0){ fighter.x = 0; } // 左端かどうか調べる
  }
  if (game.input.right){
   fighter.x = fighter.x + 4// パドルを右に移動
   if (fighter.x > (game.width-fighter.width)){ fighter.x = game.width - fighter.width; } // 右端かどうか調べる
  }
  // ビームが発射されていない場合は自機と一緒に移動
  if (!beam.flag){
   beam.x = fighter.x + 14// 自機の中央に設置
   beam.y = fighter.y - 11// 自機より少し上のY座標に設置
  }
  }
  // ------------ ■敵とビームの接触判定を行う -----------------
  function hitCheck(){
  for(var i=0; i<count; i++){
   if (beam.intersect(enemy[i])){
   beam.flag = false// 接触した場合はビームを消す
   enemy[i].y = -9999// 見えない場所に移動
   game.score = game.score + 1// スコアを加算(1点)
   total = total - 1// 総敵数から1を引く
   if (total < 1){  // 全部倒したか調べる
    setTimeout("drawEnemy()", 2000); // 2秒後に敵を再描画
   }
   }
  }
  scoreLabel.text = "SCORE : "+game.score;
  }
 });
 }
 // ゲーム処理開始
 game.start();
 // ------------ 敵を描く -----------------
 function drawEnemy(){
 count = 0// 敵の総数を示すカウンタを0にする
 // ビームの設定を縦横の数だけ繰り返し生成
 for(var y=0; y<5; y++){
  for(var x=0; x<7; x++){
  enemy[count] = new Sprite(32, 32);
  enemy[count].image = game.assets["images/droid.png"];
  enemy[count].x = x * (32+10); // X座標
  enemy[count].y = y * 32 + 30; // Y座標
  enemy[count]._style.zIndex = 2// Z座標
  game.rootScene.addChild(enemy[count]);
  count = count + 1// 敵の総数を示すカウンタを増やす
  }
 }
 total = count; // 消す敵の総数を変数に入れる
 }
 window.drawEnemy = drawEnemy;
}
Web Professionalトップページバナー

この記事の編集者は以下の記事をオススメしています

ASCII.jp会員サービス 週刊Web Professional登録

HTMLリファレンス誘導バナー

CSSリファレンスサイト誘導バナー

Webディレクター江口明日香が行く

ランキング