技ポイント(PP)とは
ポケモンではそれそれの技に使用回数が定められています。それが技ポイント(PP)と呼ばれているものです。
PP(ポケモンwiki)
技のクラスを実装した際に、それぞれにppというプロパティをもたせて回数をセットしています。これが、対象の技を使える基本最大回数です。もしすべての技の使用回数がなくなってしまうと、技を使うことができなくなるため前回実装した「わるあがき」が発動します。
PPを管理するためにも、現在ポケモンに対して格納している技(move)のプロパティの格納形式を、以下のような多次元連想配列に変更します。
ポケモンクラスの技プロパティ
$move = [
0 => [
'class' => 技クラス名(string),
'remaining' => 残り回数(integer),
'correction' => 補正値(integer),
],
];
classキーの値には、今まで値として格納していた技クラス名が入ります。では追加された残りの2つを見ていきましょう。
残り回数
remainingキーに割り当てられている値は、技の残り回数です。「でんきショック」であれば、基本最大使用回数が30回なので、全回復状態で1度技を使えば、残りは29回となります。この数を格納して置かなければ、あと何回使用できるかがチェックできませんよね。
技を習得した時点では、残り回数はその技の最大数が格納されます。でんきショックであれば、覚えた段階の残り回数は30回となります。
補正値
correctionキーに割り当てられている値は、技の最大回数に対する補正値です。ポイントアップなどのアイテムを使うと、技の上限回数を増やすことができます。
最大PPの増え方には3段階あり、1段階ごとに元のPPの1/5増え、最大まで上げれば元のPPの8/5(=1.6倍)となる
補正値の初期値は0です。現段階では補正値を増やす手段はありませんが、こちらも今後実装するものと考え用意しました。残り回数の算出も、補正値の概念が存在するものとして作成していきます。
それでは、ポケモンの技プロパティが変更したことによって修正しなければならない箇所を1つずつ対応していきましょう。まずは技クラスからです。
技クラス(/Classes/Move.php)
/**
* 使用回数の取得
*
* @param integer $correction 補正値
* @return integer
*/
public function getPp(int $correction=0)
{
return $this->pp + (int)(floor($this->pp / 5) * $correction);
}
使用回数の取得メソッド(getPp)に補正値の計算を追加しました。引数に補正値をセットすると、最大回数に補正値x20%数を加算して返却しています。ポケモンの技回数は5区切りのため、小数点以下の数値が出ることはありませんが、念の為切り捨て処理を行なっています。
次に技の習得処理を修正します。
Set格納トレイト(/Traits/Pokemon/SetTrait.php)
/**
* 初期技をセットする
* @return void
*/
protected function setDefaultMove()
{
foreach($this->level_move as list($level, $move)){
if($level <= $this->level){
// 現在レベル以下の技であれば習得
$this->setMove(new $move);
}else{
// 現在レベルを超えていれば処理終了
break;
}
}
}
/**
* 技を覚える
* @return object Move
*/
protected function setMove($move)
{
$this->move[] = [
'class' => get_class($move),
'remaining' => $move->getPp(),
'correction' => 0,
];
if(count($this->move) > 4){
// 技が4つを超過していれば、一番上を忘れさせる
unset($this->move[0]);
// 技の添字を採番する
$this->move = array_values($this->move);
}
}
ポケモンが持つmoveプロパティの構造が変更になったので、それに合わせた形式でセットするようにsetMoveメソッドを変更しました。引数は今までクラス名(string)で受け取っていましたが、オブジェクトで受け取る仕様に変更しました。remainingには先程作成したgetPpを引数無しで格納し、補正値であるcorrectionには0をセットしています。
setMoveはdefaultMoveメソッド内で呼び出しているので、引数にオブジェクトを渡せるように、newをつけておきましょう。
次に覚えている技を取得するメソッド(getMove)を修正します。
Get格納トレイト(/Traits/Pokemon/GetTrait.php)
/**
* 覚えている技の一覧を取得する
* @param integer $num
* @param string
* @return array
*/
public function getMove($num=null, $param='')
{
if(is_null($num)){
// 全返却
if($param === 'array'){
// 配列(加工不要)で返却
return $this->move;
}else{
// array_mapで配列内の技クラスをオブジェクト化して返却
return array_map(function($move){
// 無名関数
return [
'class' => new $move['class'],
'remaining' => $move['remaining'],
'correction' => $move['correction'],
];
}, $this->move);
}
}else{
// 番号指定で返却
$move = $this->move[$num] ?? $this->move[0];
if($param === 'array'){
// 配列(加工不要)で返却
return $move;
}else{
// オブジェクトにして返却
return new $move['class'];
}
}
}
最後に技の一覧出力方法を修正して、残りPPが確認できるようにしておきましょう。
ホーム画面詳細モーダル(Resources/Partials/Home/Modals/details.php)
<div class="tab-pane fade show" id="move" role="tabpanel" aria-labelledby="move-tab">
<?php # 覚えている技 ?>
<table class="table table-bordered table-sm table-hover">
<thead class="thead-light">
<tr>
<th scope="col">覚えている技</th>
<th scope="col">タイプ</th>
<th scope="col">PP</th>
</tr>
</thead>
<tbody>
<?php foreach($pokemon->getMove() as $move): ?>
<tr class="move-detail-link" data-target="#move_<?=get_class($move['class'])?>-content">
<th scope="row" class="w-50"><?=$move['class']->getName()?></th>
<td><?=$move['class']->getType()->getName()?></td>
<td><?=$move['remaining']?>/<?=$move['class']->getPp($move['correction'])?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php # 技説明 ?>
<div class="overflow-auto p-3 border" style="height:160px;">
<?php foreach($pokemon->getMove() as $key => $move): ?>
<div class="move-detail-content <?php if(array_key_first($pokemon->getMove()) == $key) echo 'active'; ?>" id="move_<?=get_class($move['class'])?>-content">
<h6><?=$move['class']->getName()?></h6>
<hr>
<p><?=$move['class']->getDescription()?></p>
</div>
<?php endforeach; ?>
</div>
</div>
初期状態では技ポイントが減っていないので、数値から判断することは出来ませんが、エラーが出ていないことが確認できればOKです。
詳細と同じように、バトル画面でのPP表示も残数が出力されるよう変更しておきましょう。
バトルシステムへの導入
それでは、バトルシステムにPPを導入していきます。通常、すべての技PPが0回であれば技選択ができず、「たたかう」を選んだ時点でわるあがきが発動しますが、まずは技PPが減る処理と、使用時に技ポイントが残っているかどうかを確認して、なければ「わるあがき」が発動するという処理を実装しましょう。
使用可不可の判定
まずは技の使用可不可の判定です。こちらを行動チェックの直後に追加しましょう。
攻撃用トレイト(/Traits/Battle/AttackTrait.php)
/**
* 攻撃
* (攻撃→ダメージ計算→ひんし判定)
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param object $move
* @return void
*/
protected function attack($atk_pokemon, $def_pokemon, $move)
{
// 行動チェック(状態異常・状態変化)
if(!$this->checkBeforeSa($atk_pokemon) || !$this->checkBeforeSc($atk_pokemon)){
// 行動失敗
return;
}
// わざの使用可不可判定
if(!$this->checkEnabledMove($move, $atk_pokemon)){
$this->setMessage($atk_pokemon->getPrefixName().'は出すことのできる技がない');
// わるあがきをセット
$move = new Struggle;
}
// チャージチェック
if($move->charge($atk_pokemon)){
// チャージターンならメッセージを格納して行動終了
$this->setMessage($atk_pokemon->getMessages());
$atk_pokemon->resetMessage();
return;
}
checkEnabledMoveというメソッドを使い、使える技かどうかを確認しています。ここで使えない技だと判定された場合は、わるあがきをセットして以降の処理を進めていきます。
では、新しく作成したcheckEnabledMoveメソッドの処理を見てみましょう。
チェック用トレイト(/Traits/Battle/CheckTrait.php)
/**
* 技の使用可不可判定
*
* @param object $move Move
* @param object $pokemon Pokemon
* @return boolean (true: 使用可, false:使用不可(わるあがき))
*/
protected function checkEnabledMove(object $move, object $pokemon)
{
$move_class = get_class($move);
if($move_class === 'Struggle'){
// わるあがき
return false;
}
// ポケモンの技一覧
$move_list = $pokemon->getMove(null, 'array');
// 選択された技の添番を取得
$num = array_search(
$move_class,
array_column($move_list, 'class'),
);
// PP残数の確認
if($move_list[$num]['remaining'] > 0){
// 使用可能
// チャージターンは残PPそのまま
if(!$this->checkChargeTurn($pokemon, $move)){
// 残PPをマイナス1
$pokemon->calRemainingPp('sub', 1, $num);
}
return true;
}else{
// 使用不可
return false;
}
}
/**
* チャージターンかどうかの確認
*
* @param object $pokemon
* @param object $move
* @return boolean
*/
protected function checkChargeTurn($pokemon, $move)
{
// チャージ技ではない
if(!$move->getChargeFlg()){
return false;
}
// 状態変化を取得
$sc = $pokemon->getSc();
// 現在未チャージ状態
if(!isset($sc['ScCharge'])){
// チャージターン
return true;
}
// 残チャージターン数が1超過
if($sc['ScCharge']['turn'] > 1){
// チャージターン
return true;
}
// チャージターンではない
return false;
}
最初に技が「わるあがき」かどうかをチェックしています。これは技選択がされなかった場合にわるあがきが選択された状態で処理に入るため、最初にチェックをして該当すればfalseを返却し、そのままわるあがきを実行してもらいます。
その後、技ポイントのチェックをして使用回数が残っていれば、残りPPをマイナス1して使用可(true)、もし残数が残っていなければ使用不可(false)を返却しています。ただし、チャージ技の場合、チャージターンに技ポイントが引かれてしまうと1回の使用でPPが2減少してしまうため、checkChargeTurnというメソッドを使ってこのターンがチャージターンかどうかを確認しています。
※多次元配列のキーを使った検索方法(array_searchとarray_columnの組み合わせ)は以下の記事にまとめているので参考にしてください
残りポイントの計算
次に、残りPPを変更するために使っているcalRemainingPpメソッドの処理を見ていきましょう。こちらは残りHPを取得する処理と同じように、引数を指定することで加算とリセットができるようにしています。
計算用トレイト(/Traits/Pokemon/CalculationTrait.php)
/**
* 残りPPの計算
*
* @param string $param
* @param integer $val (default:0)
* @param integer $num (default:null)
* @return integer
*/
public function calRemainingPp(string $param, int $val=0, $num=null)
{
switch ($param) {
// リセット処理
case 'reset':
if(is_null($num)){
// すべてのPPを全回復
foreach($this->getMove() as $key => $move){
$this->move[$key]['remaining'] = $move['class']->getPp($move['correction']);
}
}else{
// 指定された技PPを全回復
$this->move[$num]['remaining'] = $this->move[$num]
->getPp($this->move[$num]['correction']);
}
break;
// 減算処理
case 'sub':
if(is_null($num)){
// すべてのPPに減算処理
foreach($this->move as $key => $move){
$this->move[$key]['remaining'] -= $val;
if($this->move[$key]['remaining'] < 0){
// 最小値の処理
$this->move[$key]['remaining'] = 0;
}
}
}else{
// 指定された技PPに減算処理
$this->move[$num]['remaining'] -= $val;
if($this->move[$num]['remaining'] < 0){
// 最小値の処理
$this->move[$num]['remaining'] = 0;
}
}
break;
// 加算処理
case 'add':
if(is_null($num)){
// すべてのPPに加算処理
foreach($this->getMove() as $key => $move){
$this->move[$key]['remaining'] += $val;
if($this->move[$key]['remaining'] > $move['class']->getPp($move['correction'])){
// 最大値の処理
$this->move[$key]['remaining'] = $move['class']->getPp($move['correction']);
}
}
}else{
// 技をインスタンス化
$move = new $this->move[$num];
$this->move[$num]['remaining'] += $val;
if($this->move[$num]['remaining'] > $move->getPp($this->move[$num]['correction'])){
// 最大値の処理
$this->move[$num]['remaining'] = $move->getPp($this->move[$num]['correction']);
}
}
break;
}
}
リセットが選択された場合はすべての技PPを全回復(補正値を含めた最大PPをセット)、加算と減算では指定された値の計算結果をそれぞれセットしています。残りHPの計算処理とは異なり、どの技のPPを変更するかを指定できなければならないため、第3引数では技番号(添え番)を受け取っています。
分岐の関係上、記述量が多くなっていますが、処理自体は加算減算後に上限下限を確認しているという至って簡単な処理となっています。
それでは、実際に技を使って確認してみましょう。
戦闘終了後、使った技のPPが使用回数分減っていることが確認できました。これでPPシステムの実装は完了です。
まとめ
いかがだったでしょうか。
今回のPHPポケモンでは「技ポイント(PP)システムの導入方法」をご紹介しました。
PHPポケモンではDBを使用しないため、クラスのプロパティを活用していますが、DB構造などが変更になればそれだけ修正しなければならない箇所が多いということがわかりますね。それだけ開発では設計が重要なのです。
現在プログラミングを学習中の方は、ぜひ参考にしてみてくださいね。