one's way blog

ワクワクを生み出せるWebエンジニアを目指して。

【13:アクションゲーム】enchant.jsを使ったアクションゲーム

f:id:seintoseiya:20150528232002p:plain
プロジェクトNo.13:アクションゲーム
前回に引き続き、enchant.jsというHTML5JavaScriptベースのゲームエンジンで簡単なゲームを作ってみました。

ゲームの仕様は、
画面に表示されている敵を全部倒せばクリアです。
スマホからは画面の十字キーをタップで方向転換。右下部分をタップすると攻撃です。
PCからは画面をクリックするか、キーボードの矢印キーとzボタンがそれぞれに対応しています。

なんかすごくシンプルなゲームですけど、前回とは違う機能をたくさん使ったので、ご紹介させてもらいます。

マップの作成

背景を画像に設定することもできるけど、RPGのような1マス1マスのマップデータを配置する方法もあります。
enchant.jsが用意しているMapクラスを使っていきます。

オブジェクトの作成と画像データの読み込み

前回、Spriteの説明をしましたがそれと同じで画像を読み込んで、画像の中のどの部分を使うかを指定します。
マップは2次元配列で場所とデータを指定し、レイヤのように重ねることも可能です。(-1はnullデータとなります)

JavaScript
// 背景
var baseMap = new Map(16, 16);
baseMap.image = game.assets['./img/map1.png'];

// マップを2レイヤ重ねる
baseMap.loadData([
  [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],            
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3,  3, -1],
  [ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1]
],[
  [  7, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],            
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [  7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,  7],
  [ 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23]
]);

マップに障害物を配置する

マップの描画自体の指定は上記で行い、障害物判定は別にMAPオブジェクトの持つcollisionDataに指定します。
1が障害物ありという意味になります。

JavaScript
// 障害物の判定用 1:障害物有り
baseMap.collisionData = [
  [  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],            
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  1],
  [  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1]
];

キャラクターの表示

これは前回と一緒でナイトと敵キャラを表示します。
敵キャラは一定間隔で障害物以外の場所を上下左右ランダムで動くようにしています。

イベント

前回は毎フレームイベントと画面タッチイベントを紹介しましたが、
今回はキーボード操作のイベントを紹介します。

  • aボタン押下時イベント及びaボタン離した時イベント
JavaScript
game.keybind('Z'.charCodeAt(0), 'a');   // z を a ボタンとして割り当てる
// aボタン押下時イベント
game.addEventListener("abuttondown", function(e) {
    buttonAction();
});
// aボタン離した時イベント
game.addEventListener("abuttonup", function(e) {
    aPush = 0;
});

enchant.jsではaボタンとbボタンというイベントハンドラが用意されていて、
それらをどのキーボードに対応させるのかを上記のコードで指定できます。

十字キーパッド

今回はプラグインも使ってみました。
まず、UI系のプラグインであるui.enchant.jsをHTML上で読み込みます。

HTML
<script src="./js/lib/ui.enchant.js"></script>

JavaScriptではオブジェクトを生成し、位置を指定したら、
game.input.leftなどでイベントハンドラを取得できるので、それぞれに処理を記述していくだけです。

JavaScript
// 十字キー
var pad = new Pad();
pad.x = 0;
pad.y = 304;

// 各方向ボタンの処理
if (game.input.left) {
    this.direction = 1; // 移動方向の設定 0:down, 1:left, 2:right, 3:up
    this.vx = -4; // 移動距離の設定
} else if (game.input.right) {
    this.direction = 2;
    this.vx = 4;
} else if (game.input.up) {
    this.direction = 3;
    this.vy = -4;
} else if (game.input.down) {
    this.direction = 0;
    this.vy = 4;
}

画面のスクロール設定

アクションゲームだと画面の端まで行く前に画面全体がスクロールします。
これはどうやって実現するかというと、
主人公キャラクターが中心からある一定の距離を移動したら、画面全体も移動させるということを行っています。

JavaScript
// 画面のスクロール設定
var stage = new Group();
stage.addChild(baseMap);
stage.addChild(knight);
stage.addChild(knight_hit);
stage.addChild(knight_hitU);
stage.addChild(knight_hitD);
stage.addChild(knight_hitR);
stage.addChild(knight_hitL);
stage.addChild(enemies);
stage.addChild(enemies_hit);

// ステージに「毎フレーム実行イベント」を追加します。
stage.addEventListener('enterframe', function(e) {
    if (this.x > 200 - knight.x && knight.x <= 250) { 
        this.x = 200 - knight.x;
    }
    if (this.x < 140 - knight.x && knight.x != 0) { 
        this.x = 140 - knight.x;
    }
    if (this.y > 200 - knight.y && knight.y <= 300) { 
        this.y = 200 - knight.y;
    }
    if (this.y < 160 - knight.y && knight.y != 0) { 
        this.y = 160 - knight.y;
    }
});

Groupというクラスを使って全てのオブジェクトをまとめてから、そのGroupに対して移動処理を行っています。

参考サイト

   百式さんの記事でサンプルにあるRPGプログラムをわかりやすく解説してくれています。
   マップの概念を理解するのに良いかと思います。

全ソースはこちら

github.com