プログラミング

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ポケモンでは「行動後の状態異常の判定」についてご紹介しました。

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

 

注目の記事

PHPポケモン「バトルシステム編〜経験値の獲得〜」29
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「バトルシステム編〜経験値の獲得〜」29

経験値の獲得 今まではポケモンに直接経験値を与えるというチートびっくりの仕様でしたが、バトルシステムも終盤に差し掛かってきたので「倒したポケモンから経験値を取得する」というごく当たりまえの仕様を導入していきます。   基礎経験値の設定 では、経験値の計算式に入る前に、必要なパラメーターを1つ用...

AIが人類にもたらす驚異!深層学習が与える影響とは
ネットワーク
AI,ディープフェイク,深層学習
AIが人類にもたらす驚異!深層学習が与える影響とは

  海外の記事でAIについて記述した興味深いものが掲載されました。   今回は「AIが人類にもたらす驚異!深層学習が与える影響とは」について、上記サイトを参考にまとめましたのでご紹介します。 ※直訳ではありません、あくまで記事の内容を参考にして自らの考えを記述したものです     AIの驚異と...

PHPポケモン「バトルシステム編〜状態異常2〜」31
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「バトルシステム編〜状態異常2〜」31

  前回に引き続き、状態異常チェックを実装します。 まず、前回実装した「ねむり」の処理についてですが、やはりターン数をセットして経過ターン数を引いていくという処理の方が解除率もゲーム再現になるので、まず修正をしておきます。サーセン。   チェック格納トレイト(/Traits/Battle/CheckTrait.php) ...

グローバル&ヘルパー関数編 PHPポケモン 61
プログラミング
PHP,PHPポケモン,ポケモン
グローバル&ヘルパー関数編 PHPポケモン 61

進化や技習得、HPバーや経験値バーの演出ができているのに、なぜ状態異常の演出はされていないの?   そう感じている方が少なからずいるはずです。 現段階では、状態異常になっても次の画面に移管しなければ表示されません。これは、PHP側で内部処理は行われているが、メッセージに合わせた動的な変更がされていな...

壁技編(リフレクター・ひかりのかべ)PHPポケモン 51
プログラミング
PHP,PHPポケモン,ポケモン
壁技編(リフレクター・ひかりのかべ)PHPポケモン 51

壁技とは 前回実装した「しろいきり」と同じく、初代から実装されたフィールド効果技が存在します。それが「壁技」です。 壁(ポケモンwiki) https://wiki.ポケモン.com/wiki/壁 壁とは、バトルにおける場の状態の一種、およびその場を形成するわざの総称。わざとしては第一世代から存在するが、場の状...

保守っていくらで何をする?フリーランスWebエンジニア対象!保守業務について解説します
フリーランス
システムエンジニア,フリーランス,プログラマー
保守っていくらで何をする?フリーランスWebエンジニア対象!保守業務について解説します

  サイトの保守を頼まれたけど、具体的に何をやればいいの?   フリーランスの、特に独学やスクール上がりでなった人にとっては保守は何をすれば良いかわからないという悩みを抱えている人は多いのではないでしょうか。 システム開発会社や制作会社にいた人でも、どのぐらいの金額で請け負うのが良いか検...

かなしばり編 PHPポケモン 95
プログラミング
PHP,PHPポケモン,ポケモン
かなしばり編 PHPポケモン 95

かなしばりとは 最近は技のアップデートをおろそかにしていたので、久々の追加実装です。へんしんという再現が面倒な技は乗り越えましたが、他の技も仕様がややこしいため、覚えるポケモンが用意できたタイミングに基本的に増やしていきたいのですが、バトルシステムを作り上げていく関係上、どうしても見逃せない部...

【Laravel】1対1リレーションをわかりやすく解説(belongsTo)
プログラミング
Laravel,PHP
【Laravel】1対1リレーションをわかりやすく解説(belongsTo)

Laravelの1対1リレーションのbelongsToについて、公式マニュアルでは専用単語ばかりでどうしてもわかりにくいと感じてしまっている方へ向けて、わかりやすく解説しました。 ※例で紹介しているコードについては、一部英語を日本語表記で使用している部分もありますので、コピペで使用する方は必要に応じて置き換え...

カテゴリ

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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力