プログラミング

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

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

 

注目の記事

【Wi-Fi6とは】スマホやPCの買い替えは必要?騙されないための基礎知識
ネットワーク
5G,IEEE 802.11ax,Wi-Fi
【Wi-Fi6とは】スマホやPCの買い替えは必要?騙されないための基礎知識

  本格的に5G普及への取り組みが始まり、Wi-Fi6といった新世代の規格が出たことによって、超情報化社会へ加速しました。 ですが、こういったナンバリングが一般化することによって、わからずに高性能な機器を斡旋されて無駄な購入をさせられたり、わからないことを理由に詐欺的営業を仕掛けてくる悪意ある人...

PHPポケモン「オートローダー編」16
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「オートローダー編」16

  今までは技やタイプを一括requireという荒業で対応していましたが、フシギダネ系列の技を実装した際に「こんなん全部読み込んでられるか!」と流石になったので、簡易ながらオートローダーを実装していきます。 そして、実装したらしたで色々と問題も浮かび上がってきたので、このあたりは回を進めて行きなが...

フレンドリィショップ編 アイテムの販売 PHPポケモン 76
プログラミング
PHP,PHPポケモン,ポケモン
フレンドリィショップ編 アイテムの販売 PHPポケモン 76

リュックの作成 前回はフレンドリィショップへ商品を並べ、計算機を作成するところまで作成しました。ですが、商品が購入できたとしても、それを保管しておくためのスペースがなければ意味がありません。 なので、プレイヤー情報に対してアイテムを格納できるように機能拡張をしましょう。   プレイヤークラス(/C...

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

  PHPを実践で使えるレベルまで押し上げるための初心者向け講座、第3回目は「関数」です。   第1回の変数、第2回の配列、そして今回の関数の3つをマスターできていれば、いざ業務に望んでも、ある程度通用すると思っておいて良いでしょう。  ※オブジェクト指向やクラスの理解、データベースの知識など、...

トレーナー戦編 バトルシステムへの追加 PHPポケモン 98
プログラミング
PHP,PHPポケモン,ポケモン
トレーナー戦編 バトルシステムへの追加 PHPポケモン 98

トレーナー戦闘の追加 前回トレーナー情報を作成したので、今回はその情報をバトルシステムへ組み込んでいきます。 現在、野生ポケモンとの戦闘では「battle」という値actionの値で受け取っています。同じサービス内で分岐を作ると複雑になってしまうので、battle_trainerという新しい分岐を使ってサービス分けをし...

動画編集に役立つ基本的な考え方【Adobe AfterEffects】
動画編集
Adobe,AfterEffects,PremierePro,YouTube
動画編集に役立つ基本的な考え方【Adobe AfterEffects】

  YouTubeの人気に合わせて、動画編集の需要も高まってきましたが、その大変さから挫折してしまう人も続出しています。 動画編集は奥が深く、技術的な部分に関してはプロのクリエイターであっても自分がよく使うような一部しか把握していないのが普通であり、調べても該当する情報が出てきにくいということもあ...

仕事用PCは2年で買い替えろ!その理由とは?
フリーランス
MacBook
仕事用PCは2年で買い替えろ!その理由とは?

  「あなたはパソコンをどのタイミングで買い替えますか?」   私は先日、2015年モデルのMacbookPro15インチから最新モデルのMacbookPro13インチに買い替えました。その理由は、メモリが32GBになったことや、15インチが持ち運びという点で見れば向いていないと判断したということ、そこそこの年数が経...

命中率補正と交代技編 PHPポケモン 45
プログラミング
PHP,PHPポケモン,ポケモン
命中率補正と交代技編 PHPポケモン 45

命中率補正とは ダメージ計算や行動順判定には補正(ランク)を計算した結果を反映していましたが、命中率補正についてはまだ未実装だったため、今回はこちらを作成していきたいと思います。 まずはwikiを参考に、計算方法を見ていきましょう。 命中(ポケモンwiki) https:// wiki.ポケモン.com/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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力