プログラミング

PHPポケモン「バトルシステム編〜状態異常2〜」31

PHP PHPポケモン ポケモン
PHPポケモン「バトルシステム編〜状態異常2〜」31

 

前回に引き続き、状態異常チェックを実装します。

まず、前回実装した「ねむり」の処理についてですが、やはりターン数をセットして経過ターン数を引いていくという処理の方が解除率もゲーム再現になるので、まず修正をしておきます。サーセン。

 

チェック格納トレイト(/Traits/Battle/CheckTrait.php
/**
* アタック前の状態異常チェック
*
* @param object Pokemon
* @return boolean
*/
protected function checkBeforeSa($pokemon)
{
    if(empty($pokemon->getSa())){
        // 状態異常にかかっていない
        return true;
    }
    switch ($pokemon->getSa()) {
        /**
        * まひ
        */
        case 'SaParalysis':
        // 1/4の確率で行動不能
        $paralysis = new SaParalysis;
        if(random_int(1, 4) === 1){
            $this->setMessage($paralysis->getFalseMessage($pokemon->getPrefixName()));
            return false;
        }
        break;
        /**
        * こおり
        */
        case 'SaFreeze':
        // 1/5の確率でこおり解除
        $freeze = new SaFreeze;
        if(random_int(1, 5) === 1){
            // こおり解除
            $pokemon->releaseSa();
            $this->setMessage($freeze->getRecoveryMessage($pokemon->getPrefixName()));
        }else{
            // 行動不可
            $this->setMessage($freeze->getFalseMessage($pokemon->getPrefixName()));
            return false;
        }
        break;
        /**
        * ねむり
        */
        case 'SaSleep':
        // ターンカウントが残っていれば行動不能
        $sleep = new SaSleep;
        // ターンカウントを進める
        $pokemon->goSaTurn();
        if($pokemon->getSa('turn') <= 0){
            // ねむり解除
            $pokemon->releaseSa();
            $this->setMessage($sleep->getRecoveryMessage($pokemon->getPrefixName()));
        }else{
            // 行動失敗
            $this->setMessage($sleep->getFalseMessage($pokemon->getPrefixName()));
            return false;
        }
        break;
        /**
        * ひんし
        */
        case 'SaFainting':
        return false;
        break;
    }
    // 行動可
    return true;
}

 

その他若干修正をかけていますが、メインの修正は前述したように「ねむり状態」のターン数部分です。ねむり状態をセットする際に、2〜4のターン数を確定させて、チェック時に毎回減算してき0ターンになれば解除という方法です。

 

攻撃技である「ねむりごな」の追加効果を見てみましょう。

 

ねむりごな(/Classes/Move/SleepPowder.php
/**
* 追加効果
*
* @param array $args
* @return void
*/
public function effects(...$args)
{
    /**
    * @param Pokemon $atk 攻撃ポケモン
    * @param Pokemon $def 防御ポケモン
    */
    list($atk, $def) = $args;
    // 相手をねむり状態にする(2〜4ターン)
    $msg = $def->setSa('SaSleep', random_int(2, 4));
    // メッセージをセット
    $this->setMessage($msg);
}

 

おなじみrandom_intを使ってターン数をランダムでセットしています。これで、ねむるなどを使った際も同じ処理で判定することができます。

 

それでは今回のメインである状態異常チェックの後半部分に入りましょう。

 

状態異常チェック

行動前にチェックする状態異常は前回実装しましたね。今回は行動後に判定を行う状態異常です。該当するのは以下の3つです。

  1. どく
  2. もうどく
  3. やけど

 

それぞれ、ダメージを受けるといったものです。もちろん、これらのダメージによってひんしになる可能性もあるため、それぞれチェックを行なった後にダメージ計算、ひんし判定という流れも実装しなければなりません。

 

チェック格納トレイト(/Traits/Battle/CheckTrait.php
/**
* アタック後の状態異常チェック
*
* @param object Pokemon
* @return boolean (false:ひんし)
*/
protected function checkAfterSa($pokemon)
{
    if(empty($pokemon->getSa())){
        // 状態異常にかかっていない
        return true;
    }
    switch ($pokemon->getSa()) {
        /**
        * どく
        */
        case 'SaPoison':
        // 最大HPの1/8ダメージを受ける
        $poison = new SaPoison;
        // 小数点以下切り捨て
        $damage = (int)($pokemon->getStats('HP') / 8);
        if($damage){
            // 最小ダメージ数は1
            $damage = 1;
        }
        // メッセージ
        $this->setMessage($poison->getTurnMessage($pokemon->getPrefixName()));
        break;
        /**
        * もうどく
        */
        case 'SaBadPoison':
        // 最大HPの(ターン数/16)ダメージを受ける(最大15/16)
        $bad_poison = new SaBadPoison;
        // ターンカウントを進める
        $pokemon->goSaTurn();
        // 小数点以下切り捨て
        $damage = (int)($pokemon->getStats('HP') / 16) * $pokemon->getSa('turn');
        if($damage){
            // 最小ダメージ数は1
            $damage = 1;
        }
        // メッセージ
        $this->setMessage($bad_poison->getTurnMessage($pokemon->getPrefixName()));
        break;
        /**
        * やけど
        */
        case 'SaBurn':
        // 最大HPの1/16ダメージを受ける
        $burn = new SaBurn;
        // 小数点以下切り捨て
        $damage = (int)($pokemon->getStats('HP') / 16);
        if($damage){
            // 最小ダメージ数は1
            $damage = 1;
        }
        // メッセージ
        $this->setMessage($burn->getTurnMessage($pokemon->getPrefixName()));
        break;
    }
    // ダメージ計算
    $pokemon->calRemainingHp('sub', $damage ?? 0);
    // ひんしチェック(ひんしチェックとは逆の結果(boolean)を返却)
    return !$this->checkFainting($pokemon);
}

 

それでは状態異常について1つずつ見ていきましょう。

  

どく

 ターン終了ごとに、ダメージを受ける(第一世代では最大HP1/16、第二世代以降は最大HP1/8)

小数点は切り捨て。ただし、ダメージの最小は1 

 

毒のダメージは最大HPの1/8(切り捨て)、毎ターン受けることになります。

/**
* どく
*/
case 'SaPoison':
// 最大HPの1/8ダメージを受ける
$poison = new SaPoison;
// 小数点以下切り捨て
$damage = (int)($pokemon->getStats('HP') / 8);
if($damage){
    // 最小ダメージ数は1
    $damage = 1;
}
// メッセージ
$this->setMessage($poison->getTurnMessage($pokemon->getPrefixName()));
break;

 

 最小ダメージは1となるので、もし計算結果が0であれば1ダメージと判定しています。ダメージ計算からひんし判定の流れは残りの2つと共通処理になるため、switchから抜け出した後に記述します。

// ダメージ計算
$pokemon->calRemainingHp('sub', $damage ?? 0);
// ひんしチェック(ひんしチェックとは逆の結果(boolean)を返却)
return !$this->checkFainting($pokemon);

  

もうどく

 ターン終了ごとに、ダメージを受ける。受けるダメージは、ターンごとにHP1/162/16・・・と増える(最高15/16)

小数点は切り捨て。ただしダメージの最小は1

もうどく(ポケモンwiki

 

こちらも毒と同じように毎ターンダメージを受けるというものですが、経過ターンに合わせてダメージ量に変化があります。ここでターン数を使用します。

/**
* もうどく
*/
case 'SaBadPoison':
// 最大HPの(ターン数/16)ダメージを受ける(最大15/16)
$bad_poison = new SaBadPoison;
// ターンカウントを進める
$pokemon->goSaTurn();
// 小数点以下切り捨て
$damage = (int)($pokemon->getStats('HP') / 16) * $pokemon->getSa('turn');
if($damage){
    // 最小ダメージ数は1
    $damage = 1;
}
// メッセージ
$this->setMessage($bad_poison->getTurnMessage($pokemon->getPrefixName()));
break;

 

ねむり状態とは異なり、最初にターン数をデフォルト(0)でセットしておきます。毎ターンチェックを行う最初の段階でターンを進行させ、その数値を16で割った数にかけることでダメージ量とします。

 

ポケモンクラス(/Classes/Pokemon.php
/**
* ターンカウントをすすめる(状態異常)
*
* @return void
*/
public function goSaTurn()
{
    // 状態異常クラスを取得
    $sa = $this->getSa();
    switch ($sa) {
        /**
        * ねむり
        */
        case 'SaSleep':
        // 残ターン数を1マイナス
        $this->sa[$sa]--;
        break;
        /**
        * もうどく
        */
        case 'SaBadPoison':
        // 経過ターン数を1プラス(最大15)
        if($this->sa[$sa] <= 14){
            $this->sa[$sa]++;
        }else{
            $this->sa[$sa] = 15;
        }
        break;
    }
}

 

ターンカウントを進めるメソッド(goSaTurn)はポケモンクラスに作成しています。ねむりでは減算もうどくでは加算処理が必要になるため、分岐させてそれぞれに適した処理を割り当てています。もうどくの最大ダメージ量は15/16のため、ターン数の上限は15としています。

 

やけど

 ターン終了時に、HPが減少する(第一世代・第七世代以降では最大HP1/16、第二世代~第六世代は最大HP1/8)

小数点は切り捨て。ただしダメージの最小は1

第二世代まで、ダメージ発生のタイミングはターン終了時ではなく行動直後(#備考も参照)

やけど(ポケモンwiki

 

やけどのダメージは最新世代を参考に1/16のダメージとしています。こちらは毒ダメージの処理と同じで、ダメージ量だけが異なります。

/**
* やけど
*/
case 'SaBurn':
// 最大HPの1/16ダメージを受ける
$burn = new SaBurn;
// 小数点以下切り捨て
$damage = (int)($pokemon->getStats('HP') / 16);
if($damage){
    // 最小ダメージ数は1
    $damage = 1;
}
// メッセージ
$this->setMessage($burn->getTurnMessage($pokemon->getPrefixName()));
break;

 

これで毎ターンの状態異常チェック用のメソッドが完成しました。

 

行動順判定の変更

実際に状態異常のメソッドを呼び出す前に、行動順判定の算出方法を見直しておきましょう。

状態異常も行動順で呼び出す必要があり、その度に2つの順番分の処理を記述していては可視性も保守性も悪くなってしまいます。なので、ダブルバトル等をもし実装した場合でも活用できるように、行動順で配列に格納するというメソッドを作成し、foreachで順番に処理ができるようにしましょう。

 

バトルコントローラー(/Classes/Controller/BattleController.php
/**
* たたかう
*/
case 'fight':
// 自ポケモンの技をインスタンス化
$p_move = $this->getInstance($param);
// 行動順の判定
$order_array = $this->orderMove(
    [$this->pokemon, $this->enemy, $p_move],
    [$this->enemy, $this->pokemon, $e_move],
);
// 行動順にforeachでattackメソッドを実行
foreach($order_array as list($atk, $def, $move)){
    if($this->attack($atk, $def, $move)){
        // 相手をひんし状態にした
        break;
    }
}
// 行動順にforeachでcheckAfterSaとcheckAfterScを実行
foreach($order_array as list($atk, $def, $move)){
    if(!$this->checkAfterSa($atk)){
        // ひんし状態になった
        break;
    }
}

 

新しくorderMoveというメソッドを作成しました。これにポケモンの情報を配列として格納して、行動順に配列で受け取り($order_array)、foreachで処理をしています。

行動が終われば、再度foreachで順番に行動後の状態異常判定(checkAfterSa)を実行しています。

 

行動順判定用数値の算出

それでは新しく作成したorderMoveというメソッドの內部を見てみましょう。

 

バトルコントローラー(/Classes/Controller/BattleController.php
/**
* 行動順の判定
* (行動順判定用数値を生成)
* @var 100万の位:優先度
* @var 10〜10万の位:すばやさ
* @var 1の位:乱数
* @param array [攻撃ポケモン, 防御ポケモン, 攻撃ポケモンの技]
* @return array [行動順判定用数値 => [攻撃ポケモン, 防御ポケモン, 技],...]
*/
private function orderMove(...$pokemons)
{
    $results = [];
    foreach($pokemons as list($atk, $def, $move)){
        // 優先度のセット
        $speed = $move->getPriority() * 1000000;
        // 素早さ実数値の加算
        $speed += $atk->getStats('Speed', true) * 10;
        // 乱数の生成(同速判定用)
        $key = $speed + random_int(0, 9);
        // 重複回避
        while(isset($results[$key])){
            $key = $speed + random_int(0, 9);
        }
        // 配列へセット
        $results[$key] = [$atk, $def, $move];
    }
    // 降順(行動が早い順)に並び替え
    krsort($results);
    // [行動順判定用数値 => [ポケモン => 技],...] の多次元配列で返却
    return $results;
}

 

引数は可変長引数でポケモンの配列を更にまとめた配列として受け取っています。現在はダブルバトルの仕様は実装していないので不要ですが、今後柔軟に活用できるようにと作成しました。

 

今までは技の優先度が勝っていれば先行と単純に判定をしていましたが、これらをすべて数値化して判定を行います。

優先度 + 素早さ実数値 + 乱数

 

単純にこの計算をしてしまえば、正しい判定はできません。なので、優先度の高い順に上位の位を割り当てました。

 

すばやさには上限が設定されている。ランク補正・特性・もちもの・おいかぜの効果を受けてもすばやさは10000を超えることはない。8192以上になったすばやさは8192引かれ、すばやさ1809~8191のポケモンより行動順が遅くなる。

すばやさ(ポケモンwiki

 

ポケモンの素早さの最大値は10000を超えることが無いらしいので、そのまま参考にして100万の位を優先度、実数値を10から10万の位、そして乱数を1の位に割当てました。乱数は、同速だった場合の判定用として使用します。

// 優先度のセット
$speed = $move->getPriority() * 1000000;
// 素早さ実数値の加算
$speed += $atk->getStats('Speed', true) * 10;
// 乱数の生成(同速判定用)
$key = $speed + random_int(0, 9);
// 重複回避
while(isset($results[$key])){
    $key = $speed + random_int(0, 9);
}

 

素早さをそれぞれの値に割当てて計算、最後に乱数を割り当てて、それをキーにした配列を作成していきます。同速で乱数が同じとなれば上書きされてしまうので、重複回避用にwhileを使用しました。

 

あとは、キーの値で降順に並び替えをすれば、行動順に並び替えされた配列を返却することができます。

// 降順(行動が早い順)に並び替え
krsort($results);
// [行動順判定用数値 => [ポケモン => 技],...] の多次元配列で返却
return $results;

 

これで、より簡単に行動順でアクションを起こすことができるようになりました。出力結果を見てみましょう。

 

 

 

 

 

いろんな技を試さないとダメになったので、デバック用にミュウを作成しました。きっとゲームフリークスもこのために用意したんでしょう。調べていないので、その辺り気になった人は是非調べてみてください。

 

問題なく毒のダメージ判定が行われましたね。これで行動後の状態異常チェック処理は完成です。

 

まとめ

いかがだったでしょうか。

今回のPHPポケモンでは「行動後の状態異常の判定」についてご紹介しました。

ゲームづくりに興味がある人、現在プログラミング学習に取り組んでいる人は、ぜひ参考にしてみてくださいね。

 

注目の記事

有料ノートが公開停止!?プラットフォーム依存の危険性とは
雑記
note,YouTube,ブログ
有料ノートが公開停止!?プラットフォーム依存の危険性とは

  「いままでのnoteがいきなり公開停止になった」 「YouTubeであげていた人気動画が削除された」   規約変更、違反などを理由にこういった被害は年々増えています。自覚して悪意あるコンテンツを作っているユーザーであれば当然の結果だと言ってしまえば済むことですが、努力して収益化する糸口を見つけ...

【変数とは】初心者が最短でPHPを使えるようになるための実践的な学び方
プログラミング
HTML,PHP,プログラミング学習
【変数とは】初心者が最短でPHPを使えるようになるための実践的な学び方

  ※PHP初心者へ向けた内容となりますので、ある程度HTMLの知識がある方を対象とした内容になります。予めご了承ください。    Webプログラミングを学ぶ方はHTML、そしてCSSを学び、そしてJavascriptやPHPという順に学んでいくひとが多いでしょう。 私も実際に、似たような手順で学んでいきました。   ...

PHPポケモン「技クラス実装編」14
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「技クラス実装編」14

  前回せっかくBootstrapを使って見た目を整えたにも関わらず、ビューポートの記述が抜けているという凡ミスが発覚したので修正しています。 サーセン。   今回のPHPポケモンでは本格的な技システムを実装していきます。技システムが整えば、皆さん期待のバトルシステムも間近です。セキュリティ面やファイル構成...

戦闘不能による交代編 PHPポケモン84
プログラミング
PHP,PHPポケモン,ポケモン
戦闘不能による交代編 PHPポケモン84

戦闘不能による交代 ポケモンが戦闘不能になった際、もし手持ちに戦える状態のポケモンが残っていれば「交代」か「逃げる」の2択になります。今までは手持ちポケモンが1匹のみで判定を行っていたので、今回は交代の選択肢も含めて勝負の判定の見直しをしていきます。   パーティーを含めた勝負判定 味方または...

ポケモン図鑑編 PHPポケモン 88
プログラミング
PHP,PHPポケモン,ポケモン
ポケモン図鑑編 PHPポケモン 88

ポケモン図鑑とは ポケモンに出会ったり、仲間にしたりすると、ポケモン図鑑のデータがどんどんと埋まっていきます。PHPポケモンでもこの仕組みを実現させるために、ポケモン図鑑を作成していきましょう。   クラスによる管理 ポケモン図鑑はクラス管理をしていきます。プレイヤー1人に対して1つのポケモン図...

ピカチュウから学ぶオブジェクト指向 〜クラス継承編〜 2
プログラミング
PHP,PHPポケモン,オブジェクト指向,ポケモン
ピカチュウから学ぶオブジェクト指向 〜クラス継承編〜 2

  ピカチュウから学ぶオブジェクト指向の第2弾はオブジェクトの継承についてです。 前回作成したピカチュウクラスを使用するので、もし基礎的な内容を学習したい人は、以下の記事を参考にしてください。   オブジェクトの継承が理解できれば、複雑で規模の大きなシステムを構築することができるようになり...

TwitterでYouTubeのリンク付きサムネイルを表示させる方法【超実践的Webプログラミング活用法】
プログラミング
HTML,JavaScript,PHP,Twitter,YouTube
TwitterでYouTubeのリンク付きサムネイルを表示させる方法【超実践的Webプログラミング活用法】

  Twitter(ツイッター)をブログや商品、イベントの宣伝目的で使用している人は多いです。そして、そのためのマーケティング方法や戦略は数多く練られています。 今回は、その中でもYouTubeの告知をするために特化させた内容をまとめました。   一般的な方法と、プログラミングの知識(HTMLやJavascript等)があ...

HPバー色変更編(アニメーション) PHPポケモン53
プログラミング
PHP,PHPポケモン,ポケモン
HPバー色変更編(アニメーション) PHPポケモン53

HPバーの色変更 残りHPの割合に合わせて色クラスをセットしていましたが、ダメージを受けた際の判定処理(動的は変更)が未実装でしたので、今回はコチラを対応します。   残数(割合)の判定 HPの色判定は、HTMLの描画時とJSによるHP変動時に行います。animateメソッド前や後に行うと変更のズレが生じるため、a...

カテゴリ

SEO対策 イベント デザイン ネットワーク ビジネスモデル フリーランス プログラミング マーケティング ライティング 動画編集 雑記

タグ

5G Adobe AfterEffects AI ajax amazon Animate api artisan atom Automator AWS Bluetooth CSS CVR description EC-CUBE4 ECショップ ESLint Facebook feedly foreach function Google Google AdSense Honeycode htaccess HTML IEEE 802.11ax Illustrator Instagram IoT JavaScript jQuery jQuery UI keyword LAN Laravel Linux MacBook MAMP meta MLM MySQL NoCode note OS OSI参照モデル Paypal Photoshop PHP phpMyAdmin PHPポケモン PremierePro rss SEO SEO対策 Sequel Pro Skype SNS SSH Symfony TCP/IP title Toastr Trait Twig Twitter UCC V系 WAN WebSub Wi-Fi wiki Windows WordPress XAMPP xml Xserver YouTube YouTuber Zoom アーティスト アウトプット アクセス層 アニメーション アフィリエイト イーブイ インターネット インプット エンジニア オブジェクト指向 お金配り クリック単価 クリック数 コミュニケーション能力 コロナ コンサルティング サムネイル システムエンジニア スタートアップ スタイルシート スパム データベース ディープフェイク デザイナー デザイン テレワーク ナンパ ニュース ネットワークモデル ノマドワーク バナー ピカチュウ ビジネス フィード フリーランス ブロガー ブログ プログラマー プログラミング プログラミング学習 プログラミング教育 プロトコル ホームページ制作 ポケモン マークアップ マーケティング メール リモートワーク レンダリング 三井住友 三宮 仕事依頼 児童デイ 児童デイサービス 児童発達支援 公開鍵 初心者 助成金 勉強法 営業 広告 広告収入 必勝マニュアル 放課後等デイサービス 朝活 楽天 深層学習 無線LAN 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力