プログラミング

PHPポケモン「バトルシステム実装編〜タイプ相性の判定〜」デモ&配布有り 19

jQuery PHP PHPポケモン ポケモン
PHPポケモン「バトルシステム実装編〜タイプ相性の判定〜」デモ&配布有り 19

 

システムを組むなら、仕様書や設計書はしっかり作りましょう。

 

ということで、またまたフォルダ移動やページ分けなどを見えないところでやりました。正直説明すると全く進まなくなりそうなので、改修部分は必要最低限にします。

結論、説明しません。(コード配布するので許してください)

 

そして今回は、本格的なバトルシステム実装に向けた第一歩、タイプ相性の判定を作り込んでいきます。

  

バトルシステムの実装

 まず、ポケモンのバトルシステムについて、簡単におさらいしていきましょう。

 

ポケモンの第2世代からはダブルバトルという2対2で戦うシステムが追加され、その後の世代ではマルチバトルなど戦う形式も豊富になりました。ですが、今回は初代を再現するということで、1対1のバトルのみを想定して作成します。

 

技選択をして行われる判定は以下の通りです。(順不同)

  1. 素早さの判定(早い方が先に行動)
  2. 技タイプと相手ポケモンのタイプ相性の判定
  3. 技タイプと攻撃ポケモンのタイプ一致の判定
  4. 攻撃種別(物理・特殊・変化)の判定
  5. 急所判定
  6. 命中率(回避率)の判定
  7. ダメージ量の計算
  8. 追加効果の判定

 

ざっとまとめてこんな感じです。(漏れあるかも知れません)

流石にこれを一気に組み上げるとなると膨大な量になってしまうので、一つずつ組み上げていきます。

 

「たたかう」アクションの作成

 それではまず、「たたかう」というアクションの分岐をコントローラーに追加します。

 

バトル用コントローラー(/Classes/Controller/BattleController.php
/**
* アクション
*
* @param string $action
* @param mixed $param
* @return void
*/
private function action($action, $param)
{
    switch ($action) {
        // にげる
        case 'run':
        unset($_SESSION['enemy']);
        header("Location: ./home.php", true, 307) ;
        exit;
        // たたかう
        case 'fight':
        $message = $this->attack($this->pokemon, $this->enemy, $param);
        $this->setMessage($message);
        break;
    }
}

 ※海外版ポケモンのバトル画面を見たところ、にげるは「run」と表記されていたため、変更しています

 

たたかう(fight)のアクションが選択された場合は、attackというメソッドを実行します。こちらは新しくトレイトに作成しました。

今回は、すべてを作り込むわけではなく、一旦メッセージだけを返却してもらうので、受け取ったものをコントローラーでメッセージとしてセットして結果を確認しましょう。

 

タイプ相性の判定

 では、バトルシステムの中では最も簡単(だと思われる)相性判定を行います。

先程コントローラーで記述したattackのメソッドが含まれているバトル用トレイトを見ていきましょう。

 

攻撃トレイト(/Traits/Battle/AttackTrait.php
<?php
trait AttackTrait
{
    /**
    * 攻撃する
    *
    * @param object $atk_pokemon
    * @param object $def_pokemon
    * @param string $move_class
    * @return array
    */
    protected function attack($atk_pokemon, $def_pokemon, $move_class)
    {
        // 技のインスタンスを取得
        $move = $this->getInstance($move_class);
        // タイプ相性チェック
        $type_comp = $this->checkTypeCompatibility($move->getType(), $def_pokemon->getTypes());
        return $type_comp['message'];
    }
 
    /**
    * タイプ相性チェック
    *
    * @param object $atk_type
    * @param array $def_types
    * @return array
    */
    private function checkTypeCompatibility($atk_type, $def_types)
    {
        // ダメージ補正(初期値は等倍)
        $damage_comp = 1;
        // 補正判定
        foreach($def_types as $def_type){
            // 「こうかがない」かチェック
            if(in_array($def_type, $atk_type->getAtkDoesntAffectTypes(), true)){
                // ダメージ無し
                $damage_comp = 0;
                // ループ終了
                break;
            }
            // 「こうかばつぐん」かチェック
            if(in_array($def_type, $atk_type->getAtkExcellentTypes(), true)){
                // 2倍
                $damage_comp *= 2;
                // 次の処理へスキップ
                continue;
            }
            // 「こうかいまひとつ」かチェック
            if(in_array($def_type, $atk_type->getAtkNotVeryTypes(), true)){
                // 半減
                $damage_comp /= 2;
            }
        }
        // 補正によるメッセージの分岐
        if($damage_comp === 0){
            $message = 'こうかがないみたいみたいだ';
        }elseif($damage_comp > 1){
            $message = 'こうかはばつぐんだ!';
        }elseif($damage_comp < 1){
            $message = 'こうかはいまひとつだ';
        }
        // ダメージ補正とメッセージを配列にして返却
        return [
            'damage_comp' => $damage_comp,
            'message' => $message ?? '',
        ];
    }
}

 

それでは順番に見てきましょう。まずは親となるattackというメソッドについてです。

/**
* 攻撃する
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param string $move_class
* @return array
*/
protected function attack($atk_pokemon, $def_pokemon, $move_class)
{
    // 技のインスタンスを取得
    $move = $this->getInstance($move_class);
    // タイプ相性チェック
    $type_comp = $this->checkTypeCompatibility($move->getType(), $def_pokemon->getTypes());
    // 技種類での分岐
    switch ($move->getSpecies()) {
        // 物理
        case 'physical':
        // ここに物理技の処理
        break;
        // 特殊
        case 'special':
        // ここに特殊技の処理
        break;
        // 変化
        case 'status':
        // ここに変化技の処理
        break;
    }
    return $type_comp['message'];
}

 

第1引数では攻撃ポケモン第2引数では防御ポケモン第3引数では技のクラス名を受け取っています。

 

まず最初に、受け取った技クラスをgetInstanceでインスタンス化します。相性チェックでは、攻撃技のタイプと、防御ポケモンのタイプがあれば判定が可能なので、それぞれを引数としてタイプ相性チェック用のメソッド(checkTypeCompatibility)にかけます。

/**
* タイプ相性チェック
*
* @param object $atk_type
* @param array $def_types
* @return array
*/
private function checkTypeCompatibility($atk_type, $def_types)
{
    // ダメージ補正(初期値は等倍)
    $damage_comp = 1;
    // 補正判定
    foreach($def_types as $def_type){
        // 「こうかがない」かチェック
        if(in_array($def_type, $atk_type->getAtkDoesntAffectTypes(), true)){
            // ダメージ無し
            $damage_comp = 0;
            // ループ終了
            break;
        }
        // 「こうかばつぐん」かチェック
        if(in_array($def_type, $atk_type->getAtkExcellentTypes(), true)){
            // 2倍
            $damage_comp *= 2;
            // 次の処理へスキップ
            continue;
        }
        // 「こうかいまひとつ」かチェック
        if(in_array($def_type, $atk_type->getAtkNotVeryTypes(), true)){
            // 半減
            $damage_comp /= 2;
        }
    }
    // 補正によるメッセージの分岐
    if($damage_comp === 0){
        $message = 'こうかがないみたいみたいだ';
    }elseif($damage_comp > 1){
        $message = 'こうかはばつぐんだ!';
    }elseif($damage_comp < 1){
        $message = 'こうかはいまひとつだ';
    }
    // ダメージ補正とメッセージを配列にして返却
    return [
        'damage_comp' => $damage_comp,
        'message' => $message ?? '',
    ];
}

 

まず、$damage_compという変数に初期値として1をセットしています。これは、ダメージ補正値になります。タイプ相性が関係なければ、ダメージ計算をして1をかけます。攻撃技のタイプは1つですが、防御ポケモンのタイプは1〜2つが想定されます。なので、防御ポケモンのタイプをforeachで1つずつ判定していきます。

判定する方法は、攻撃する技が防御ポケモンのタイプにとって「こうかばつぐん」「こうかいまひとつ」「こうかがない」のいずれかに存在しているかを、順番にin_arrayを使って検証しています。

まず「こうかがない」のタイプ(getAtkDoesntAffectTypes)の配列に含まれているかどうかを検証します。

// 「こうかがない」かチェック
if(in_array($def_type, $atk_type->getAtkDoesntAffectTypes(), true)){
    // ダメージ無し
    $damage_comp = 0;
    // ループ終了
    break;
}

 

効果がないと判定されると、もう一つのタイプがどうであれダメージを与えることができません。なので、もし該当した場合は$damage_compに0をセットして、breakを使ってループ自体を終了させます。

 

残りの2つは順不同です。今回は「こうかばつぐん」を先にチェックしています。

// 「こうかばつぐん」かチェック
if(in_array($def_type, $atk_type->getAtkExcellentTypes(), true)){
    // 2倍
    $damage_comp *= 2;
    // 次の処理へスキップ
    continue;
}

 

「こうかばつぐん」であれば、ダメージ量が倍になります。もし2タイプともにこうかばつぐんの判定がでれば、1xx2=4倍のダメージが与えられます。「こうかばつぐん」の判定がされた後、「こうかいまひとつ」と判定されることはありえないので、現在ループしているタイプでの判定を終了するため、continueを使って残りの処理はスキップします。

 

最後は「こうかいまひとつ」の判定をします。

// 「こうかいまひとつ」かチェック
if(in_array($def_type, $atk_type->getAtkNotVeryTypes(), true)){
    // 半減
    $damage_comp /= 2;
}

 

もしタイプが該当していれば、補正値割る2をしています。「こうかばつぐん」→「こうかいまひとつ」という判定をされた場合は、1x/2=1になり、ダメージ補正無し(等倍)に戻ります。

 

補正値を計算したら、相性に対応したメッセージを出力します。

// 補正によるメッセージの分岐
if($damage_comp === 0){
    $message = 'こうかがないみたいみたいだ';
}elseif($damage_comp > 1){
    $message = 'こうかはばつぐんだ!';
}elseif($damage_comp < 1){
    $message = 'こうかはいまひとつだ';
}

 補正値が0 →「こうかがない」

補正値が1超過 →「こうかばつぐん」

補正値が1未満 →「こうかいまひとつ」

補正値が1 →「等倍(メッセージ不要)」

 

上記の判定をしています。メッセージ格納後は、補正値と一緒に返り値とするため、配列に格納してretrunを行います。

// ダメージ補正とメッセージを配列にして返却
return [
    'damage_comp' => $damage_comp,
    'message' => $message ?? '',
];

 

あとはattackのメソッドで受け取ったメッセージを返却するだけです。今回ダメージ計算は行わないため、補正値は使用しません。

 

技選択フォームの実装(jQuery

 PHPポケモンという名称ですが、(HTML,CSSを除いて)100%PHPで組み上げるようなことはしません。JavaScript(今回はjQuery)のお力も借ります。頑張れば、PHPだけでも十分に再現は可能です。しかし、私は頑張りません

 

前回実装した通り、技はテーブルから選択する形式です。記述量が多くなったので、パーツとして分割しておきます。

 

技選択フォーム(/Resources/Partials/Battle/Forms/move.php
<form action="" method="post" id="fight-form">
    <input type="hidden" name="action" value="fight">
    <input type="hidden" name="param" id="fight-form-param">
    <div class="input-group mb-3">
        <table class="table table-bordered table-hover mb-3" id="move-table">
            <thead class="thead-light">
                <tr>
                    <th scope="col">使える技</th>
                    <th scope="col">タイプ</th>
                    <th scope="col">PP</th>
                </tr>
            </thead>
            <tbody>
                <?php foreach($pokemon->getMove() as $move): ?>
                    <tr class="move-table-row" data-move_class="<?=get_class($move)?>">
                        <th scope="row" class="w-50"><?=$move->getName()?></th>
                        <td><?=$move->getType()->getName()?></td>
                        <td><?=$move->getPp()?>/<?=$move->getPp()?></td>
                    </tr>
                <?php endforeach; ?>
            </tbody>
        </table>
    </div>
</form>
<script>
// jQueryでsubmitを実行
$(document).ready(function() {
              $('.move-table-row').click(function(){
        // 技をフォームへセット
        $('#fight-form-param').val($(this).data('move_class'));
        // サブミット実行
        $('#fight-form').submit();
    });
});
</script>

 ※battle.phpのheadでjQueryのライブラリを読み込んでいることが前提になります

 

前回から追加された部分として、テーブルをfight-formというidのフォームタグで囲い、hiddeninputとしてactionparamを用意しています。paramには選択された技クラスを格納するため、idfight-form-paramを設定してjQueryを使用します。

 

テーブル内での追加要素としては、技の行(tbody内のtr)にクラス名(move-table-row)を付け、data-move_classに技クラス名を持たせています。

 

それではjs内を見てみましょう。

<script>
// jQueryでsubmitを実行
$(document).ready(function() {
              $('.move-table-row').click(function(){
        // 技をフォームへセット
        $('#fight-form-param').val($(this).data('move_class'));
        // サブミット実行
        $('#fight-form').submit();
    });
});
</script>

 

move-table-rowのクラス要素がクリックされたら、fight-form-paramid要素の値(value)に、クリックされた要素がもつdata-move_classの値をセットして、フォームをサブミット(送信)しています。

もっと簡単に説明すると、技がクリックされたら、クリックされた技のクラス名をフォームにセットして送信までしてくれているということです。

 

これで、データの送信処理は完了です。それでは動きを確認してみましょう。

 

ヒトカゲ vs フシギダネ

 

 

 

 

ピカチュウ vs フシギダネ

 

 

 

  

各技タイプに合った判定が返ってきていることがわかりますね。

これでバトルシステムにおけるタイプ判定が実装完了です。

 

デモページ

 久々にデモページを開放します。見た目が若干良くなっていますが、システムとしてはそこまで精巧に組まれていないため、負荷をかけすぎないように節度ある楽しみ方をしてください。

PHPポケモン デモページ 

 

コードの配布

 第19回終了時点でのコードを配布します。

※ホーム画面と初期画面をわけたり、リダイレクト処理の部分については説明をスキップしているので、配布コードを参考にしてください

 

まとめ

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

今回のPHPポケモンは「バトルシステム実装編〜タイプ相性の判定〜」をご紹介しました。

サンプルコードを使って、ぜひ楽しみながらの学習に役立ててくださいね。

 

注目の記事

プログラミングで躓く人必見!一人前になるためのSDCとは
プログラミング
プログラミング学習
プログラミングで躓く人必見!一人前になるためのSDCとは

  「プログラミング学習を始めたけど中々身につかない」 「挑戦したいけど何から始めればいいかわからない」   プログラミング教育が始まるとともに、プログラミング学習のニーズも日々高まってきています。ですが、興味はあっても中々挑戦までは至らなかったり、始めたは良いものの現実は厳しく躓いてし...

迷惑メールはなぜ届く?amazonや楽天を騙る悪質メールへの対処法とは
ネットワーク
amazon,スパム,メール,楽天
迷惑メールはなぜ届く?amazonや楽天を騙る悪質メールへの対処法とは

「いきなり迷惑メールが届くようになった」 「Amazonや楽天を装ったメールはどうやって見分ければいいの?」   知らない人からいきなり連絡が届いたり、登録もしていないようなサービスからメールが届けば、それは詐欺メールかも知れません。 スマホやパソコンが一般的に普及して、ネットでの買い物やサービスを利用...

ネコにこばん編 PHPポケモン73
プログラミング
PHP,PHPポケモン,ポケモン
ネコにこばん編 PHPポケモン73

ネコにこばんとは 初代ポケモンでは、基本的にトレーナーとのバトルでしかお金を増やすことができず、もし手持ち金が0円の状態で金が必要なイベントが発生すると詰んでしまうという隠された落とし穴がありました。 ですが、初代でも唯一トレーナー意外からお金を得る方法があります。それが「ネコにこばん」という...

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

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

保守っていくらで何をする?フリーランスWebエンジニア対象!保守業務について解説します
フリーランス
システムエンジニア,フリーランス,プログラマー
保守っていくらで何をする?フリーランスWebエンジニア対象!保守業務について解説します

  サイトの保守を頼まれたけど、具体的に何をやればいいの?   フリーランスの、特に独学やスクール上がりでなった人にとっては保守は何をすれば良いかわからないという悩みを抱えている人は多いのではないでしょうか。 システム開発会社や制作会社にいた人でも、どのぐらいの金額で請け負うのが良いか検...

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

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

オウムがえし編 PHPポケモン 68
プログラミング
PHP,PHPポケモン,ポケモン
オウムがえし編 PHPポケモン 68

オウムがえしとは 今回は久々に新しい技を実装します。それが「オウムがえし」です。 オウムがえし(ポケモンwiki) https://wiki.ポケモン.com/wiki/オウムがえし   初代で登場した技であり、序盤に登場するポッポやオニスズメがレベルアップで覚える技の1つです。最新世代ではオウムがえしという技は実...

ピカチュウから学ぶオブジェクト指向 〜入門編〜 1
プログラミング
PHP,PHPポケモン,オブジェクト指向,ポケモン
ピカチュウから学ぶオブジェクト指向 〜入門編〜 1

オブジェクト指向とは  オブジェクト指向プログラミング https://ja.wikipedia.org/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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力