Phina.JSでゲームを作ってみた

#Phina.JSで簡単なゲームを作ってみました
JavaScriptベースのゲームエンジンであるPhina.JSを使用した簡単なゲームを作ってみましたので、その作成手順をステップバイステップで説明します。　なお、Windows10の環境において Microsoft Edge (Ver. 11.0.17763.379), Firefox (Ver. 65.0.2/64 bit) 及び Google Chrome (Ver. 73.0.3683.86/64 bit) で動作を確認しました。　また、AndroidやiOSでもバージョンによっては動作可能です。
また、ここで使用しているグラフィックの一部に「[​どらぴか様](https://dorapika.wixsite.com/pikasgame)」ならびに「[ぴぽや様](http://blog.pipoya.net/)」作成の素材をサウンドエフェクトには「[小森平様](https://taira-komori.jpn.org/)」作成の素材を使用させていただきました。　フリー素材を提供して下さった各位に感謝いたします。
##ファイル構成
ここで説明する内容に関するファイルは全て「[GitHub PhinaJS_Sample_games](https://github.com/To-Fujita/PhinaJS_Sample_Games)」からダウンロード可能です。　また、そのファイル構成は次の通りです。
・ images: ゲームで使用するグラフィックを収納するフォルダーです。
・ js: 各ステップ毎のメイン・プログラムであるJavaScriptを収納するフォルダーです。
　- maze_02.js: Step-7で使用する迷路作成プログラムです。
　- phina.js, phina.min.js: Phina.JSゲームエンジン本体です。（Phina.JSからダウンロードしたもの）
　- phina_sample_01.js ～ phina_sample_09_with_sound_effect.js: 各ステップ毎のゲーム・プログラムです。
　- stages_01.js, stages_02.js: Step-5以降で使用するステージ用データで、stage_01.jsは敵がいないバージョン、stage_02.jsは敵がいるバージョンです。
・ sound: ゲームで使用するサウンド・エフェクトを収納するフォルダーです。
・ index_Phina_01.html ～ index_Phina_09_with_sound_effect.html: 各ステップ毎のゲーム開始用HTMLファイルです。

##ゲーム作成の説明
以下にゲーム作成の手順をステップバイステップで説明します。
###Step-1：　静止画グラフィックスを表示させる
始めにHTMLファイルを示します。　Phina.JSゲームエンジンの読込及びゲーム本体のJavaScriptファイルの読込等を行い全てのステップでほぼ同一ですが、各ステップ毎にタイトルと「src='./js/phina_sample_01.js'」の部分を変更しています。
[Step-1のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_01.html)

```html:index_Phina_01.html
<!doctype html>

<html>
  <head>
    <meta charset='utf-8' />
    <meta name="viewport" content="width=device-width, user-scalable=no" />
    <meta name="apple-mobile-web-app-capable" content="yes" />

    <title>Sample for phina.js #001 by T. Fujita on 2019/9/12</title>
    <!-- phina.js を読み込む -->
    <script src='./js/phina.js'></script>

    <!-- メイン処理 -->
    <script src='./js/phina_sample_01.js'></script>
  </head>
  <body>

  </body>
</html>
```

次にメインプログラムである「phina_sample_01.js」を示します。　背景画像とグラフックス（player）を読み込み表示します。

```javascript:phina_sample_01.js
/*
 * phina.js sample #001 by T. Fujita on 2019/9/12
 */

phina.globalize();

var SCREEN_X = 800;
var SCREEN_Y = 600;
var scale = 1;
var P_size = 48 * scale;

var ASSETS = {
  image: {
    'Player': './images/ETNR_TOMITA_01.png',
    'bg': './images/bg.jpg',
  },
};

phina.define('MainScene', {
  superClass: 'DisplayScene',
  
  init: function(option) {
    this.superInit(option);
    this.bg = Sprite("bg").addChildTo(this);
    this.bg.origin.set(0, 0);
    this.bg.width = SCREEN_X;
    this.bg.height = SCREEN_Y;
//    this.backgroundColor = '#ffaaaa';
    var player = Sprite('Player', 48, 48)
	.addChildTo(this)
	.setPosition(this.gridX.center(), this.gridY.center())
	.setScale(scale);
    player.frameIndex = 0;
  },
});

phina.main(function() {
  var app = GameApp({
    startLabel: 'main',
    width: SCREEN_X,
    height: SCREEN_Y,
    assets: ASSETS,
  });
  
  app.run();
});
```

###Step-2：　静止画グラフィックスを移動させてみる
前述の「phina_sample_01.js」にマウスをクリックした位置あるいはキー入力の検知を追加し、それに合わせてplayerを移動させるものが「phina_sample_02.js」です。　追加・修正個所を以下に示します。
[Step-2のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_02.html)

```javascript:phina_sample_02.js（追加・修正部分）
  update: function(app) {
    var keyboard = app.keyboard;
    var touch_p = app.pointer;
    
    // 左右移動
    if (keyboard.getKey('left') || (touch_p.getPointing() && (Math.floor(this.player.x / Step) * Step) > (Math.floor(touch_p.x / Step) * Step))) {
      this.player.x -= Step;
	if(this.player.x < (P_size / 2)) {
		this.player.x = P_size / 2;
	}
    }
    if (keyboard.getKey('right') || (touch_p.getPointing() && (Math.floor(this.player.x / Step) * Step) < (Math.floor(touch_p.x / Step) * Step))) {
      this.player.x += Step;
	if(this.player.x > (SCREEN_X - P_size / 2)) {
		this.player.x = SCREEN_X - P_size / 2;
	}
    }
    // 上下移動
    if (keyboard.getKey('up') || (touch_p.getPointing() && (Math.floor(this.player.y / Step) * Step) > (Math.floor(touch_p.y / Step) * Step))) {
      this.player.y -= Step;
	if(this.player.y < (P_size / 2)) {
		this.player.y = P_size / 2;
	}
    }
    if (keyboard.getKey('down') || (touch_p.getPointing() && (Math.floor(this.player.y / Step) * Step) < (Math.floor(touch_p.y / Step) * Step))) {
      this.player.y += Step;
	if(this.player.y > (SCREEN_Y - P_size / 2)) {
		this.player.y = SCREEN_Y - P_size / 2;
	}
    }
  }
```

###Step-3：　アニメーション・グラフィックスを表示させる
「phina_sample_01.js」を元にplayerをアニメーションさせる「phina_sample_03.js」を作成します。　変更部分を次に示します。　Phina.JSでは、RPGツクール等の歩行グラフィックスが使用可能です。
[Step-3のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_03.html)

```javascript:phina_sample_03.js（追加分）
var ASSETS = {
  image: {
    'Player': './images/ETNR_TOMITA_01.png',
    'bg': './images/bg.jpg',
  },
  spritesheet: {
    'Player_ss':
    {
      'frame': {
	'width': 48,
	'height': 48,
	'cols': 12,
	'rows': 1,
      },
      'animations': {
	'down': {
	  'frames': [0, 1, 2],
	  'next': 'down',
	  'frequency': F_rate,
	},
      }
    },
  }
};

phina.define('MainScene', {
  superClass: 'DisplayScene',
  
  init: function(option) {
    this.superInit(option);
    this.bg = Sprite("bg").addChildTo(this);
    this.bg.origin.set(0, 0);
    this.bg.width = SCREEN_X;
    this.bg.height = SCREEN_Y;
//    this.backgroundColor = '#ffaaaa';
    var player = Sprite('Player', P_size, P_size).addChildTo(this)
	.setPosition(this.gridX.center(), this.gridY.center())
	.setScale(scale);
    var anim_p = FrameAnimation('Player_ss').attachTo(player);
    anim_p.gotoAndPlay('down');
  },
});
```

###Step-4：　アニメーション・グラフィックスを移動させてみる
前述の「phina_sample_02.js」と「phina_sample_03.js」を組み合わせて前後（上下）左右の歩行アニメーションを追加したものが「phina_sample_04.js」です。　追加分の一部として左右歩行部分を以下に示します。
[Step-4のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_04.html)

```javascript:phina_sample_04.js（追加・修正の一部分）
  update: function(app) {
    var keyboard = app.keyboard;
    var touch_p = app.pointer;
    
// 左右移動
    if (keyboard.getKey('left') || (touch_p.getPointing() && (Math.floor(this.player.x / Step) * Step) > (Math.floor(touch_p.x / Step) * Step))) {
      this.player.x -= Step;
      if(app.frame % F_rate === 0) {
	this.player.frameIndex = (this.player.frameIndex === 3) ? 5:3;
      }
    }
    else if (keyboard.getKey('right') || (touch_p.getPointing() && (Math.floor(this.player.x / Step) * Step) < (Math.floor(touch_p.x / Step) * Step))) {
      this.player.x += Step;
      if(app.frame % F_rate === 0) {
	this.player.frameIndex = (this.player.frameIndex === 6) ? 8:6;
      }
    }
```

###Step-5：　壁等を表示させ、移動を制限する
「phina_sample_04.js」に「stages_01.js」で設定する各ステージを読み込みタイルデータを表示させたものが「phina_sample_05.js」です。　ここでは、壁を通り抜け出来ない設定とゴール、再度実行（Aタイル）のみ対応するようにしています。
[Step-5のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_05.html)

```javascript:phina_sample_05.js（タイルデータ表示部分）
    if(counter >= max_rooms) {
	counter = 0;
	this.exit();
    }
    ROOM = room[counter];
    counter = counter + 1;

    this.player = Sprite('Player', 48, 48).addChildTo(this).setScale(scale);
    this.player.frameIndex = 0;
    this.wall_0Group = DisplayElement().addChildTo(this);
    this.wall_1Group = DisplayElement().addChildTo(this);

    for (i=0; i<ROOM.length; i++) {
	temp[i] = [];
	for (j=0; j<ROOM[i].length; j++) {
	    temp[i][j] = ROOM[i].substr(j,1);
	    if(ROOM[i].substr(j,1) == "P") { 
		this.player.setPosition( j * P_size + P_size/2, i * P_size + P_size/2); 
	    }
	    else if(ROOM[i].substr(j,1) == "A") { 
		this.again = Again().addChildTo(this).setScale(scale);
		this.again.setPosition( j * P_size + P_size/2, i * P_size + P_size/2); 
	    }
	    else if(ROOM[i].substr(j,1) == "G") { 
		var goal = Goal().addChildTo(this).setScale(scale);
		goal.setPosition( j * P_size + P_size/2, i * P_size + P_size/2); 
	    }
	    else if(ROOM[i].substr(j,1) == "B") { 
		BLOCK[BL_counter] = Block().addChildTo(this).setScale(scale);
		BLOCK[BL_counter].setPosition( j * P_size + P_size/2, i * P_size + P_size/2); 
		BL_counter = BL_counter + 1;
	    }
	    else if(ROOM[i].substr(j,1) == "w") { 
		var wall_0 = Wall_0().addChildTo(this.wall_0Group).setScale(scale); 
		wall_0.setPosition( j * P_size + P_size/2, i * P_size + P_size/2); 
	    }
	    else if(ROOM[i].substr(j,1) == "W") { 
		var wall_1 = Wall_1().addChildTo(this.wall_1Group).setScale(scale); 
		wall_1.setPosition( j * P_size + P_size/2, i * P_size + P_size/2); 
	    }
	}
    }
```

```javascript:phina_sample_05.js（左側の壁対応部分）
	if (flag == "left") {
	    player.x -= Step;
	    if(app.frame % F_rate === 0) {
		player.frameIndex = (player.frameIndex === 3) ? 5:3;
	    }
	    if((pos[p_x - 1][p_y] == "W") || pos[p_x - 1][p_y] == "w") {
		if(player.x < (p_x) * P_size + P_size/2 + Step) {
		    player.x = (p_x) * P_size + P_size/2;
		}
	    } else if(pos[p_x - 1][p_y] != "G") {
		pos[p_x][p_y] = "F";
		pos[p_x - 1][p_y] = "P";
	    }
	} else if (flag == "right") {
```

```javascript:phina_sample_05.js（ゴール対応部分と再度実行部分）
    if(pos[p_x][p_y] == "G") {
	END_falg = "Goal !";
	this.nextLabel ="title";
	this.exit();
    }
    again.setInteractive(true);
    again.onpointstart = function () {
	END_flag = "Again";
    }
    if(END_flag == "Again") {
	this.nextLabel ="title";
	this.exit();
    }
```

各ステージを構成する「stage_01.js」の一部を以下に示します。　メモ帳等のエディターで修正・追加してみてください。

```javascript:stage_01.js
// All the rooms are here. 
// by T. Fujita
// 
// A: Again
// B: Block(Movable)
// F: Floor
// G: Goal
// P: Player
// W: Wall

var room = [];
    room[0] = [ "WwwwwGwW",
		"WFFFFFFW",
		"WFFFFFFW",
		"WFPFFFFW",
		"WFFFFFFW",
		"WwwwwwwA"]

    room[1] = [ "WwwwwGwW",
		"WFFFFBFW",
		"WFFFFFFW",
		"WFPFFFFW",
		"WFFFFFFW",
		"WwwwwwwA"]

    room[2] = [ "WwwwwwwwwwwwwGwW",
		"WFFFFFFFFFFFFFFW",
		"WBBBBBBBBBBBBBBW",
		"WFFFFFFFFFFFFFFW",
		"WFFFFFFFFFFFFFFW",
		"WFFFFFFFFFFFFFFW",
		"WFFFFFFFFFFFFFFW",
		"WFFFFFFFFFFFFFFW",
		"WFFPFFFFFFFFFFFW",
		"WFFFFFFFFFFFFFFW",
		"WFFFFFFFFFFFFFFW",
		"WwwwwwwwwwwwwwwA"]
```

###Step-6：　ボックスを押して移動可能にさせる
「phina_sample_06.js」でplayerがBoxを押して移動できるようにしました。　これでゲームの基本部分は完成です。
[Step-6のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_06.html)

```javascript:phina_sample_06.js（左側の壁対応にBOX移動を追加した部分）
	if (flag == "left") {
	    player.x -= Step;
	    if(app.frame % F_rate === 0) {
		player.frameIndex = (player.frameIndex === 3) ? 5:3;
	    }
	    if((pos[px - 1][py] == "W") || pos[px - 1][py] == "w") {
		if(player.x < px * P_size + Step) {
		    player.x = px * P_size;
		}
	    } else if(pos[px - 1][py] == "B") {
		for(i=0; i<BL_counter; i++) {
		    if((Math.round(BLOCK[i].x / P_size) == (px - 1)) && (Math.round(BLOCK[i].y / P_size) == py)) {
			BL_temp = i;
			BL_x = BLOCK[i].x;
			BL_y = BLOCK[i].y;
		    }
		}
		if(pos[px - 2][py] == "F") {
		    if(player.x < BL_x + P_size + Step) {
			BLOCK[BL_temp].x = (px - 2) * P_size;
			if(pos[px][py] == "P") {pos[px][py] = "F";}
			if(pos[px - 1][py] == "B") {pos[px - 1][py] = "P";}
			if(pos[px - 2][py] == "F") {pos[px - 2][py] = "B";}
		    }
		} else {
		    if(player.x <= BL_x + P_size - Step) {
			player.x = px * P_size;
			if(pos[px][py] == "F") {pos[px][py] = "P";}
		    }
		}
	    } else if(pos[px - 1][py] != "G") {
		if(pos[px][py] == "F") {pos[px][py] = "P";}
	    }
	    if(pos[px + 1][py - 1] == "P") {pos[px + 1][py - 1] = "F";}
	    if(pos[px + 1][py] == "P") {pos[px + 1][py] ="F";}
	    if(pos[px + 1][py + 1] == "P") {pos[px + 1][py + 1] = "F";}
	} else if (flag == "right") {
```

さらにゴール時の効果音を追加しました。　「phina_sample_06_with_sound_effect.js」でゴール時に効果音を作動させます。
[Step-6効果音付きのデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_06_with_sound_effect.html)

```javascript:phina_sample_06_with_sound_effect.js（ゴール時の効果音を追加したASSETS部分）
var ASSETS = {
  image: {
    'bg': './images/bg.jpg',
    'Player': './images/ETNR_TOMITA_01.png',
    'again': './images/A_48.png',
    'block': './images/Box_06.png',
    'goal': './images/Goal_00.png',
    'wall_0': './images/Block_05.png',
    'wall_1': './images/Block_04.png',
  },
  sound: {
    'GOAL': './sound/crrect_answer3.mp3',
  },
};
```

```javascript:phina_sample_06_with_sound_effect.js（ゴール時の効果音を鳴らす部分を追加した箇所）
    if(pos[p_x][p_y] == "G") {
	END_falg = "Goal !";
	SoundManager.play('GOAL');
	this.nextLabel ="title";
	this.exit();
    }
```

###Step-7：　迷路を作成しアニメーション・グラフィックスを移動させてみる
「phina_sample_06_with_sound_effect.js」を若干変更し、「maze_02.js」で作成した迷路を表示させたものが「phina_sample_07_with_sound_effect.js」です。　各グラフィックスのサイズは1/2に設定しています。　迷路は、「[Algoful : Algorithm for making a maze](http://algoful.com/Archive/Algorithm/MazeDig)」のアルゴリズムを使用させていただきました。
[Step-7のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_07_with_sound_effect.html)

```javascript:maze_02.js
// This program is based on "http://algoful.com/Archive/Algorithm/MazeDig" by Algoful.
// by T. Fujita

var maze = [];
var startCells = [];

var Maze = function(X, Y) 
{
    var w = X;									// 幅（奇数）
    var h = Y;									// 高さ（奇数）
    if(w < 11) {w = 11;}
    if(h < 11) {h = 11;}
    if(w % 2 == 0) {w = w - 1;}
    if(h % 2 == 0) {h = h - 1;}

    var x;
    var y;
    var results = [];

    var currentCells = [];

    for (x = 0; x < w; x++) {
	maze[x] = new Array();
	for (y = 0; y < h; y++) {
            if (x == 0 || y == 0 || x == w - 1 || y == h - 1) {
                maze[x][y] = "F";						// 外周を通路に設定
            } else {
		maze[x][y] = "W";						// 残りを壁に設定
	    }
	}
    }

    startCells[0] = [];
    startCells[0][0] = (Math.floor(Math.random() * (w / 2 - 2))) * 2 + 1;
    startCells[0][1] = (Math.floor(Math.random() * (h / 2 - 2))) * 2 + 1;

    Dig(startCells[0][0], startCells[0][1]);

    for (x = 0; x < w; x++)
    {
	for (y = 0; y < h; y++)
	{
	    if (x == 0 || y == 0 || x == w - 1 || y == h - 1)
	    {
		maze[x][y] = "W";						// 外周を壁に戻す
	    }
	}
    }
    let i = 0;
    while (maze[i][1] == "W") {
	if(maze[i + 1][1] == "F") {
	    maze[i + 1][1] = "P";
	    break;
	}
	i = i + 1;
    }
    i = maze.length - 2;
    let j = maze[0].length - 1;
    while(maze[i][j] == "W") {
	if(maze[i - 1][j - 1] == "F") {
	    maze[i - 1][j] = "G";
	    break;
	}
	i = i - 1;
    }
    for (y = 0; y < h; y++) {
	results[y] = [];
	for (x = 0; x < w; x++) {
	    results[y] = results[y] + maze[x][y];
	}
    }
// alert(results[0] + "\n" + results[1]);
    return results;
}


function Dig(x, y) 
{
  while (true) {
    var directions = [];
    if(maze[x][y-1] == "W" && maze[x][y-2] =="W") {directions.push("UP");}
    if(maze[x][y+1] == "W" && maze[x][y+2] =="W") {directions.push("DOWN");}
    if(maze[x-1][y] == "W" && maze[x-2][y] =="W") {directions.push("LEFT");}
    if(maze[x+1][y] == "W" && maze[x+2][y] =="W") {directions.push("RIGHT");}
    if(directions.length == 0) {break;}
    var rnd = Math.floor(Math.random() * directions.length);
    switch (directions[rnd])
    {
	case 'UP':
	    SetPath(x, --y);
            SetPath(x, --y);
            break;
        case 'DOWN':
            SetPath(x, ++y);
            SetPath(x, ++y);
            break;
        case 'LEFT':
            SetPath(--x, y);
            SetPath(--x, y);
            break;
        case 'RIGHT':
            SetPath(++x, y);
            SetPath(++x, y);
            break;
    }
// console.log(maze[0] + "\n" + maze[1] + "\n" + maze[2] + "\n" + maze[3] + "\n" + maze[4] + "\n" + maze[5]);
    var cell = GetStartCell();
    if(cell != null)
    {
	Dig(cell[0], cell[1]);
    }
  }
}


function SetPath(x, y) 
{
    maze[x][y] = "F";
    if((x % 2 == 1) && (y % 2 == 1))
    {
	var Temp = [x, y];
	startCells.push(Temp);
    }
}


function GetStartCell()
{
    if(startCells.length == 0) {return null;}
    var rnd = Math.floor(Math.random() * startCells.length);
    var cell = startCells[rnd];
    startCells.splice(rnd, 1);
    return cell;
}
```

##Step-8: 敵を出現させる
追加として、「phina_sample_06_with_sound_effect.js」に敵（gost）を出現させランダムに移動させてみたものが「phina_sample_08_with_sound_effect.js」です。　もちろんplayerが敵と当たればGame Overとなります。　なお、敵の出現数・位置は「stage_02.js」での"E"文字で設定しており、壁やBOXと同様に表示させています。
[Step-8のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_08_with_sound_effect.html)

```javascript:phina_sample_08_with_sound_effect.js（敵の出現・移動部分）
    for(var k = 0; k < EN_counter; k++) {
	    EN_x[ k ] = Math.floor(ENEMY[ k ].x / P_size);
	    EN_y[ k ] = Math.floor(ENEMY[ k ].y / P_size);
	    EC_x[ k ] = Math.ceil(ENEMY[ k ].x / P_size);
	    EC_y[ k ] = Math.ceil(ENEMY[ k ].y / P_size);
	    var Temp = Math.random() * 5;
	    if(Temp < 1 && EN_flag[ k ] == "stay") {
		EN_flag[ k ] = "left";
		EN_temp[ k ] = 0;
	    }
	    else if(Temp < 2 && EN_flag[ k ] == "stay") {
		EN_flag[ k ] = "right";
		EN_temp[ k ] = 0;
	    }
	    else if(Temp < 3 && EN_flag[ k ] == "stay") {
		EN_flag[ k ] = "up";
		EN_temp[ k ] = 0;
	    }
	    else if(Temp < 4 && EN_flag[ k ] == "stay") {
		EN_flag[ k ] = "down";
		EN_temp[ k ] = 0;
	    }
	    else {

	    }

	if(EN_flag[ k ] == "left") {
		if(pos[EC_x[ k ] - 1][EN_y[ k ]] == "F") {
			EN_temp[ k ] += 1;
			ENEMY[ k ].x -=Step;
			if(app.frame % F_rate === 0) {
			    ENEMY[ k ].frameIndex = (ENEMY[ k ].frameIndex === 3) ? 5:3;
			}
		    if(EN_temp[ k ] >= P_size/Step) {
			EN_flag[ k ] = "stay";
		    }
		} else {
		    EN_flag[ k ] = "stay";
		}
	}
	if(EN_flag[ k ] == "right") {
		if(pos[EN_x[ k ] + 1][EN_y[ k ]] == "F") {
			EN_temp[ k ] += 1;
			ENEMY[ k ].x +=Step;
			if(app.frame % F_rate === 0) {
			    ENEMY[ k ].frameIndex = (ENEMY[ k ].frameIndex === 6) ? 8:6;
			}
		    if(EN_temp[ k ] >= P_size/Step) {
			EN_flag[ k ] = "stay";
		    }
		} else {
		    EN_flag[ k ] = "stay";
		}
	}
	if(EN_flag[ k ] == "up") {
		if(pos[EN_x[ k ]][EC_y[ k ] - 1] == "F") {
			EN_temp[ k ] += 1;
			ENEMY[ k ].y -=Step;
			if(app.frame % F_rate === 0) {
			    ENEMY[ k ].frameIndex = (ENEMY[ k ].frameIndex === 9) ? 11:9;
			}
		    if(EN_temp[ k ] >= P_size/Step) {
			EN_flag[ k ] = "stay";
		    }
		} else {
		    EN_flag[ k ] = "stay";
		}
	}
	if(EN_flag[ k ] == "down") {
		if(pos[EN_x[ k ]][EN_y[ k ] + 1] == "F") {
			EN_temp[ k ] += 1;
			ENEMY[ k ].y +=Step;
			if(app.frame % F_rate === 0) {
			    ENEMY[ k ].frameIndex = (ENEMY[ k ].frameIndex === 0) ? 2:0;
			}
		    if(EN_temp[ k ] >= P_size/Step) {
			EN_flag[ k ] = "stay";
		    }
		} else {
		    EN_flag[ k ] = "stay";
		}
	}
	if(EN_flag[ k ] == "stay") {

	}

	for(var m=0; m<EN_counter; m++) {
	    if(player.hitTestElement(ENEMY[ m ])) {
		END_flag = "Game Over !";
		SoundManager.play('GAMEOVER');
		this.nextLabel ="title";
		this.exit();
	    }
	}
    }
```

##Step-9: 敵をBOXで潰すことができるようにする
さらに敵をBOXで潰すことができるようにしました。　左側での対処プログラムを以下にしまします。
[Step-9のデモ実行](https://to-fujita.github.io/PhinaJS_Sample_Games/index_Phina_09_with_sound_effect.html)

```javascript:phina_sample_09_with_sound_effect.js（敵をBOXで潰す／左側対応部分）
	if(EN_flag[ k ] == "left") {
	    ENEMY[ k ].frameIndex = 3;
	    if(pos[EC_x[ k ] - 1][EN_y[ k ]] == "F") {
		EN_temp[ k ] += 1;
		ENEMY[ k ].x -=Step;
		if(app.frame % F_rate === 0) {
		    ENEMY[ k ].frameIndex = (ENEMY[ k ].frameIndex === 3) ? 5:3;
		}
		if(EN_temp[ k ] >= P_size/Step) {
		    EN_flag[ k ] = "stay";
		}
	    }
	    else if((pos[EN_x[ k ]][EN_y[ k ]] == "B") && (pos[EN_x[ k ] - 1][EN_y[ k ]] != "F")) {
		EN_temp = k;
		ENEMY[ EN_temp ].tweener.fadeOut(1000).call(function(){
		    ENEMY[ EN_temp ].remove();
		    ENE_flag = 1;
		}).play();
	    } else {
		EN_flag[ k ] = "stay";
	    }
	}
	else if(EN_flag[ k ] == "right") {
```

##Reference
1. [github Cocos2d_Sample_games](https://github.com/To-Fujita/Cocos2d_Sample_games)
2. [Home Page of Cocos2d-x](https://www.cocos.com/en/)
3. [Download page for Cocos2d-JS](https://cocos2d-x.org/filecenter/jsbuilder/)
4. [Home Page of PIKA's GAME](https://dorapika.wixsite.com/pikasgame)
5. [Home Page of Pipoya](http://blog.pipoya.net/)
6. [Algoful : Algorithm for making a maze](http://algoful.com/Archive/Algorithm/MazeDig)
7. [無料効果音で遊ぼう！](https://taira-komori.jpn.org/)

##おまけ
JavaScriptベースのゲームエンジンは他にも多数ありますが、その内の幾つかで同様のゲームを作成してみました。　ソースファイルを覘くとゲームエンジン別の特徴がうかがえるかと思います。

- [Cocos2d.JSでゲームを作ってみた](https://qiita.com/halboujp/items/3d99c122bdc471caa5b8)
- [Phaser3.JSでゲームを作ってみた](https://qiita.com/halboujp/items/7f6cfcd73a1c95023511)
- Phina.JSでゲームを作ってみた（本記事です）
- [Babylon.jsで3Dゲームを作成してみた（その１：迷路編）](https://qiita.com/halboujp/items/8b4b07136420c5defb13)　　
- [Babylon.jsで3Dゲームを作成してみた（その２：脱出パズル編）](https://qiita.com/halboujp/items/751c42f3bda10adb7674)　

以上