Phaser3+typescriptでSRPGを開発してみる #013 ユニットの移動(3)
しばらくぶりです。色々と忙しく、しばらくぶりになってしまいました。 仕事でもSpringBootでWebアプリやバッチを開発することになったり、プライベートではコロナ禍におけるレコーディングオンラインセッションに参加、 そのあとはコロナが弱まったことで飲み会等で、このブログを執筆するどころか、SRPG開発もかなり中断してしまいました。
それにも関わらず、見てくださる方がかなりいらっしゃって、大変嬉しい限りです。
ということで、SRPG開発は再開できていないものの、以前作ったネタでHatena Blogを書くことでリハビリしながら、ゲーム開発に戻っていきたいと思います。
本題
前回は味方ユニットを動かせる領域を表示する、という地味な内容でしたが、今回はTweenをついに動かそうと思います。 今回は、相手(敵)側も自動で動かしたいと思います。
ユニットの移動(3)
味方ユニットの移動
味方ユニットを動かすには、
動かすユニットを選択(カーソルの移動は#009、#011で解説。このブログでは選択にはSpaceキーを割当)
動かせる範囲を可視化(#012で解説)
動かす先を設定(今回はEnterキーを割当)
実際に動かす(#011で解説)
が必要です。今まで解説してきたロジックを使えば実現できます。
動かすユニットの選択と動かす先の設定
この2つの仕様を今回は同じメソッド内でやっています。
このcheckAllyUnit()
メソッドは、update()
メソッドから呼んでいます。
GameScene.ts
/** * ゲーム中の処理 * @override */ public update(): void { // 移動中のユニットの確認。位置の更新 // 移動終了するまでは以降の処理はしない (中略) // カーソルが動いているときは、移動終了まで以降の処理をしない (中略) // キー処理チェック if (this._cursorKeys === undefined) { throw Error("EG0001: cursorKeyが設定されていません。"); } if (this._keys === null && this._keys === undefined) { throw Error("EG0002: keysが設定されていません"); } // カーソル・キャラが自動で動いていない場合 // 味方ターンの場合の初期対応 if (this._gameStatus.turnTeam === Team.ALLY) { // 味方全員が終わりの場合、ターン終了 this.checkTurnEnd(this._gameData.allyUnits); // カーソルを動かす this.moveCursor(); // カーソルの該当セルにユニットが居た場合の処理 this.checkAllyUnit(); } else if (this._gameStatus.turnTeam === Team.ENEMY) { // (中略)敵ターンの場合 }
checkAllyUnit()
メソッドは、味方ターンで、(1)移動中のユニットが居ないとき (2)カーソルが動いていないとき に呼ばれることになります。
checkAllyUnit()
メソッドの中で、現状のステータス(味方ターゲットを選択しているか否か?)に基づいて場合分けして、キー押下時のハンドリングをしています。
- 味方ターゲットが選択中(=
this.targetUnit
がnullでない場合) →Enterキーが押されたら、(移動可能であれば)その場所へ移動 - 味方ターゲットが未選択の場合(上記以外)
→Spaceキーが押されたら、味方ターゲットを選択(
targetSelectedUnit(GameUnit)
メソッドが呼ばれる)
GameScene.ts
/** * ユニットがいる場合の対応 */ protected checkAllyUnit(): void { // 既に味方ターゲット選択中 if (this._targetUnit) { // ENTERキーを押下した場合、キャラを移動 if (this._keys.get(GameScene.SETUP_KEY.ENTER).isDown && this._gameData.checkCanMoveToTargetCell(this._targetUnit, this._cursor.currentX, this._cursor.currentY)) { // this.setMovingUnit(this._cursor.currentX, this._cursor.currentY) this._movingUnit = new MovingUnit(this._targetUnit, this._cursor.currentX, this._cursor.currentY); this.moveUnit(this._movingUnit); } // ESCキーを押下した場合、キャンセル else if (this._keys.get(GameScene.SETUP_KEY.ESC).isDown) { this.destroyMoveScope(); this._targetUnit = null; // いったんカーソルも戻さず、ステータスを消す // @TODO 将来的にはカーソルをターゲットに戻す? this.disappearStatusInfoArea(); } } // ターゲットを未選択の場合 else { var selectedUnit: GameUnit = this.getUnitOnCursor(); // どのキャラもいない場合はステータスと可動領域を消す if (!selectedUnit) { this.disappearStatusInfoArea(); this.destroyMoveScope(); } // キャラがいる場合はステータスと可動領域を見せる else if (selectedUnit) { this.showStatusInfoArea(selectedUnit); if (!selectedUnit.turnEnd) { // 一旦、可動域を消す this.destroyMoveScope(); // 再度復活 this.showMoveScope(selectedUnit); } } // カーソルに味方キャラが居て、スペースキーを押下した場合 // ターゲットにする if (this._keys.get(GameScene.SETUP_KEY.SPACE).isDown && selectedUnit && (selectedUnit.team === Team.ALLY) && !selectedUnit.turnEnd) { this.targetSelectedUnit(selectedUnit); } } }
ユニットを選択するSPACEキーを押下されたとき、下記の通り、クラス全体のメンバー変数にセットして、 ユニットをロックオンします。
GameScene.ts
/** * スペースキーを押下して選択した味方ユニットをターゲットにする * @param selectedUnit */ private targetSelectedUnit(selectedUnit: GameUnit) { this.showMoveScope(selectedUnit); this._targetUnit = selectedUnit as GameUnitPhaser; // ステータスを表示する this.showStatusInfoArea(selectedUnit); }
そして、実際に動くわけです。実際に動かすのは#011で実装したので割愛します。
敵ユニットの自動移動
一方で、敵ユニットを動かすときも、動かすモーションについては、味方ユニットのものを流用できます。 但し、動く場所をコンピュータ側で適切に決める必要があります。 この辺りは今は考えず、まずは、右に1歩ずつ動くという固定的な仕様にしたいと思います。
まず、敵ユニットを順次取得して、動かしていきます。この辺りも、いずれは、有利不利を判定して動かす順番を変えたりしたいですね。
GameScene.ts
/** * ゲーム中の処理 * @override */ public update(): void { : (中略) : // カーソル・キャラが自動で動いていない場合 // 味方ターンの場合の初期対応 if (this._gameStatus.turnTeam === Team.ALLY) { // 上記参照 } else if (this._gameStatus.turnTeam === Team.ENEMY) { // 敵全員が終わりの場合、ターン終了 this.checkTurnEnd(this._gameData.enemyUnits); // 次の敵を移動 this.moveEnemyUnit(); } } /** * 敵ユニットを順次移動 */ protected moveEnemyUnit(): void { // 動いていない敵キャラを取得 var turnUnits: GameUnit[] = this._gameData.enemyUnits.filter((unit: GameUnit) => { return unit.isAlive && !unit.turnEnd; }); if (turnUnits.length === 0) { return; } // 戦略に従い移動を準備 // @TODO 今はとりあえず右に1マス進むだけ this._targetUnit = (turnUnits[0] as GameUnitPhaser); let move: { x: number, y: number } = { x: turnUnits[0].currentX + 1, y: turnUnits[0].currentY }; this._movingUnit = new MovingUnit( this._targetUnit, move.x, move.y); // 自動的に移動 this.moveUnit(this._movingUnit); }
味方の移動でも使ったmoveUnit(MovingUnit)
メソッドを使っています。
この結果、こんな感じで動きます(動画でなく静止画の連続ですみません)。
まとめ
今回はユニットの移動にもチャレンジしました。 かなり、SRPGっぽくなってきた気がしますが、まだ戦いまで至っていません。。
そろそろ戦い見据えた実装に入っていこうと思います。
過去の日記
タイトル | 記載内容 | |
---|---|---|
#001 | プロローグ | Phaser3とは |
#002 | 環境設定 | インストール・ビルド環境設定 |
#003 | はじめてのPhaser3 | 初回稼働・キャンバス表示 |
#004 | マップ作成 | タイルセットの読込、画面手動作成、Sceneクラスのライフサイクル |
#005 | Tiledの利用 | Tiledによる画面マップJSON作成、マルチレイヤーの背景画面 |
#006 | JSONファイルの読込 | データのpreload |
#007 | ファイルのキャッシュ | ゲームデータの管理・シングルトンパターン |
#008 | データモデル(ユニット編) | SRPGに必要なデータ・管理方法 |
#009 | カーソルの移動 | キー受け付け、Spriteの活用 |
#010 | 各メソッドのコールと状態遷移 | Sceneメソッドのライフサイクル・デバッグモードの作り方 |
#011 | ユニットの移動(1) | Tweenオブジェクト、味方ユニット移動 |
#012 | ユニットの移動(2) | 味方の可動領域の表示 |