β版の実装に向けて
大型アップデートにより、ある程度機能改善や実装箇所も増えてきましたが、それと同時に次の段階への移行が本格的に見えてきました。それがβ版です。
PHPポケモンは2020年12月現在α版となっており、完全な試作段階のWEBアプリケーションです。セーブ機能はなく、セッションの有効期限や破棄により最初からやり直さなければいけなくなるという点が最大の難点となっており、β版の実現に要求されている項目でもあります。
では、そのセーブ機能を実現するためにもいくつか課題となっている項目を取り上げていきましょう。
セーブデータの軽量化
まずは、データを記録するためにはデータの軽量化が求められます。PHPポケモンではDBを使用せずにセッションによるデータの受け渡しをしているため、セッションに格納しているデータ自体を軽量化することが、この実現に繋がります。
そして、セッション容量の大半を占めているのはインスタンスです。
ポケモン
ポケモン情報はインスタンスをシリアライズして格納するという方式が現段階では最善なので、形式はこのままで進めていきます。
前回の記事で紹介したように、現在はほとんどの値をプロパティとして格納しているため、情報の追加やステータス・技の変更等によるアップデートも効きません。しかも、プロパティが多ければ多いほど、データ容量は膨れ上がるため、共通項目を定数または静的変数として、オブジェクト自体に持たせるといった仕様に変更します。
ピカチュウクラス(/Classes/Pokemon.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Pokemon.php');
/*
* ピカチュウ
*/
class Pikachu extends Pokemon
{
//=====================================
// オブジェクト定数
//=====================================
/**
* ポケモンナンバー
* @var integer
*/
public const NUMBER = 25;
/**
* 正式名称
* @var string
*/
public const NAME = 'ピカチュウ';
/**
* 分類
* @var string
*/
public const SPECIES = 'ねずみポケモン';
/**
* 分類
* @var string
*/
public const DESCRIPTION = 'つくる 電気が 強力な ピカチュウほど ほっぺの 袋は 軟らかく よく 伸びるぞ。';
/**
* タイプ
* @var array
*/
public const TYPES = ['TypeElectric'];
/**
* 基礎経験値
* @var integer
*/
public const BASE_EXP = 112;
/**
* 捕捉率
* @var integer
*/
public const CAPTURE = 190;
/**
* 重さ
* @var float
*/
public const WEIGHT = 6.0;
/**
* レベルアップ技
* @var array::[習得レベル:integer,技名称:string]
*/
public const LEVEL_MOVE = [
[1, 'MoveThunderShock'],
[1, 'MoveGrowl'],
[9, 'MoveThunderWave'],
[16, 'MoveQuickAttack'],
[26, 'MoveSwift'],
[33, 'MoveAgility'],
[43, 'MoveThunder'],
];
/**
* 種族値
* @var array
*/
public const BASE_STATS = [
'H' => 35,
'A' => 55,
'B' => 40,
'C' => 50,
'D' => 50,
'S' => 90,
];
/**
* 獲得努力値
* @var array
*/
public const REWARD_EV = [
'S' => 2,
];
//=====================================
// 静的変数
//=====================================
/**
* 進化先
* @var string
*/
public static $after_class = 'Raichu';
}
ポケモンそれぞれのクラスに記述するものは、定数・静的変数で解決ができました。これで、ピカチュウのインスタンスを作成しても、これらの情報は含まれず、呼び出す際はオブジェクトそのものを参照してくれることになります。
進化先を静的変数にしたのは、イーブイなどの進化先不定のポケモンがいるためです。こちらについては、前回の記事で細かくまとめているので、ぜひ参考にしてください。
アイテム
次にアイテムについてです。こちらはプレイヤー情報に含まれていますが、既にクラス名と所有数だけを格納した配列をプレイヤーに持たせることで、すでにある程度の軽量化はされています。
ただ、毎回アイテムを参照する際にインスタンス化するとなればメモリ負担は多くなってしまうため、こちらも定数を使って実装していきましょう。
モンスターボール(/Classes/Item/PokeBall.php)
<?php
$root_path = __DIR__.'/../..';
require_once($root_path.'/Classes/Item.php');
/**
* モンスターボール
*/
class ItemPokeBall extends Item
{
/**
* 正式名称
* @var string
*/
public const NAME = 'モンスターボール';
/**
* 説明文
* @var string
*/
public const DESCRIPTION = '野生のポケモンに投げて捕まえるためのボール。カプセル式になっている。';
/**
* カテゴリ
* @var string::general|health|ball|important|machine
*/
public const CATEGORY = 'ball';
/**
* 最大所有数
* @var integer
*/
public const MAX = 99;
/**
* 買値
* @var integer
*/
public const BID_PRICE = 200;
/**
* 売値
* @var integer
*/
public const SELL_PRICE = 100;
/**
* 対象
* @var string::friend|player|enemy
*/
public const TARGET = 'enemy';
/**
* 使用できるタイミング
* @var array
*/
public const TIMING = ['battle'];
/**
* 捕獲補正率
* @var float
*/
public const PERFORMANCE = 1.0;
}
これで、全ての値をインスタンス化せずに取り出すことができます。しかし、現状のままではメソッドを呼び出すためにはインスタンス化が必要です。こちらも解決するために、アイテムで使用しているメソッドはすべて静的メソッドへ変更しましょう。
どうぐクラス(/Classes/Item.php)
<?php
/**
* どうぐ
*/
abstract class Item
{
/**
* 性能値(捕獲補正率※ボールのみ個別で設定)
* @var float
*/
public const PERFORMANCE = 0;
/**
* 現在のページで使用できるかどうかの判定
* @param page:string
* @return boolean
*/
public static function allowUsed($page): bool
{
return in_array($page, static::TIMING, true);
}
/**
* 削除できるかどうかの判定
* @return boolean
*/
public static function allowTrashed(): bool
{
// 最大所有数が設定されていれば削除可
if(is_null(static::MAX)){
return false;
}
return true;
}
}
静的変数と同じく、静的メソッドもstaticを付与することでインスタンス化せずに呼び出すことができます。
これにより、アイテム自体は現状インスタンスが不要なオブジェクトとなりました。他のアイテムなども踏まえてインスタンス化の必要性がなさそうであれば、子クラス自体もabstractを付与してインスタンス化できないようにしても良さそうです。
処理の見直し
セーブデータの軽量化については、ある程度定まってきたので次に全体の処理見直しをしましょう。こちらは、現段階での予定となるので本格実装するかどうかは未定のものも含まれています。
クラスの抽象化
ポケモン・アイテムクラスでも説明しましたが、ほとんどのクラスは抽象化することで解決ができそうです。アイテムですら現状インスタンス化が不要な段階まで見えてきたので、おそらく他のクラスは抽象化が可能になります。
現在実装されているものであれば、以下のクラスが該当します。
- 技
- タイプ
- 状態異常
- 状態変化
- フィールド
以上の5クラスについてはほぼ抽象化が可能です。抽象化することにより、毎回インスタンスを作成していた手間が省けるため、現在の仕様と比較すればメモリ消費を大幅に削減できる見込みです。
こちらは、ほぼ確定で実装予定となります。
base64の画像処理
こちらは軽量化とは逆の内容になります。現在は公開ディレクトリに保管した画像へパスでアクセスをしています。画像自体はPHPポケモンオリジナルのものではないため、パスを指定することで他の画像へのアクセスができること自体はそこまで大きな問題ではありませんが、システム上大きな問題点が上がってきました。それがアクセスエラーです。
エラー等の関係上、一部レスポンスコードによる管理を行なっている処理があるため、もし404が返ってくるとレスポンスが上書きされてしまい、データ消失につながる可能性があります。セーブ機能が実装できれば、保険としてオートセーブを取り入れるなどして対応できるかも知れませんが、プレイの進行に支障が出ることは間違いありません。
なので、画像不定などのリスクを少しでも軽減するために、画像へのアクセスはPHPを使ってサーバー処理で行いbase64を生成、フロントでは生成されたデータを使うというのが候補として上がりました。
gifや画像数の関係上、処理速度にも不安が残るため、この方法が有効かどうかはわかりませんが、一部の画像についてはbase64で管理したほうが良さそうです。
TypeScriptの導入
処理自体はPHPで行なっているため、jsそのもののエラーによりデータが消失してしまうというケースはまずありません。ただ、画面入力などの操作機能全般はjsに頼っているため、エラーにより読み込みが上手く行かないなどが発生すれば、実質フリーズ状態に陥ってしまい強制リセットを余儀なくされます。
こういったリスクを軽減するためにも、TypeScriptを使ったjsファイルの作成・管理を視野に入れています。
ただ、jQueryとの共存やTypeScript自体を使ったことがないという現段階では悩みどころです。
もし導入するのであれば、記述量が少ないうちに判断したほうが良いのですが、一気に対応することで粗がでそうな気がしており躊躇しています。こちらはβ版では導入せず、γ版で本格的な画面の作り込みが入った時点でも良いかも知れません。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは「β版の実装へ向けたデータの軽量化」についてご紹介しました。
WEBアプリケーションづくりに興味がある方や、ゲーム好きな人は、ぜひ参考にしてみてくださいね。