プログラミング

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

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

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

 

注目の記事

進化アニメーション 前編 PHPポケモン 59
プログラミング
PHP,PHPポケモン,ポケモン
進化アニメーション 前編 PHPポケモン 59

進化アニメーションの実装 今回は後回しにしていた進化アニメーションの作り込みをしていきます。今までもレベルに達すれば進化はしていましたが、その演出はありませんでした。また、ポケモンではBボタンを押すことで進化のキャンセルをすることができます。なので、この辺りも実際のゲームを再現していきましょう。...

バトルステータス可視化編 PHPポケモン 70
プログラミング
PHP,PHPポケモン,ポケモン
バトルステータス可視化編 PHPポケモン 70

バトルステータスの可視化 現在のポケモンでは、バトルステータスの現状を可視化できるようになっており、どれぐらいのランク補正がかかっているか、フィールドがどういう状態になっているかがわかるようになっています。 初代、第2世代等では確認できなかった内容ではありますが、システムとしては便利な要素でも...

動画編集で知っておくべき基本的な3工程
動画編集
Adobe,AfterEffects,Illustrator,Photoshop,PremierePro
動画編集で知っておくべき基本的な3工程

  YouTubeに動画を投稿したいけど、動画の基本的な作成手順がわからない・・・   動画作成をするためのソフトは色々ありますが、有料なものであればやはりAdobe製品に軍配が上がります。 しかし準備したは良いものの、いろんなソフトが合ってどれを使えばよいか分からないという悩みを抱えている方は非常...

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

  初心者の方、今からプログラミングを始める方、HTMLとCSSの領域から抜け出せない人たちを対象にした、PHPをできるだけ使えるようになるための方法を紹介するコーナー。 栄えある第二回は【配列編】です。   第一回を飛ばしてこのページへに来てしまい「変数って何?」というレベルの人は、第一回の【変数編...

システムエンジニア向けまとめ情報サイト IT News Checker
ネットワーク
システムエンジニア向けまとめ情報サイト IT News Checker

新年のスタートダッシュが遅れ気味に見えるかも知れませんが、年末からじっくりと作業を進めており、やっとある程度形になりました。 師走の如く作業に走り、気分転換に雪遊びもしつつ、成人式を終えた辺りでリリースしたのが「IT News Checker」です。   IT News Checkerとは 簡潔に説明すると、まと...

反動技編(はかいこうせん・とびひざげり)PHPポケモン40
プログラミング
PHP,PHPポケモン,ポケモン
反動技編(はかいこうせん・とびひざげり)PHPポケモン40

反動技とは ポケモンでは反動技というものがあります。反動によっても様々で、現在の仕組みのままでも実装可能なものや、新たに処理が必要なものまであります。なので、一律で反動技という同じ処理は出来ません。  反動技(ポケモンwiki) https://wiki.ポケモン.com/wiki/反動技 反動技(はんどうわざ...

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

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

フリーランス必見!良質案件を獲得するための3つのプロセス
フリーランス
フリーランス必見!良質案件を獲得するための3つのプロセス

  「良い案件に巡り会えない」 「なかなか仕事が受注できない」   駆け出しフリーランスや、これから独立しようと考えている人が直面する大きな悩みの1つですね。 ですが、意外にも自分でその案件自体を制限していたり、良質だった案件を自らで質を下げてしまっているというケースは少なくありません。...

カテゴリ

SEO対策 イベント デザイン ネットワーク ビジネスモデル フリーランス プログラミング マーケティング ライティング 動画編集 雑記

タグ

5G Adobe AfterEffects AI ajax amazon Animate api artisan atom Automator AWS Bluetooth CSS CVR description EC-CUBE4 ECショップ ESLint Facebook feedly foreach fortify function Google Google AdSense Honeycode htaccess HTML IEEE 802.11ax Illustrator Instagram IoT JavaScript jetstream 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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 認証 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力