前回せっかくBootstrapを使って見た目を整えたにも関わらず、ビューポートの記述が抜けているという凡ミスが発覚したので修正しています。
サーセン。
今回のPHPポケモンでは本格的な技システムを実装していきます。技システムが整えば、皆さん期待のバトルシステムも間近です。セキュリティ面やファイル構成なども少しずつ見直し、学習しながら楽しく進めていきましょう。
本格的な技システムの実装
今まで技は文字列としてしか扱っていませんでしたが、技1つをとっても膨大な情報量です。
でんきショック(ポケモンwiki)
なので、技もポケモンと同様にクラスで用意して、呼び出す際は同様にインスタンス化して使用するという方式をとります。
賢い人なら既にお気づきかも知れませんが、PHPポケモンではデータベースを使用していないため、データベースに格納すべきものが全て子クラスとして準備することになります。
技クラスの作成
PHPポケモンの技システムでは、ポケモンwikiを参考にしながら第1世代で用意されていた内容を少しカスタムして反映させます。
- タイプ
- 分類
- 威力
- 命中率
- PP
- 優先度
- 効果
-
分類(物理or特殊)については、第3世代まではタイプに対して割り振られていましたが、こちらは技に対して用意します。
-
命中率に関しても必中技(スピードスター等)は第2世代以降同様に数値設定無し(null)を用います。
-
PPや威力、効果等の補正に関しても、執筆時現在の最新の値を参考にする予定です。
子クラス
まずは、ピカチュウの技からクラス化していきましょう。例としてでんきショックのクラスを作成します。
でんきショック(/Classes/Move/ThunderShock.php)
<?php
require_once(__DIR__.'/../Move.php');
// でんきショック
class ThunderShock extends Move
{
/**
* 正式名称
* @var string
*/
protected $name = 'でんきショック';
/**
* 説明文
* @var string
*/
protected $description = '10%の確率で相手をまひ状態にする。';
/**
* タイプ
* @var string
*/
protected $type = 'Electric';
/**
* 分類
* @var string(physical:物理|special:特殊|status:変化)
*/
protected $species = 'special';
/**
* 威力
* @var integer
*/
protected $power = 40;
/**
* 命中率
* @var integer
*/
protected $accuracy = 100;
/**
* 使用回数
* @var integer
*/
protected $pp = 30;
/**
* 優先度
* @var integer
*/
protected $priority = 0;
}
※親クラスであるMove.phpはまだ作成していませんが、作成する想定で継承させています。
でんきショップのタイプは「でんき」ですが、こちらは英語表記(頭文字大文字)で設定しています。これは、タイプもクラスとして用意しなければならないことを想定していることが理由です。
また、分類についても英語表記にしています。クラスとして用意する必要はなくても、判定をする際にプログラム内で使用する可能性が高いので、想定して英語表記を採用しました。
技効果については、説明文にそのままの文章を用意しています。技の説明文は別にありますが、こちらはわかりやすさ重視で効果詳細を採用しました。また、効果についてはプロパティではなくメソッドで準備することが想定されるため、バトルシステムを作成する際に合わせて作り込んで行く予定です。
なきごえ(/Classes/Move/Growl.php)
<?php
require_once(__DIR__.'/../Move.php');
// なきごえ
class Growl extends Move
{
/**
* 正式名称
* @var string
*/
protected $name = 'なきごえ';
/**
* 説明文
* @var string
*/
protected $description = '相手のこうげきを一段階下げる。';
/**
* タイプ
* @var string
*/
protected $type = 'Normal';
/**
* 分類
* @var string(physical:物理|special:特殊|status:変化)
*/
protected $species = 'status';
/**
* 威力
* @var integer
*/
protected $power = null;
/**
* 命中率
* @var integer
*/
protected $accuracy = 100;
/**
* 使用回数
* @var integer
*/
protected $pp = 40;
/**
* 優先度
* @var integer
*/
protected $priority = 0;
}
変化技など威力がないものについては、nullを設定しています。0としても構いませんが、鳴き声は原則としてダメージを与えることがないため、今回はnullで判定します。スピードスターの命中率も同様です。
※プロパティの=nullは省略してもらって構いません
親クラス
次に技の親クラスとしてMoveを用意します。このクラスもインスタンス化する必要はないので、abstractを宣言しておきましょう。
技のクラス(/Classes/Move/php)
<?php
// 技
abstract class Move
{
/**
* インスタンス作成時に実行される処理
*
* @return void
*/
public function __construct()
{
//
}
/**
* 名称の取得
*
* @var integer
*/
public function getName()
{
return $this->name;
}
/**
* 説明文の取得
*
* @var integer
*/
public function getDescription()
{
return $this->description;
}
/**
* タイプの取得
*
* @var integer
*/
public function getType()
{
return $this->type;
}
/**
* 分類の取得
*
* @var integer
*/
public function getSpecies()
{
return $this->species;
}
/**
* 命中率の取得
*
* @var integer
*/
public function getAccuracy()
{
return $this->accuracy;
}
/**
* 使用回数の取得
*
* @var integer
*/
public function getPp()
{
return $this->pp;
}
/**
* 優先度の取得
*
* @var integer
*/
public function getPriority()
{
return $this->priority;
}
}
技に設定されているプロパティを返却するようのgetメソッドをそれぞれ記述しています。それぞれ公開情報なので、アクセス修飾子はpublicで構いません。
一括読み込み
技を使用するためには、ポケモン(Pokemon)のクラス、またはピカチュウなどそれぞれで技クラスを読み込まなければなりません。ですが、それぞれに対する記述量が膨大になってしまうため、一括読み込みができるように、技のrequire_onceをまとめたファイルを用意します。
技の一括読み込み用ファイル(/Include/MoveInclude.php)
<?php
// 技クラスの読み込み
require_once(__DIR__.'/../Classes/Move/ThunderShock.php');
require_once(__DIR__.'/../Classes/Move/Growl.php');
require_once(__DIR__.'/../Classes/Move/ThunderWave.php');
require_once(__DIR__.'/../Classes/Move/QuickAttack.php');
require_once(__DIR__.'/../Classes/Move/Swift.php');
require_once(__DIR__.'/../Classes/Move/Agility.php');
require_once(__DIR__.'/../Classes/Move/Thunder.php');
ポケモンクラス(/Classes/Pokemon.php)
<?php
require_once(__DIR__.'/../Traits/ResponseTrait.php');
require_once(__DIR__.'/../Traits/Pokemon/SetTrait.php');
require_once(__DIR__.'/../Traits/Pokemon/GetTrait.php');
require_once(__DIR__.'/../Include/MoveInclude.php');
// ポケモン
abstract class Pokemon
注意事項
今回採用した「使用する予定のあるファイルを全て読み込む」という方法は好ましいとは言えません。本来、必要のないファイルは読むこむべきではないからです。気になる人はオートロード(spl_autoload_register)を使って読み込むか、Composerを活用してください。現状はファイル量も多くないため、簡易的にこの方法で進めていきます。
技関係のメソッドを修正
次に、今まで使っていたメソッドの技関係の部分を修正していきます。まずはGet関係の格納メソッドからです。
Get関係格納メソッド(/Traits/Pokemon/GetTrait.php)
<?php
trait GetTrait
{
/**
* 詳細を取得する
* @return integer
*/
public function getDetails()
{
return [
'正式名称' => $this->getName(),
'ニックネーム' => $this->getNickName(),
'現在のレベル' => $this->getLevel(),
'現在の経験値' => $this->getExp(),
'次のレベルまでに必要な経験値' => $this->getReqLevelUpExp(),
];
}
--省略
/**
* 覚えている技の一覧を取得する
* @return array
*/
public function getMove()
{
/**
* 技のインスタンス化関数
* @param string $class_name
* @return object
*/
function getMoveInstance($class_name){
if(class_exists($class_name)){
return new $class_name();
}
}
// array_mapで配列内の技クラスをインスタンス化
return array_map('getMoveInstance', $this->move);
}
getDetailsのメソッドで技一覧を返却していましたが、こちらは削除しておきましょう。
そして、技の一覧を取得していたgetMoveのメソッドで、技をインスタンス化して返却するという仕様に変更しています。
/**
* 覚えている技の一覧を取得する
* @return array
*/
public function getMove()
{
/**
* 技のインスタンス化関数
* @param string $class_name
* @return object
*/
function getMoveInstance($class_name){
if(class_exists($class_name)){
return new $class_name();
}
}
// array_mapで配列内の技クラスをインスタンス化
return array_map('getMoveInstance', $this->move);
}
ポケモンクラスに用意されたmoveプロパティ内の要素をクラス名で想定して、array_mapを使いそれぞれの要素をインスタンス化して返却しています。
これに合わせて、それぞれのポケモンに設定された技をクラス名に変更しましょう。
ピカチュウ(/Classes/Pokemon/Pikachu.php.)
/**
* レベルアップで覚える技
* @var array[習得レベル(integer), 技名称(class_name)]
*/
protected $level_move = [
[1, 'ThunderShock'],
[1, 'Growl'],
[9, 'ThunderWave'],
[16, 'QuickAttack'],
[26, 'Swift'],
[33, 'Agility'],
[43, 'Thunder'],
];
ポケモンクラスの技習得用メソッド(checkMove)でも技を使っているので、こちら合わせて修正します。
ポケモンクラス(/Classes/Pokemon.php)
/**
* 現在のレベルで覚えられる技があるか確認する処理
*
* @var integer
*/
protected function checkMove()
{
// レベルアップして覚えられる技があれば習得する
$level_move_keys = array_keys(array_column($this->level_move, 0), $this->level);
foreach($level_move_keys as $key){
$move_class = $this->level_move[$key][1];
// 覚えようとしている技(クラス)が存在かつ重複していないか
if(class_exists($move_class) && !in_array($move_class, $this->move, true)){
// 技クラスをセット
$this->setMove($move_class);
// インスタンス化
$move = new $move_class();
$this->setMessage($move->getName().'を覚えた!', 'success');
}
}
}
出力画面の修正
最後に出力画面を整えます。詳細に表示していた技リストを削除したので、ステータスの横に技の一覧をテーブルで出力しましょう。
出力用ファイル(/index.php)
<div class="col-12 col-sm-6 col-md-4">
<?php # 詳細 ?>
<table class="table table-bordered table-hover">
<thead class="thead-light">
<tr>
<th scope="col" colspan="2">詳細</th>
</tr>
</thead>
<tbody>
<?php foreach($controller->pokemon->getDetails() as $key => $val): ?>
<tr>
<th scope="row" class="w-50"><?=$key?></th>
<td><?=$val?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="col-12 col-sm-6 col-md-4">
<?php # ステータス ?>
<table class="table table-bordered table-hover">
<thead class="thead-light">
<tr>
<th scope="col" colspan="2">ステータス</th>
</tr>
</thead>
<tbody>
<?php foreach($controller->pokemon->getStats() as $key => $val): ?>
<tr>
<th scope="row" class="w-50"><?=$key?></th>
<td><?=$val?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<div class="col-12 col-sm-6 col-md-4">
<?php # 覚えている技 ?>
<table class="table table-bordered table-hover">
<thead class="thead-light">
<tr>
<th scope="col">覚えている技</th>
<th scope="col">PP</th>
</tr>
</thead>
<tbody>
<?php foreach($controller->pokemon->getMove() as $move): ?>
<tr>
<th scope="row" class="w-75"><?=$move->getName()?></th>
<td><?=$move->getPp()?>/<?=$move->getPp()?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
今回は技名とPPだけを表示する仕様です。PPはゲームらしく残回数がわかるようにスラッシュ表記で表示させています。
せっかくなので、詳細とステータスもテーブルレイアウトに書き換えています。
それでは出力結果を確認してみましょう。
テーブルにすることで大分見やすくなりました。技も問題なく出力されていますね。
今回ピカチュウで実装しましたが、他のポケモンも同様に技名をクラス名に変更して、それぞれの技クラスを作成すれば同様に表示することができます。
まとめ
いかがだったでしょうか。
今回のPHPポケモンは「技クラスの実装」についてご紹介しました。
第1回から進めてきている人は、大分クラスの扱い方が分かってきたのではないでしょうか。
まだまだ開発としては初歩段階ですが、楽しく進めていくことで学習効果は必ず現れるので、興味がある人はぜひ挑戦してみてくださいね。