プログラミング

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ポケモンは「バトル終了判定」の実装方法についてご紹介しました。

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

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

 

注目の記事

「数字を上げる」必勝マニュアル 〜再生回数・フォロワー・PV数〜
マーケティング
YouTuber,ブロガー,必勝マニュアル,自己啓発
「数字を上げる」必勝マニュアル 〜再生回数・フォロワー・PV数〜

  「継続は力なり」   色んな場面で言われます。何事も地道な努力が大事です。 しかし、地道な努力というのは成果が見えづらく、反応が得にくいことも確かです。   運良く勢いに乗れて、常に努力のし易い環境にいることで伸びていく人の確かにいます。 しかし、ほとんどの人がそうはいきません。...

PHPポケモン「行動順判定+敵ポケモン攻撃編」25
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「行動順判定+敵ポケモン攻撃編」25

行動順の判定 ポケモンの行動順は以下の通りです。 技の優先度 すばやさの実数値(補正有り) 同速の場合は50%の乱数   この順番で比較を行い、先行後攻を決めます。ただし、これは両者ともに攻撃を選択した場合のみです。アイテムの使用や交代は技よりも優先されますし(※一部技を除く)、にげる...

プレイヤー情報作成編 PHPポケモン72
プログラミング
PHP,PHPポケモン,ポケモン
プレイヤー情報作成編 PHPポケモン72

プレイヤー情報の作成 少し前よりプレイヤーをホーム画面に表示させるようにしましたが、ただイメージとして配置しただけでプレイヤー情報自体は設定していませんでした。なので、今回はプレイヤーとして持たせる必要のある情報を作成していきましょう。   クラスの作成 初代ポケモンではプレイヤー情報として閲...

バトル状態のクラス化編 PHPポケモン 67
プログラミング
PHP,PHPポケモン,ポケモン
バトル状態のクラス化編 PHPポケモン 67

バトルの状態 PHPポケモンでも様々な技を再現してきましたが、まだまだ未実装のものはたくさんあります。そのほとんどがイレギュラー処理の必要なものだったりします。 それらをしっかりと解決していくためにも、今回は「バトル状態」をひとまとめに管理できるようにシステムの見直しを行います。   ひとまとめに...

バズる!ビジネスの見つけ方 〜何で起業するか悩んでいませんか?〜
マーケティング
ビジネス,起業
バズる!ビジネスの見つけ方 〜何で起業するか悩んでいませんか?〜

  独立や起業を志している9割以上の人が「何をビジネスにするか?」を悩んでいます しかし、それは負のサイクルです。 起業するためにビジネスを考えるという行為自体が”矛盾している”ということに気づかなければ、このサイクルからは抜け出すことができません。 その結果、行動できずに終わってしまう人は多...

フリーランスのための値段交渉術!案件はこうやって見極めろ
フリーランス
フリーランスのための値段交渉術!案件はこうやって見極めろ

  「これもっと安くならない?」 「他の人はこれぐらいの金額でやってくれるよ」   こういった値切り交渉を受けたことがある人もいるのではないでしょうか?技術を提供するフリーランスであれば、金額を落とすということは自分の価値を下げているということを理解しておかなければなりません。 値段交渉...

たった2日で200万円!フリーランスが簡単に仕事を受注できる方法とは
マーケティング
コロナ,フリーランス,ホームページ制作,助成金
たった2日で200万円!フリーランスが簡単に仕事を受注できる方法とは

  仕事の依頼が全然来ない・・・ 営業しても話を聞いてくれない・・・   その多くが営業力以上に、営業をすべきタイミングがわかっていない人がほとんどです。 実は助成金等を活用することで、行政書士などの職業以外でも仕事を受注することは可能になります。   今回は筆者が、たった2日で200万...

SEO対策で役立つ!ライティングスキルを向上させる3ステップ
ライティング
SEO,ブログ
SEO対策で役立つ!ライティングスキルを向上させる3ステップ

  ブログやメール、SNSなどライティングスキルが問われる場面が多く、それは質に直結しています。各プラットフォームによって特徴はありますが、基礎的な文章力自体がついていれば多様な場面で役立つことは間違いありません。 文章の質が高くなれば、ブログであればSEOに強い価値のあるサイトになりますし、SNS...

カテゴリ

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