ポケモン預かりシステムの実装
今回は、前回ざっくりと仕様決めをした「ポケモン預かりシステム」を実装していきます。ボックス内では操作する項目が多いため、ボックス自体に1つの画面を用意して、できる限りPHPによる制御だけで完結できるように作成していきます。
ボックスクラスの作成
それではまず、今回の主軸となるボックスクラスを作成しましょう。以下、文中でのポケモン預かりシステムについては「ポケボックス」と表記していきます。
ポケモン預かりシステム(/Classes/Pokebox.php)
<?php
// ポケモンボックス
class Pokebox
{
/**
* ポケモンの格納ボックス
* @var array
*/
protected $pokebox = [];
/**
* 選択中のボックス番号
* @var integer|null
*/
protected $selected = null;
/**
* @return void
*/
public function __construct()
{
//
}
/**
* 全ボックスの取得(暗号化状態で返却)
* @return array
*/
public function getPokebox(): array
{
return $this->pokebox;
}
/**
* ボックスが選択状態になっているかの確認
* @return boolean
*/
public function isSelected(): bool
{
if(is_null($this->selected)){
return false;
}else{
return true;
}
}
}
プロパティとして用意したのは、ボックスを管理するためのpokeboxと、現在選択中のボックスを判定するためのselectedの2つです。
メソッドには、ボックス内の情報をそのまま返却するgetPokeboxと、現在ボックスが選択された状態になっているかを確認するためのisSelectedの2つです。
起動
ポケボックスのクラスの役割についてですが、パソコンをイメージしてみるとわかりやすいでしょう。通常時は余計な処理を省くためにも停止状態としておき、必要な際に起動します。
起動処理についてですが、ボックスを選択する(selectedプロパティへ値をセット)することが、起動状態かどうかの判定となります。なので、こちらをセットするためのメソッドをトレイトを作成して追加しましょう。
ポケボックスのボックス操作トレイト(/App/Traits/Class/Pokebox/ClassPokeboxTrait.php)
<?php
trait ClassPokeBoxBoxTrait
{
/**
* ボックスを選択状態にする(起動)
* @param box_number:integer
* @return boolean
*/
public function selectBox(int $box_number): bool
{
if(isset($this->pokebox[$box_number])){
$this->selected = $box_number;
return true;
}else{
return false;
}
}
ボックス自体は配列による添字管理のため、引数ではボックス番号を受け取ります。もしそそのボックスが存在していれば、selectedプロパティに値をセットしてtrueを返却、見つからなければfalseを返却します。
単純ですが、これが擬似的な起動処理となります。
終了
次にボックスの終了処理についてです。ボックス操作が終われば、原則としてボックスの選択状態は解除します。あくまで「起動」→「操作」→「終了」という一連の流れをルール付しておくことが目的の1つです。ボックス操作では、ポケモンオブジェクトの移動が伴うため、些細なエラーでもデータの消失に繋がります。それを回避するためにも、手順はある程度ルール化しています。
/**
* ボックスの選択を解除する
* @return void
*/
public function shutdown(): void
{
$this->selected = null;
}
終了用のメソッド自体は単純で、selectedのプロパティに対してnullをセットするだけです。これで、起動・終了の基本的な処理が準備できました。
ボックスへのアクセス
次に、ボックスへのアクセス方法について見ていきましょう。まずはボックスを追加するためのメソッドです。
/**
* ボックスの追加
* @return integer
*/
protected function addBox(): int
{
$this->pokebox[] = [];
// 追加したボックス番号を返却
return array_key_last($this->pokebox);
}
pokeboxのプロパティの最後に、新しい空の配列を追加しています。このとき、追加したボックスの番号をそのまま使用する可能性があるので、返り値ではpokeboxの最終キーを返却しています。
ボックスへのアクセスで最初に行われる処理は「ポケモンを預ける」です。なので、まずはボックスにポケモンを格納するためのメソッドを作成しましょう。
ポケボックスのポケモン管理用トレイト(/App/Traits/Class/Pokebox/ClassPokeboxPokemonTrait.php)
<?php
trait ClassPokeboxPokemonTrait
{
/**
* ポケモンの追加
* @param pokemon:object::Pokemon
* @return boolean
*/
public function addPokemon(object $pokemon): bool
{
// 引数の値を確認(ポケモンクラス、立場の確認)
if(
!is_a($pokemon, 'Pokemon') ||
$pokemon->getPosition() !== 'friend'
){
return false;
}
// ボックス番号の取得
if(is_null($this->selected)){
// 空きボックスの取得
$free_box = array_fileter($this->pokebox, function($box){
return count($box) < config('pokebox.max');
});
if(empty($free_box)){
// ボックスに空きが無ければ新しくボックスを追加
$box_number = $this->addBox();
}else{
// 空きボックスの先頭を取得
$box_number = array_key_first($free_box);
}
}else{
// 選択中のボックスに空きがあるかの確認
if($this->pokebox[$this->selected] >= config('pokebox.max')){
return false;
}
$box_number = $this->selected;
}
// 格納前に全回復させる
$pokemon->recovery();
// シリアライズして配列で格納(負荷軽減)
$this->pokebox[$box_number][] = [
'class' => get_class($pokemon),
'name' => $pokemon->getNickname(),
'level' => $pokemon->getLevel(),
'object' => serializeObject($pokemon),
];
// 格納完了
return true;
}
}
ポケモンを預ける際には比較的厳重なチェックを入れました。まずポケモンクラスであるかどうかの確認、ポケモンの立場の確認を行い、格納する値自体が正常なものかどうかを判断しています。
次に、ボックスの起動状態を確認して、起動状態であればその対象ボックスへの格納、そうでなければ最初の空きボックスを判定して格納します。
ボックスが起動状態でない場合の預け入れは、基本的に野生のポケモンを捕まえてパーティーへ追加できなかった際の処理です。ポケボックスへアクセスして預ける際には、現ソックとしてボックスを選択してからの預け入れとなります。
上記の条件がクリアできれば、ポケモンの体力を全回復させて、オブジェクトをシリアライズしてから配列として格納しました。アンシリアライズしなくても、一覧で最低情報が確認できるように、ポケモンクラス名、名前、レベルにはそれぞれキーを割り振って格納しています。
格納する形式が固まったので、次は預けたポケモンへアクセスするためのメソッドを用意しましょう。
/**
* 選択中のボックスへアクセス(復号化して返却)
* @return array
*/
public function accessBox(): array
{
return array_map(function($pokemon){
// オブジェクトを復号化して返却
return unserializeObject($pokemon['object']);
}, $this->pokebox[$this->selected] ?? []);
}
ポケモンの格納とは逆に、array_mapを使ってアンシリアライズ化して配列で返却しています。ボックスへのアクセスは、外部からの侵入を制限するためにもselectedプロパティのボックスに対してarray_mapをかけるようにしています。
以上で、ボックス操作に必要な基本的なメソッドが揃いました。
グローバルによる管理
ボックス自体、毎画面移管でアンシリアライズする必要がありません。ボックスクラスが必要なタイミングは、ボックス操作中またはポケモンを捕まえて手持ちがいっぱいの状態のみです。
なので、ボックスクラス自体のシリアライズ化はグローバル関数により必要な画面でのみ呼び出すようにしておきましょう。
<?php
$global_pokebox = $_SESSION['__pokebox'] ?? null;
// クラス読み込み
require_once($root_path.'/Classes/Pokebox.php');
/**
* ボックスの取得
* @return object::Pokebox
*/
function pokebox()
{
global $global_pokebox;
return $global_pokebox;
}
/**
* ボックスの初期化
* @return void
*/
function initPokebox()
{
global $global_pokebox;
$_SESSION['__pokebox'] = serializeObject(new Pokebox);
$global_pokebox = $_SESSION['__pokebox'];
}
/**
* ボックスの起動
* @return void
*/
function startPokebox()
{
global $global_pokebox;
if($global_pokebox){
// 復号化(オブジェクト状態でなければ)
if(!is_object($global_pokebox)){
$global_pokebox = unserializeObject($global_pokebox);
}
}else{
// 初期化されていなければ新規作成
$global_pokebox = new Pokebox;
}
}
/**
* ボックスの終了
* @return void
*/
function shutdownPokebox()
{
global $global_pokebox;
// ボックスをシャットダウン状態にする
if(is_object($global_pokebox)){
$global_pokebox->shutdown();
}else{
$global_pokebox = unserializeObject($global_pokebox)->shutdown();
}
// 暗号化して格納
$_SESSION['__pokebox'] = serializeObject($global_pokebox);
}
/**
* ボックスの保存
* @return void
*/
function savePokebox()
{
global $global_pokebox;
// 暗号化して格納
$_SESSION['__pokebox'] = serializeObject($global_pokebox);
}
毎画面で行う処理は、セッション内のポケボックスをグローバル変数へ格納するだけです。ボックス操作が必要になる際はstartPokeboxでクラスそのものをアンシリアライズ、終了時にはクラスの状態をセッションに保存してからポケボックスのクラスをシリアライズします(shutdownPokebox)。
ポケモンの移管などを行えばボックス終了をせずに保存する必要があるので、保存単体の処理用の関数(savePokebox)も用意しています。
これで、ポケモン預かりシステムの根幹処理が揃いました。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは、ポケモン預かりシステムにおけるボックスの作成方法についてご紹介しました。
ポケモンに興味がある方、プログラミング学習に取り組んでいる方は、ぜひ参考にしてみてくださいね。