ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンに複数の敵キャラを表示する

enchant.js
enchant.js / Ubiquitous Entertainment Inc.

 ”ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンに表示する敵キャラのステータス情報を作成して、その名前をメッセージウィンドウに表示する”の記事で「敵キャラのステータス」を設けたので、今回は、戦闘毎にランダムで複数の敵キャラを表示できるようにしてみたいと思う。これまでにウザイム(敵キャラ)を4種ほど用意しているので、この4種をランダムに複数、戦闘シーンに表示できるようにしてみよう。

enemy32
(※透過処理済:GIMPで特定の色を透明(透過)にする方法

 あと、これまでの戦闘用の処理を、「battle.js」という新規JavaScriptファイルを作って、ここへ移動させようと思う(ライブラリ化ではない)。JavaScriptファイルの読み込みでHTTPリクエストが1回増えるが、後の速度検証でひとつのJavaScriptファイルに統合しておくべきかどうかは判断することにする。

■「battle.js」の読み込み順

 趣味の話になるが、私はmain.jsがbattle.jsを参照する形が分かり易いと思ったので、battle.jsを先に読み込むようにした。

[html firstline=”1″ highlight=”10″ title=”index.html”]
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>ここにゲームのタイトルを書く</title>
<link type="text/css" rel="stylesheet" media="all" href="./css/main.css">
<script type="text/javascript" src="./js/lib/enchant.js"></script>
<script type="text/javascript" src="./js/lib/ui.enchant.js"></script>
<script type="text/javascript" src="./js/battle.js"></script>
<script type="text/javascript" src="./js/main.js"></script>
</head>
<body>
<!– ここにenchant.jsで作られたゲームが表示される –>
</body>
</html>
[/html]

■敵キャラのステータス設定を変動にする

 まずやっておきたいことがある。例えば、同種の敵が複数体出現した場合に、全員が同じ能力(ステータス)だと面白くないので、”ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンに表示する敵キャラのステータス情報を作成して、その名前をメッセージウィンドウに表示する”の記事でテーブルに定めた敵キャラ情報それぞれの規定値に対して、「±10%」のステータス変動を施してみる。

 以下は、「battle.js」へ移動後のコードだ。

[javascript firstline=”1″ highlight=”34,70,75,76,77,78,79,80,81″ title=”battle.js”]
/**
* 画像ファイルパス.
*/
var IMG_ENEMY_32 = ‘./img/enemy32.png’; // 敵キャラ画像(32×32).

/**
* 敵キャラ(32×32)番号.
*/
var ENEMY_32 = {
UZAIME: 0, // ウザイム.
UZAIME_FRAMBOISE: 1, // ウザイムフランボワーズ.
UZAIME_PISTACHE: 2, // ウザイムピスターシュ.
UZAIME_MANGUE: 3, // ウザイムマング.
}

/**
* 敵キャラ情報.
* label : 名前
* size : サイズ
* system : 系統
* hp : HP
* sp : SP
* attack : 攻撃力
* defense : 守備力
* quick : 素早さ
* exp : 経験値
* recette : 通貨(ルセット)
* drop : 落とすアイテム
* behavior : 行動パターン
* ability : 特殊能力
* resist : 耐性
*/
var enemiesInfo = {
0: { label:"ウザイム", size: 32, system:0, hp:7, sp:0, attack:9, defense:8, quick:3, exp:1, recette:1, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
1: { label:"ウザイムフランボワーズ", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
2: { label:"ウザイムピスターシュ", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
3: { label:"ウザイムマング", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
}

/**
* 敵キャラを生成するクラス.
*/
var Enemy = enchant.Class.create( enchant.Sprite, {
/**
* @name Enemy.
* @class
* @param {Number} no 生成する敵キャラ番号.
* @param {Number} x 敵キャラ表示X座標.
* @param {Number} y 敵キャラ表示Y座標.
* @constructs
* @extends enchant.Sprite
*/
initialize: function( no, x, y ) {
if ( 100 > no ) {
enchant.Sprite.call( this, 32, 32 ); // 継承元をコール、幅と高さを設定.
this.image = core.assets[ IMG_ENEMY_32 ]; // 敵キャラ画像(32×32)を設定.
this.frame = no; // 指定した敵キャラのフレームを設定.
} else if ( 200 > no ) {
enchant.Sprite.call( this, 64, 64 ); // 継承元をコール、幅と高さを設定.
this.image = core.assets[ IMG_ENEMY_64 ]; // 敵キャラ画像(64×64)を設定.
this.frame = no – 100; // 指定した敵キャラのフレームを設定.
} else if ( 300 > no ) {
enchant.Sprite.call( this, 96, 96 ); // 継承元をコール、幅と高さを設定.
this.image = core.assets[ IMG_ENEMY_96 ]; // 敵キャラ画像(96×96)を設定.
this.frame = no – 200; // 指定した敵キャラのフレームを設定.
}
this.x = x; // X座標.
this.y = y; // Y座標.

var c = 1 + ((Math.floor( Math.random() * 3 ) – 1) / 10); // 係数(0.9~1.1)の算出.

this.no = no; // No.
this.label = enemiesInfo[ no ].label; // 名前.
this.system = enemiesInfo[ no ].system; // 系統.
this.hp = Math.round( enemiesInfo[ no ].hp * c ); // HP.
this.sp = Math.round( enemiesInfo[ no ].sp * c ); // SP.
this.attack = Math.round( enemiesInfo[ no ].attack * c ); // 攻撃力.
this.defense = Math.round( enemiesInfo[ no ].defense * c ); // 守備力.
this.quick = Math.round( enemiesInfo[ no ].quick * c ); // 素早さ.
this.exp = Math.round( enemiesInfo[ no ].exp * c ); // 経験値.
this.recette = Math.round( enemiesInfo[ no ].recette * c ); // 通貨(ルセット).
this.drop = enemiesInfo[ no ].drop; // 落とすアイテム.
this.behavior = enemiesInfo[ no ].behavior; // 行動パターン.
this.ability = enemiesInfo[ no ].ability; // 特殊能力.
this.resist = enemiesInfo[ no ].resist; // 耐性.
},
getLabel: function() {
return this.label; // 名前を返却.
}
});
[/javascript]

 70行目で係数の計算をしている。「Math.random()」は、0以上1未満の値をランダムに返却する関数だ。そして「Math.floor()」は小数点以下の値を”切り捨て”した値を返却する関数となっている。

 今回、±10%の変動を施そうと思うので、まず、「3」を掛けている。この時点では、

  0以上1未満 ⇒切り捨て ⇒0
  1以上2未満 ⇒切り捨て ⇒1
  2以上3未満 ⇒切り捨て ⇒2

 となる。これでもし「Math.ceil()」の小数点以下”繰り上げ”関数を用いていたら、

  0         ⇒繰り上げ ⇒0
  0より大きく1まで ⇒繰り上げ ⇒1
  1より大きく2まで ⇒繰り上げ ⇒2
  2より大きく3まで ⇒繰り上げ ⇒3

 となり、「0」は「Math.random()」がジャスト0を生成時にのみしか得られなくなるので注意が必要だ。

 さて、「Math.floor()」で「0」「1」「2」が得られたら、「マイナス1」して「1/10」して「プラス1」すれば、「0.9(90%)」「1.0(100%)」「1.1(110%)」という係数が得られる。34行目でウザイムのステータスを設定してみたが、75行目のようにHP(ヒットポイント)に係数を掛ける場合、HPは「7」だから、「6」「7」「8」という具合に、敵キャラ生成時に変動したステータスを設定できるようになる。
 ※「Math.round()」は四捨五入

 これで同一戦闘シーンでウザイムが3体出現したとしても、3体ともが同一のステータスを有しいているというわけではなくなるので、ちょっと面白味が増すのではないかと思っている。

■エリア情報の設定

 遭遇する敵キャラをランダムに決めたいと思う。ドラクエなんかでもそうだが、地域、エリアによってエンカウント率や遭遇する敵キャラの種別は異なっている。始まりの町の周辺は弱い敵キャラのみだったり、ラスボスの手前は強敵だらけだったり、だ。それを先に設定しておこう。

[javascript firstline=”521″ highlight=”” title=”main.js”]
/**
* エリア情報.
* label : エリア名
* enemyMax : 一度に遭遇する敵キャラの最大数
* enemies : 遭遇する敵キャラ種別
*/
var areaInfo = {
0: { label:"ここにエリア名", enemyMax:13, enemies:[ ENEMY_32.UZAIME, ENEMY_32.UZAIME_FRAMBOISE, ENEMY_32.UZAIME_PISTACHE, ENEMY_32.UZAIME_MANGUE ] },
}
[/javascript]

 今は取り敢えず、「一度に遭遇する敵キャラの最大数」と「遭遇する敵キャラ種別」だけをテーブルに設定した。このテーブル、エリア情報を用いて、最大で13の敵キャラ、最大で4種のウザイムを表示するように作成していこう。

 呼び出しのイメージはこうだ。

[javascript firstline=”178″ highlight=”182″ title=”main.js – function createScene()”]
var cmd5 = new CommandWindow( (WINDOW_COMMAND_WIDTH * 4), WINDOW_COMMAND_Y );
cmd5.setText( "逃走" );
scene.addChild( cmd5 );

scene.addChild( createEnemyGroup( areaInfo[ 0 ] ) ); // シーンに敵キャラを追加.

var pad = new Pad(); // バーチャルパッド(十字方向キーパッド)を生成.
pad.moveTo( 10, SCREEN_HEIGHT – 125 ); // 表示座標を設定.
scene.addChild( pad ); // シーンに追加.
[/javascript]

 エリア情報を引数に取って、そのエリア情報から出現する敵キャラを導き出して、最終的にGroupに詰めて呼び出し元に返却する「createEnemyGroup()」という関数を作成しよう。

■エリア情報から複数の敵キャラをランダムに表示する

 画面幅に最大に表示できる敵キャラの数は、画面両端から敵キャラ(32×32)を一体ずつ引いた「13体」としている。この最大数を超えた情報がエリア情報に定義されている場合は丸めて「13体」として処理していく。

 ただ遭遇数はランダムにする為、その最大数を基にランダムで決定する。最小で必ず1体になる。

 1体をGroupに追加するごとに、Groupの合計幅を計算するようにして、あと何体分の表示が可能かを常に判定するようにする。エリア情報の遭遇最大数に達する前に、既に次の敵キャラが画面幅に収まらないくらいまで表示されていたら、そこで敵キャラの選出処理は終了としている。

 この処理を行うために、敵キャラ情報(enemiesInfo)に「size」を設けた。なので、「ENEMY_32」としていた定義は「ENEMY」に変更した(14行目)。

[javascript firstline=”1″ highlight=”6,8,9,11,12,13,14,24″ title=”battle.js”]
/**
* 画像ファイルパス.
*/
var IMG_ENEMY_32 = ‘./img/enemy32.png’; // 敵キャラ画像(32×32).

var BATTLE_SCREEN_WIDTH = 480; // 戦闘画面の幅.

var ENEMY_BOTTOM_Y = 46 + (32 * 8); // 敵キャラ表示Y座標.
var ENEMY_32_MAX = (BATTLE_SCREEN_WIDTH / 32) – 2; // 敵キャラ(32×32)表示最大数.

/**
* 敵キャラ番号.
*/
var ENEMY = {
UZAIME: 0, // ウザイム.
UZAIME_FRAMBOISE: 1, // ウザイムフランボワーズ.
UZAIME_PISTACHE: 2, // ウザイムピスターシュ.
UZAIME_MANGUE: 3, // ウザイムマング.
}

/**
* 敵キャラ情報.
* label : 名前
* size : サイズ
* system : 系統
* hp : HP
* sp : SP
* attack : 攻撃力
* defense : 守備力
* quick : 素早さ
* exp : 経験値
* recette : 通貨(ルセット)
* drop : 落とすアイテム
* behavior : 行動パターン
* ability : 特殊能力
* resist : 耐性
*/
var enemiesInfo = {
0: { label:"ウザイム", size: 32, system:0, hp:7, sp:0, attack:9, defense:8, quick:3, exp:1, recette:1, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
1: { label:"ウザイムフランボワーズ", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
2: { label:"ウザイムピスターシュ", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
3: { label:"ウザイムマング", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
}
[/javascript]
[javascript firstline=”105″]
/**
* エリア情報から敵キャラを生成する関数.
*/
function createEnemyGroup( areaInfo ) {
var enemyGroup = new Group(); // グループを生成.
enemyGroup.width = 0; // グループの幅を初期化.

var remain = areaInfo.enemyMax; // エリア情報から敵キャラの最大遭遇数を取得.
if ( ENEMY_32_MAX < remain ) { // 表示可能数を敵キャラの最大遭遇数が超えている場合.
remain = ENEMY_32_MAX; // 表示可能数に丸める.
}
remain = Math.floor( Math.random() * remain ) + 1; // 敵キャラ遭遇数をランダムに決定.

var rename = new Array(); // リネーム処理用配列を用意.
var i = 0;
while ( i < areaInfo.enemies.length ) { // 敵キャラ種別数分の準備.
rename[ i ] = new Array();
i++;
}

while ( remain > 0 ) {
i = Math.floor( Math.random() * areaInfo.enemies.length ); // 生成する敵キャラ種別をランダムに決定.
var enemyNo = areaInfo.enemies[ i ]; // 敵キャラ番号の取得.
if ( ((ENEMY_32_MAX * 32) – enemyGroup.width) < enemiesInfo[ enemyNo ].size ) { // この敵キャラを表示できる空きがあるか判定.
// 表示できる空きがない場合、次はもう敢えて選出しない.
break;
}
enemyGroup.addChild( new Enemy(
enemyNo,
enemyGroup.width,
(ENEMY_BOTTOM_Y – enemiesInfo[ enemyNo ].size) ) ); // 敵キャラを生成してグループに追加.
enemyGroup.width += enemiesInfo[ enemyNo ].size; // グループの幅に加算.
remain–; // 残遭遇数をデクリメント.
rename[ i ].push( enemyGroup.childNodes.length – 1 ); // 種別毎に遭遇数をカウントしておく.
}

// リネーム処理(同種が複数体存在する場合は名前末尾にA,B,C,~を付与).
for ( i = 0 ; i < areaInfo.enemies.length ; i++ ) { // 種別毎の遭遇数をチェック.
if ( 1 < rename[ i ].length ) { // 同種が複数体存在.
for ( var j = 0 ; j < rename[ i ].length ; j++ ) {
enemyGroup.childNodes[ rename[ i ][ j ] ].label += String.fromCharCode( ‘A’.charCodeAt( 0 ) + j );
}
}
}

enemyGroup.x = (BATTLE_SCREEN_WIDTH – enemyGroup.width) / 2; // グループを画面のセンターへ配置.

return enemyGroup;
}
[/javascript]

 実行結果は以下だ。戦闘突入毎にランダムに選出されている。ちゃんと最大の13体にもなるし、1体にもなる。

enchant131
enchant133
enchant132

■サイズの異なる敵キャラを混ぜてみる

 これまでは「32×32」の敵キャラしか扱ってこなかったので、ここで「96×96」の敵キャラを混ぜて表示してみよう。仕組みは対応できるように用意してきているので、基本は画像と情報の追加くらいでいけるはずだ。

 ではやってみよう。追加する敵キャラは「ウザイムの怨み」だ。

enemy96
(※透過処理済:GIMPで特定の色を透明(透過)にする方法

[javascript firstline=”” highlight=”4,19,44″ title=”battle.js”]
/**
* 画像ファイルパス.
*/
var IMG_ENEMY_32 = ‘./img/enemy32.png’; // 敵キャラ画像(32×32).
var IMG_ENEMY_96 = ‘./img/enemy96.png’; // 敵キャラ画像(96×96).

var BATTLE_SCREEN_WIDTH = 480; // 戦闘画面の幅.

var ENEMY_BOTTOM_Y = 46 + (32 * 8); // 敵キャラ表示Y座標.
var ENEMY_32_MAX = (BATTLE_SCREEN_WIDTH / 32) – 2; // 敵キャラ(32×32)表示最大数.

/**
* 敵キャラ番号.
*/
var ENEMY = {
UZAIME: 0, // ウザイム.
UZAIME_FRAMBOISE: 1, // ウザイムフランボワーズ.
UZAIME_PISTACHE: 2, // ウザイムピスターシュ.
UZAIME_MANGUE: 3, // ウザイムマング.
GRUDGE_OF_UZAIME: 200, // ウザイムの怨み.
}

/**
* 敵キャラ情報.
* label : 名前
* size : サイズ
* system : 系統
* hp : HP
* sp : SP
* attack : 攻撃力
* defense : 守備力
* quick : 素早さ
* exp : 経験値
* recette : 通貨(ルセット)
* drop : 落とすアイテム
* behavior : 行動パターン
* ability : 特殊能力
* resist : 耐性
*/
var enemiesInfo = {
0: { label:"ウザイム", size: 32, system:0, hp:7, sp:0, attack:9, defense:8, quick:3, exp:1, recette:1, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
1: { label:"ウザイムフランボワーズ", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
2: { label:"ウザイムピスターシュ", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
3: { label:"ウザイムマング", size: 32, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
200: { label:"ウザイムの怨み", size: 96, system:0, hp:100, sp:0, attack:1, defense:0, quick:0, exp:10, recette:10, drop:0, behavior:[0,1,2,3,4,5], ability:0, resist:0 },
}
[/javascript]

 エリア情報にも忘れずに足す。

[javascript firstline=”521″ highlight=”528″ title=”main.js”]
/**
* エリア情報.
* label : エリア名
* enemyMax : 一度に遭遇する敵キャラの最大数
* enemies : 遭遇する敵キャラ種別
*/
var areaInfo = {
0: { label:"ここにエリア名", enemyMax:13, enemies:[ ENEMY.UZAIME, ENEMY.UZAIME_FRAMBOISE, ENEMY.UZAIME_PISTACHE, ENEMY.UZAIME_MANGUE, ENEMY.GRUDGE_OF_UZAIME ] },
}
[/javascript]

 実行結果は以下だ。よしうまくいったぞ!

enchant134
enchant135
enchant136

(アシベズヘア@ashibehair_m

╋ 関連記事 ━━━

  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンに戦闘用コマンド一覧(攻撃/特技/道具/防御/逃走)を表示する
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 文字列に「8bitファミコン風のWEBフォント(漢字も使える!フリー!)」を採用する
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンにステータスゲージ(HPゲージ)を表示する
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンにステータスウィンドウ(状態表示枠)を表示する
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【RPG(ロールプレイングゲーム)篇】 戦闘シーンに切り替える
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【導入篇】 バーチャルボタン(Aボタン/Bボタン/Xボタン/Yボタン)を表示してみる
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【導入篇】 キャラクター画像をバーチャルパッド(十字方向キーパッド)で動かしてみる
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【導入篇】 キャラクター画像をキー入力で動かしてみる
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【導入篇】 キャラクター画像を表示してみる
  • ゲーム開発をenchant.js(HTML5 + JavaScript)で! 【導入篇】 enchant.jsのダウンロードと開発準備
  • コメントを残す

    メールアドレスが公開されることはありません。 * が付いている欄は必須項目です