バトルの状態
PHPポケモンでも様々な技を再現してきましたが、まだまだ未実装のものはたくさんあります。そのほとんどがイレギュラー処理の必要なものだったりします。
それらをしっかりと解決していくためにも、今回は「バトル状態」をひとまとめに管理できるようにシステムの見直しを行います。
ひとまとめにする項目は以下の通りです。
- フィールド
- にげる実行回数
- ターンダメージ
上記のように、バトル中に管理しなければならない項目が、現在はコントローラーに直接紐付いています。ですが、この調子で増やしていくと、コントローラーがパンパンになってしまうので、これらを効率よく管理できるようにまとめます。
ランクや状態変化についてもバトルステータスに該当しますが、これはポケモンに直接影響を与えているものとして、今まで通りポケモンに対して持たせておきます。ターンダメージも同じような位置関係ではありますが、リセット漏れなどを防ぐためにも細かなものはバトル状態として扱いましょう。
保存ステータスの増加
まず、これから実装する技でバトル状態として保存しなければならない可能性が高いものを上げていきましょう。あくまで想定のため、いざ実装する際にはそこまで複雑化しない可能性もあります。
へんしん、ものまね、オウム返し、ネコにこばん
初代技でざっと取り上げるとこの辺りがバトル状態として保存が必要になると予想されます。ものまねに関しては、レベルアップ習得するポケモンがいないのでさほど問題ではありませんが、オウム返しは序盤から登場する「ポッポ一家」が覚えるため早めに実装したいところです。
ネコにこばんは「ニャース一家」しか覚えませんが、こちらもレベルアップ技として用意する必要があるので、順を追って揃えなければなりません。
へんしんに関しては、実装できなければ「メタモン」が実装できないことになります。ポケモンを欠けさせるわけにはいかないので、こちらもしっかりと作り込んでいきます。
ざっと上げただけでもこれらを管理する必要があります。第2世代に入れば更に増える見込みです。
クラス化(BattleState)
バトル状態の管理方法についてですが、クラス化することで一括管理をします。レスポンスなどはヘルパー関数を用意しましたが、こちらは関数の重複なども防ぐためにも、今回は実装を見送りましょう。
バトル状態クラス(/Classes/BattleState.php)
<?php
/**
* バトル状態クラス
*/
class BattleState
{
/**
* 逃走を試みた回数
* @var integer
*/
private $run;
/**
* フィールド効果
* @var integer
*/
private $fields;
/**
* このターンに受けた攻撃によるダメージ量
* @var array
*/
private $turn_damages;
/**
* @return void
*/
public function __construct()
{
$this->init();
}
/**==================================================================
* 初期化・初期値
==================================================================**/
/**
* 初期化
* @return void
*/
public function init() :void
{
$this->run = 0;
$this->dafaultFields();
$this->dafaultTurnDamages();
}
/**
* ターン始めの状態へ初期化
* @return void
*/
public function turnInit() :void
{
$this->dafaultTurnDamages();
}
/**
* ターンダメージの初期値
* @return void
*/
public function dafaultTurnDamages() :void
{
$this->turn_damages = [
'friend' => [
'physical' => 0,
'special' => 0,
],
'enemy' => [
'physical' => 0,
'special' => 0,
],
];
}
/**
* フィールドの初期値
* @return void
*/
public function dafaultFields() :void
{
$this->fields = [
'friend' => [],
'enemy' => [],
];
}
/**==================================================================
* にげる
==================================================================**/
/**
* にげる実行
* @return void
*/
public function run() :void
{
$this->run++;
}
/**
* にげるの回数取得
* @return integer
*/
public function getRun() :int
{
return $this->run;
}
/**==================================================================
* フィールド
==================================================================**/
/**
* フィールド状態の確認
* @param string $target
* @param Field:object $field
* @return boolean
*/
public function checkField(string $target, object $field) :bool
{
if(isset($this->fields[$target][get_class($field)])){
return true;
}else{
return false;
}
}
/**
* ターンダメージの取得
* @param target:string::friend|enemy
* @return array
*/
public function getField(string $target) :array
{
return $this->fields[$target];
}
/**
* フィールドのセット
* @param target:string
* @param field:object::Field
* @param turn:integer
* @return void
*/
public function setField(string $target, object $field, int $turn) :void
{
if($this->checkField($target, $field)){
// 既にセットされている
setMessage($field->getAlreadyMessage($target));
}else{
// フィールドをセット
$this->fields[$target][get_class($field)] = $turn;
setMessage($field->getSetMessage($target));
}
}
/**
* フィールド状態の解除
* @param position:string
* @param field:object::Field
* @return void
*/
public function releaseField(string $position, object $field) :void
{
if($this->checkField($position, $field)){
// 解除
unset($this->fields[$position][get_class($field)]);
// 解除メッセージをセット
setMessage($field->getReleaseMessage($position));
}
}
/**
* フィールドのターンカウントをすすめる
* @return void
*/
public function goTurnFields() :void
{
foreach(['friend', 'enemy'] as $position){
//全ターゲットのフィールド状態を解除
foreach($this->fields[$position] as $field => &$turn){
$turn--;
if($turn <= 0){
// 残ターンが0ターン以下になれば解除
$this->releaseField($position, new $field);
}
}
}
}
/**==================================================================
* ターンダメージ
==================================================================**/
/**
* ターンダメージの取得
* @param target:string::friend|enemy
* @param species:string::physical|special
* @return integer
*/
public function getTurnDamage(string $target, string $species) :int
{
return $this->turn_damages[$target][$species];
}
/**
* このターン受けたダメージの格納
* @param target:string::friend|enemy
* @param species:string::physical|special
* @param damage:integer
* @return void
*/
public function setTurnDamage(string $target, string $species, int $damage) :void
{
$this->turn_damages[$target][$species] = $damage;
}
}
トレイト等に用意していたものを、クラスに移し替えました。あとは、こちらをシリアライズしながら画面移管で渡しながら管理をします。
前回進化コントローラーで実装したように、コントローラーのデストラクタを使ってデータの引き継ぎ処理をします。
バトルコントローラー(/App/Controllers/Battle/BattleController.php)
/**
* @return void
*/
public function __destruct()
{
// 次画面へ送るデータ
$_SESSION['__data']['party'] = serializeObject($this->party);
$_SESSION['__data']['enemy'] = serializeObject($this->enemy);
$_SESSION['__data']['battle_state'] = serializeObject($this->battle_state);
$_SESSION['__data']['before_responses'] = serializeObject(getResponses());
$_SESSION['__data']['before_modals'] = serializeObject(getModals());
$_SESSION['__data']['before_messages'] = getMessages();
// 親デストラクタの呼び出し
parent::__destruct();
}
今まではにげる回数などをプロパティとして管理していましたが、オブジェクトとして管理することにより、参照渡しになります。そのため、無駄な引き継ぎ処理が減るので保守性も良くなりました。
ただ、しっかりとオブジェクトの状態や動きを把握しておくことが前提となります。
セッションとリダイレクトについて
今回躓いたポイントとして、セッションとリダイレクトの関係性をまとめておきます。
フレームワークなどを使えば、なかなかセッションに頼るようなこともありませんが、PHPポケモンではDBを使わない関係上、セッションの状態をしっかりと把握しておかなければなりません。
今回バトル状態をクラス化させることで発覚した問題として、セッション処理後にリダイレクトすると、セッションが正常に処理されていないということが発覚しました。
例えばバトル画面からホーム画面へ戻る際に、不要になったセッションをunsetしてからリダイレクトさせていましたね。ですが、これが正常にunsetされず残ったままになり、次のバトル画面へ引き継がれてしまっていました。これは、セッションの更新処理がリダイレクトすることによって失われてしまっていたからです。
なので、リダイレクトをする際にセッションの処理を完了させるための関数を呼び出します。
コントローラー親クラス(/App/Contorllers/Controller.php)
/**
* 画面移管
* @return void
*/
protected function redirect()
{
// セッションを保存
session_write_close();
// リダイレクト
header("Location: ./", true, 307);
// 処理終了
exit;
}
画面移管をメソッドにまとめました。
こちらで最初に呼び出しているsession_write_closeが、セッションを更新させるための関数となっています。
session_write_close(PHP.net)
こうすることで、記述通りセッションを更新してから画面移管することができました。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは、バトル状態のクラス化と、セッションとリダイレクトの関係についてご紹介しました。
現在プログラミング学習に取り組んでいる方や、ゲーム開発に興味を持っている方は、ぜひ参考にしてみてくださいね。