プログラミング

PHPポケモン「バトルシステム編 〜バトル終了判定〜」28

JavaScript jQuery PHP PHPポケモン ポケモン
PHPポケモン「バトルシステム編 〜バトル終了判定〜」28

バトル終了判定

今回はバトル終了判定を実装しましょう。今までは「にげる」による戦闘離脱のみで、ひんし状態でも殴り合うことが出来たので、それを解消するためにも戦闘結果による判定を導入します。

 

ひんし状態の監視

まずは「ひんし」の監視です。現在は交代ポケモンどちらか一方がひんし状態になれば、その時点でバトルが終了します。なので、攻撃→ダメージ計算→ひんし判定の流れを1セットとして処理を行い、その結果がtrue(相手をひんし状態にした)であれば処理を終了するという流れを作成します。

 

バトルコントローラー(/Classes/Controller/BattleController.php
/**
* たたかう
*/
case 'fight':
// 自ポケモンの技をインスタンス化
$p_move = $this->getInstance($param);
// 行動順の判定
if($this->checkFirstMove($p_move, $e_move)){
    // 先行
    // 自ポケモンの攻撃
    $e_fainting = $this->actionFightToFainting($this->pokemon, $this->enemy, $p_move);
    if($e_fainting){
        // 相手をひんし状態にした
        break;
    }
    // 敵ポケモンの攻撃
    $p_fainting = $this->actionFightToFainting($this->enemy, $this->pokemon, $e_move);
    if($p_fainting){
        // 味方がひんし状態になった
        break;
    }
}else{
    // 後攻
    // 敵ポケモンの攻撃
    $p_fainting = $this->actionFightToFainting($this->enemy, $this->pokemon, $e_move);
    if($p_fainting){
        // 味方がひんし状態になった
        break;
    }
    // 自ポケモンの攻撃
    $e_fainting = $this->actionFightToFainting($this->pokemon, $this->enemy, $p_move);
    if($e_fainting){
        // 相手をひんし状態にした
        break;
    }
}
$this->setMessage('行動を選択してください');
break;

 

攻撃からの一連の流れをactionFightToFaintingというメソッドにまとめました。

 

バトルコントローラー(/Classes/Controller/BattleController.php
/**
* 攻撃→ダメージ計算→ひんし判定
*
* @param object $atk
* @param object $def
* @param object $move
* @return boolean
*/
private function actionFightToFainting($atk, $def, $move)
{
    // 攻撃処理
    $result['damage'] = $this->attack($atk, $def, $move);
    // ダメージ計算
    $def->calRemainingHp('sub', $result['damage']);
    // ひんしチェック
    if($this->checkFainting($def)){
        // 倒した
        return true;
    }else{
        // 倒せていない
        return false;
    }
}

 

引数に合わせて行動を行い、その結果をtruefalseで返却します。trueを受け取ればそのタイミングで処理を終了させるため、fightの分岐でbreakを行います。これで戦闘不能になったのに相手に攻撃をしてしまうという流れを回避することができます。

 

次にactionFightToFaintingメソッド内で呼び出しているcheckFaintingのメソッドについてです。こちらで防御側のポケモンがひんしかどうかを判定します。check関係のメソッドが多くなってきたので、まとめてトレイトに格納しましょう。

 

Check格納トレイト(/Traits/Battle/CheckTrait.php
<?php
// チェック関係格納トレイト
trait CheckTrait
{
 
    /**
    * にげる判定
    * F = (A × 128 / B) + 30 × C
    * Fを256で割った値 → 逃走成功率
    * @var A 味方ポケモンのすばやさ(ランク補正有り)
    * @var B 相手ポケモンのすばやさ(ランク補正無し)
    * @var C 逃走を試みた回数
    * @return boolean
    */
    protected function checkRun()
    {
        // 味方の素早さを取得(ランク補正有り)
        $a = $this->pokemon
        ->getStats('Speed', true);
        // 相手の素早さを取得(ランク補正無し)
        $b = $this->enemy
        ->getStats('Speed');
        // 逃走を試みた回数
        $c = $this->run;
        // 計算式への当てはめ
        $f = ($a * 128 / $b) + 30 * $c;
        // 確率計算
        if(round($f / 256, 2) * 100 >= mt_rand(0, 100)){
            return true;    # 逃走成功
        }else{
            return false;   # 逃走失敗
        }
    }
 
    /**
    * 先手の判定
    *
    * @param object 自ポケモンの技 $p_move
    * @param object 敵ポケモンの技 $e_move
    * @return boolean (pokemon > enemy):true (pokemon < enemy):false
    */
    protected function checkFirstMove($p_move, $e_move)
    {
        /**
        * 優先度の比較
        */
        // 判定
        if($p_move->getPriority() > $e_move->getPriority()){
            // 優先度が高い
            return true;
        }elseif($p_move->getPriority() < $e_move->getPriority()){
            // 優先度が低い
            return false;
        }
        /**
        * すばやさの比較
        */
        // 自ポケモンの素早さ(補正あり実数値)
        $p_speed = $this->pokemon
        ->getStats('Speed', true);
        // 敵ポケモンの素早さ(補正あり実数値)
        $e_speed = $this->enemy
        ->getStats('Speed', true);
        // 判定
        if($p_speed > $e_speed){
            // 素早さが上回っている
            return true;
        }elseif($p_speed < $e_speed){
            // 素早さが下回っている
            return false;
        }elseif($p_speed === $e_speed){
            // 同速(50%の乱数)
            if(random_int(0, 1)){
                return true;
            }else{
                return false;
            }
        }
    }
 
    /**
    * ひんし判定
    *
    * @param object $pokemon
    * @return boolean
    */
    protected function checkFainting($pokemon)
    {
        if($pokemon->getSa() === 'SaFainting'){
            // ひんし状態
            $this->setMessage($pokemon->getPrefixName().'は倒れた');
            if($pokemon->getPosition() === 'friend'){
                // 味方がひんし状態になった
                $this->setMessage('目の前が真っ暗になった');
            }else{
                // 相手をひんし状態にした
                // 経験値取得
                $this->setMessage('経験値をゲットした');
            }
            // バトル終了判定用メッセージの格納
            $this->setMessage(' ', 'battle-end');
            return true;
        }else{
            // ひんし状態ではない
            return false;
        }
    }

}

 

それではひんし判定の処理について見ていきましょう。

/**
* ひんし判定
*
* @param object $pokemon
* @return boolean
*/
protected function checkFainting($pokemon)
{
    if($pokemon->getSa() === 'SaFainting'){
        // ひんし状態
        $this->setMessage($pokemon->getPrefixName().'は倒れた');
        if($pokemon->getPosition() === 'friend'){
            // 味方がひんし状態になった
            $this->setMessage('目の前が真っ暗になった');
        }else{
            // 相手をひんし状態にした
            // 経験値取得
            $this->setMessage('経験値をゲットした');
        }
        // バトル終了判定用メッセージの格納
        $this->setMessage(' ', 'battle-end');
        return true;
    }else{
        // ひんし状態ではない
        return false;
    }
}

 

引数でチェックするポケモンを受け取り、その状態異常の状態からひんし状態かどうかを判別します。もし味方がひんし状態であれば「目の前が真っ暗になった」、相手がひんし状態であれば「経験値をゲットした」といったメッセージを返却しています。

※経験値計算処理は今回作成しません

 

どちらかがひんし状態になってすぐにバトル終了(画面移管)をするわけにはいきません。なので、バトルが終了したということをフロント側で受け取れるように空(空白が必要)メッセージにバトル終了を判定するためのパラメーターとしてbattle-endをセットして返却しておきます。

  

バトル終了処理

 バトル終了処理は、前回実装したjQueryのメッセージアクションを活用します。まずはメッセージの出力部分から修正していきましょう。

 

バトル画面(/battle.php
<div class="message-box border p-3 mb-3">
    <?php foreach($controller->getMessages() as $key => list($msg, $status)): ?>
        <?php $class = $key === $controller->getMessageFirstKey() ? 'active' : ''; ?>
        <?php $last_class = $key === $controller->getMessageLastKey() ? 'last-message' : ''; ?>
        <p class="result-message <?=$class?> <?=$last_class?> <?=$status ?? ''?>"><?=$msg?></p>
    <?php endforeach; ?>
    <span class="message-scroll-icon">▼</span>
</div>

 

ステータスをクラス名に書き出す仕様に変更しました。これでjQueryのでhasClassを使って判定することができます。前回idに格納していたlast-messageもクラスに変更しています。

※idにする必要性を感じなかったためです

  

リモートフォームの活用

bodyの最下部にjsでリモート操作する用の隠しフォームを用意しましょう。

<?php # 遠隔操作用隠しフォーム ?>
<form action="" method="post" id="remote-form">
    <input type="hidden" name="action" id="remote-form-action">
</form>

 

それではjsファイルにバトル終了判定を追記しましょう。

 

メッセージ用js/Resources/Assets/js/Battle/message.js
/**
* 画面読み込み時の関数
* @function ready
* @return void
**/
var startInit = function(){
    // 現在のメッセージ
    var now = $('.result-message.active');
    if((now.length === 0) || now.hasClass('last-message')){
        doLastMsg();
        return;
    }
    doNotLastMsg();
}
 
/**
* メッセージボックスクリック時の関数
* @function click
* @return void
**/
var clickMsgBoxInit = function(){
    $('.message-box').click(function(){
        // 現在のメッセージ
        var now = $('.result-message.active');
        // 最終メッセージかどうか確認
        if((now.length === 0) || now.hasClass('last-message')){
            doLastMsg();
            return;
        }
        // 現在のメッセージのactiveを解除
        now.removeClass('active');
        // 次のメッセージにactiveを付与
        var next = now.next();
        next.addClass('active');
        /**
        * メッセージのステータスに合わせた分岐
        **/
        // バトル終了
        if(next.hasClass('battle-end')){
            $('#remote-form-action').val('end');
            return $('#remote-form').submit();
        }
        // 最終メッセージ
        if(next.hasClass('last-message')){
            doLastMsg();
            return;
        }
        // どれにも該当しない
        doNotLastMsg();
    });
}

 

last-messageの判定をhasClassに変更し、その判定の前にバトル終了判定をするためbattle-endの分岐を追加しました。

もしbattle-endのクラスが付与されているメッセージに進んだら、先程作成したリモート操作用フォームのinput actionendをセットしてsubmit(送信)してくれるという仕組みです。

 

PHPポケモンという名称ですが、この辺りをPHPだけで再現するのは非常に手間なのでガッツリjsjQuery)だよりです。縛り開発はDBだけで十分です。

 

最後にendアクションの分岐をコントローラーに追加しましょう。バトル終了処理はポケモンのインスタンス化など一連の流れがすべて不要になるので、コンストラクタの最初に記述します。

 

バトルコントローラー(/Classes/Controller/BattleController.php
/**
* @return void
*/
public function __construct()
{
    // バトル終了
    if(isset($_POST['action']) && ($_POST['action'] === 'end')){
        $this->endBattle();
    }
    // 親コンストラクタの呼び出し
    parent::__construct();
    // 自ポケモンの格納
    $this->myPokemon($_SESSION['pokemon']);
    // 敵ポケモンの格納
    if(isset($_SESSION['enemy'])){
        $this->enemyPokemon($_SESSION['enemy']);
    }else{
        $this->enemyPokemon();
    }
    // ランク(バトルステータス)の引き継ぎ
    if(isset($_SESSION['rank'])){
        $this->pokemon
        ->setRank($_SESSION['rank']['pokemon']);
        $this->enemy
        ->setRank($_SESSION['rank']['enemy']);
    }
    // にげるの実行回数を引き継ぎ
    if(isset($_SESSION['run'])){
        $this->run = $_SESSION['run'];
    }
    // アクションが選択された
    if(isset($_POST['action'])){
        // アクションメソッドの実行
        $this->action(htmlspecialchars($_POST['action']), htmlspecialchars($_POST['param'] ?? null));
    }
 
}
 
-- 省略
 
/**
* バトル終了
*
* @return void
*/
private function endBattle()
{
    unset($_SESSION['enemy']);
    unset($_SESSION['rank']);
    unset($_SESSION['run']);
    header("Location: ./home.php", true, 307);
    exit;
}

 

ホームコントローラーにも、endアクションで移管してきた際の処理を追加しておきましょう。

ホームコントローラー(/Classes/Controller/HomeController.php
/**
* アクション
*
* @param string $action(method_name)
* @param mixed $param
* @return void
*/
private function action($action, $param)
{
    // リセットの処理
    if($action === 'reset' || is_null($this->pokemon)){
        header("Location: ./index.php", true, 307) ;
        exit;
    }
    // にげるの処理
    if($action === 'run'){
        $this->setMessage('上手く逃げ切れた', 'success');
        return;
    }
    // バトル終了
    if($action === 'end'){
        if($this->pokemon->getRemainingHp() <= 0){
            // ひんし状態ならHPを全回復させる
            $this->pokemon
            ->calRemainingHp('reset');
        }
        return;
    }
    // 呼び出せるメソッドか判別
    if(is_callable([$this->pokemon, $action])){
        // メソッド実行結果を$resultに格納
        $result = $this->pokemon
        ->$action($param);
        switch ($action) {
            // 経験値の取得
            case 'setExp':
            $this->pokemon = $result;
            break;
        }
        // Pokemonクラスに溜まったメッセージを取得
        $this->setMessage($this->pokemon->getMessages());
    }else{
        $this->setMessage('このアクションは使用できません', 'error');
    }
}

 

ひんし状態のままでは困るので、にげる判定で使用していた全回復処理を移し替えました。実際にバトルで動きを確認してみましょう。

 

 

 

 

 

無事バトルを終了することができました。あとは経験値の処理を作成し、特殊な技の判定を追加すればポケモンのバトルを成立させることができます。

  

まとめ

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

今回のPHPポケモンは「バトル終了判定」の実装方法についてご紹介しました。

これでバトル一連の流れが完成したことになります。大分遊べるレベルになったのではないでしょうか。

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

 

注目の記事

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

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

【Laravel7】バリデーションメッセージの日本語化【6系対応】
プログラミング
Laravel,Linux,PHP
【Laravel7】バリデーションメッセージの日本語化【6系対応】

  Laravelのバリデーションメッセージは標準だと英語で返ってきてしまいますね。 1つずつ変更する方法もありますが、言語ファイルを作成して一括変更するほうが開発時間の短縮に繋がります。   今回は「Laravel7のバリデーションメッセージを日本語化する方法」をご紹介します。Laravel6系でも同じ方法ででき...

YouTuber・ブロガー必見!知る人ぞ知るサムネイルの重要性とは
デザイン
Facebook,Instagram,Twitter,YouTuber,サムネイル,ブロガー
YouTuber・ブロガー必見!知る人ぞ知るサムネイルの重要性とは

  サムネイルって本当に重要? ブログの場合はフリー画像でもいいんじゃない?   そう考えている人はいませんか? 残念ですが、それは大きな間違いです。サムネイルを作り込むことは非常に重要であり、集客ポイントを拡大させるのはもちろん、ブランディングにもつながるのです。   今回は「知る人ぞ...

ひとのものをとったらどろぼう!編 PHPポケモン 99
プログラミング
PHP,PHPポケモン,ポケモン
ひとのものをとったらどろぼう!編 PHPポケモン 99

ひとの ものを とったら どろぼう! ポケモンの中でも有名なセリフの1つです。モンスターボールを投げることでポケモンを捕まえることができますが、既に別トレーナーが所有しているポケモンを捕まえることはできません。 初代から、トレーナー戦でモンスターボールを投げると「ひとの ものを とったら どろぼう!...

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

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

モンスターボール作成編 PHPポケモン 79
プログラミング
PHP,PHPポケモン,ポケモン
モンスターボール作成編 PHPポケモン 79

モンスターボールとは ポケモンのゲームにとっての楽しみの1つである「バトル」はある程度実装できてきましたが、もう一つ無くてはならない楽しみがあります。それが「ポケモンを集める」というコレクター要素です。 そして、ポケモンを捕まえるために欠かせない道具の1つが「モンスターボール」です。  モンス...

連続の技習得編 オブジェクトをセッションへ格納 PHPポケモン57
プログラミング
PHP,PHPポケモン,ポケモン
連続の技習得編 オブジェクトをセッションへ格納 PHPポケモン57

セッション経由でのオブジェクト引き継ぎ 技習得の処理が整ってきたので、ここで連続技習得・連続レベルアップ時にも問題なく動作するように作り込んでいきます。ですが、現状のモーダルをレスポンスやメッセージと同様に、そのまま引き継いだとしてもエラーが発生します。 その原因がセッション経由でのオブジェク...

【PHP】可変長引数とは「点(ドット)3つ」 多次元連想配列の検索
プログラミング
PHP
【PHP】可変長引数とは「点(ドット)3つ」 多次元連想配列の検索

可変長引数とは  引数の数が決まっていない、状況に応じて複数の引数を指定したいときに、関数の引数で指定する点(ドット)3つのことです。PHP5.6以降で使用することができます。  PHPマニュアル 可変長引数リスト https://www.php.net/manual/ja/functions.arguments.php#functions.variable-arg-list ...

カテゴリ

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