プログラミング

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

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

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

 

注目の記事

自称デザイナーがおしゃれ名刺を作成してみた!2度見したくなる名刺とは?
デザイン
Adobe,Illustrator
自称デザイナーがおしゃれ名刺を作成してみた!2度見したくなる名刺とは?

  自称デザイナーらしく、オリジナルデザインの名刺を作成しました。 今回作った名刺はコチラです。   会社名や住所、名前の部分は仮で当てはめています。公開情報なのでそのままでも良いんですが一応です。   今回は「自称デザイナーがおしゃれ名刺を作成してみた!2度見したくなる名刺とは?」につい...

売れるECサイトになるために必要な3つの戦略
マーケティング
ECショップ,コンサルティング
売れるECサイトになるために必要な3つの戦略

  ECサイトで全く売れない・・・   ネットショップのオープンが手軽で安価になり、クレジット決済も主流の今ECサイトを立ち上げるお店も増えてきました。 しかし期待感とは裏腹に、思ったような売上が出なかったり、お客さんが1人も獲得できていないケースも少なくありません。   今回はそういった「...

連続攻撃技と一撃必殺技編 PHPポケモン39
プログラミング
PHP,PHPポケモン,ポケモン
連続攻撃技と一撃必殺技編 PHPポケモン39

連続攻撃技とは 追加効果だけでは処理できない技が、初代に限定していても数多くあります。その一つが「連続攻撃技」です。  連続攻撃技 https://wiki.ポケモン.com/wiki/連続攻撃技 連続攻撃技はさらに4パターンに分かれる。 攻撃回数が2回固定であるもの 攻撃回数が3回固定であるもの 攻撃回...

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

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

config実装編(ドット記法・多次元配列) PHPポケモン 71
プログラミング
PHP,PHPポケモン,ポケモン
config実装編(ドット記法・多次元配列) PHPポケモン 71

configファイルの作成 プログラミングでは設定値というものを使うことが良くあります。量が多い場合はデータベースへ格納して管理する場合も多いですが、わざわざテーブルを用意してまで格納するほどのものでなければ、ファイルに配列として定義してアクセスできる方が便利です。フレームワークではこれらをconfigフ...

お金を稼ぐためは理解しておきたい基本的な3要素【お客さんを増やす前に知っておかなければならないことって?】
マーケティング
お金を稼ぐためは理解しておきたい基本的な3要素【お客さんを増やす前に知っておかなければならないことって?】

  お客さんを増やすためにはどうすればいいんだろう?   ビジネスをやっている人で儲かっている一部を除けば、誰もが抱いている共通の悩みですね。 ですが、ただお客さんを増やすために安易な広告を打ったりするのは、実は危険なことなんです。   今回は「お金を稼ぐためには理解しておきた...

経験値分配編 PHPポケモン 89
プログラミング
PHP,PHPポケモン,ポケモン
経験値分配編 PHPポケモン 89

経験値の分配とは 大型アップデート後、最初の追加機能は「経験値の分配」についてです。 ポケモンの入れ替えができるようになりましたが、経験値をもらえるのは現在フィニッシャーのみとなっており、レベルの低いポケモンを育てるのが困難な状態です。なので、ゲームバランスを整えるためにも、まず経験値周りの処...

ノーコード開発で将来プログラミングは不要?我々は何を学ぶべきなのか
プログラミング
amazon,AWS,Honeycode,NoCode,プログラミング学習
ノーコード開発で将来プログラミングは不要?我々は何を学ぶべきなのか

  近年、プログラミングの需要は増え勢いを増していますが、それ以上にノーコードというソースコードを打たなくでもアプリ開発ができるというソフトウェアやプラットフォームが普及してきました。 これにより一部の期待感とは別に、プログラマーという職業は今後不要になるのではないかと危機感を抱く人が出て...

カテゴリ

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