プログラミング

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

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

 

注目の記事

【Youも明日からデザイナー】デザインセンスを磨くコツ3つ
デザイン
YouTube,サムネイル,バナー
【Youも明日からデザイナー】デザインセンスを磨くコツ3つ

  デザイナーにセンスは必要か?   この問いに、私は必ず「必要ない」と答えます。確かに、センスを磨くことは大切ですが、それがあたかも才能のように表現するのは間違っていると思っているからです。 デザインは誰にでもできます。そしてオリジナリティは各自が必ず持っています。だからこそ、なりたい...

プログラミングとは?現役システムエンジニアが教える裏事情
プログラミング
システムエンジニア,プログラマー
プログラミングとは?現役システムエンジニアが教える裏事情

  プログラミングってよくわからない? 実際に何をやっているの?   最近注目されているSE(システムエンジニア)やPG(プログラマー)という職業ですが、「そもそもプログラミングとは?」といった疑問を抱えている方も多いですね。 今回は、そういった方へ向けて「プログラミングの基礎知識と裏事情」...

PHPポケモン「野生ポケモン遭遇編」18
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「野生ポケモン遭遇編」18

  PHPポケモンが第18回にしていよいよバトルの第一歩、野生ポケモンとの遭遇編に突入です。 新しいコントローラーの作成と、バトル画面の作成、そしてポケモンデータの受け渡しなどを中心にご紹介します。   バトル画面の実装  ポケモンのゲームでも、野生ポケモンが現れるとバトル画面へ移管し...

フィールド効果技編(しろいきり) PHPポケモン 50
プログラミング
PHP,PHPポケモン,ポケモン
フィールド効果技編(しろいきり) PHPポケモン 50

フィールド効果技とは ポケモンの技の中には、ポケモンに対して状態変化や異常を与えるもの以外に、フィールド自体に効果を持たせるものがいくつかあります。PHPポケモンでは未実装ですが、そういったフィールド効果技はポケモンを交代したとしても場に効果が残り続けます。  場の状態(ポケモンwiki) https:/...

レベルアップ時のステータス表示編 PHPポケモン 48
プログラミング
PHP,PHPポケモン,ポケモン
レベルアップ時のステータス表示編 PHPポケモン 48

ステータスの表示 前回経験値バーのアニメーションとレベルアップ時の動的な変更を実装しましたが、レベルアップ時に表示されるステータスの実装は先送りにしていました。なので今回はそのステータス表示を作成しましょう。   ステータスはメッセージとして返却せずに、小モーダルを起動させるという方法で対応しま...

PHPポケモン「アクション制御編」27
プログラミング
JavaScript,jQuery,PHP,PHPポケモン,ポケモン
PHPポケモン「アクション制御編」27

  今回のPHPポケモンでは主に画面の作り込みをしていきます。 とは言っても、ガッチリCSSを書いてよりゲームらしい見た目にするわけではなく、あくまで「ゲームシステムを再現するため」だけに整えていくのが目的です。   ということで、今回はPHPよりもBootstrapさんとjQueryさんに活躍してもらいます。   ...

ナンパしてたら独立できた「人間力の鍛え方」
フリーランス
コミュニケーション能力,ナンパ
ナンパしてたら独立できた「人間力の鍛え方」

  経験談から、人間力を鍛える方法をご紹介します。 今回は「ナンパ」がテーマです。なので、少し男性目線の内容になります。 女性の方は、「男性はこうやって考えている人もいるんだ」といった参考にしてください。   ナンパなんて、と思う人もいるでしょう。 ハラスメント規制も強くなる現代では、安易な...

PHPポケモン「バトルシステム実装編〜補正値計算・乱数・急所〜」21
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「バトルシステム実装編〜補正値計算・乱数・急所〜」21

バトルシステムの実装  今回は「急所」と「乱数」と「タイプ一致」の判定と補正を実装していきます。 ちなみにですが、ポケモンwikiを熟読したところ、補正値の計算にも順番があり、計算後に小数点の切り捨てや五捨五超入をするなど、そこそこ複雑な計算順序がありましたが、今回はそこまで精密に再現せず、補正値(...

カテゴリ

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