プログラミング

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ポケモンは「バトルシステム実装編〜タイプ相性の判定〜」をご紹介しました。

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

 

注目の記事

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

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

Laravel7系でTraitのmakeコマンドを作成する方法
プログラミング
artisan,Laravel,Linux,PHP,Trait
Laravel7系でTraitのmakeコマンドを作成する方法

Laravel7がリリースされて、さっそくそれを使った開発の機会がやってきましたので、使用頻度の高いものをまとめていきたいと思います。 5系や6系の情報は多く出回っていますが、6系がLTSということもあって7系の情報は少なめですね。   今回は「Laravel7系でTraitのmakeコマンドを作成する方法」をご紹介します...

レベニューシェアとは?利益報酬・共同事業に潜む罠
フリーランス
レベニューシェアとは?利益報酬・共同事業に潜む罠

  レベニューシェアという言葉を聞いたことがありますか?   ビジネスの世界にいる人なら、意味は知らなくてもやったことがある方が意外と多いはずです。特にフリーランスの人や、プログラミングやデザインといったスキルを持っている人はレベニューシェアでは重宝されるため多い傾向があります。ここ最近...

ポケモンプロパティ編(全国図鑑No・捕捉率・重さ) PHPポケモン 77
プログラミング
PHP,PHPポケモン,ポケモン
ポケモンプロパティ編(全国図鑑No・捕捉率・重さ) PHPポケモン 77

PHPポケモン記事も記念すべき第77回を迎えることができ、これもひとえに皆様のお力があってのことです。いろんな方のご協力もあり(ドット絵とかドット絵とかドット絵とか)見た目はかなりゲームらしくなってきました。 しかし、肝心のゲーム要素が本家と比べればまだまだ機能も少なく未実装の項目も多いので、これ...

投稿タイトルを自動生成する方法【WordPressを便利にカスタマイズ】
プログラミング
PHP,WordPress
投稿タイトルを自動生成する方法【WordPressを便利にカスタマイズ】

  カスタムフィールドを含む投稿の場合、わざわざタイトルを設定する必要がないものもあります。 そういったときに、カスタムフィールド値を使ってタイトルを自動生成してくれると楽ですし、一覧で見てもわかりやすい状態にすることができます。   今回はWordPressで「投稿タイトルを自動生成する方法」につ...

PHPポケモン「タイプ実装編」15
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「タイプ実装編」15

ポケモンのタイプについて  2020年剣盾シリーズでのポケモンでは、全18タイプが実装されています。初代では15タイプ(あく、はがね、フェアリーを除く)からスタートして、たった3つのタイプしか増えていない、と感じる方もいるかも知れませんが、この追加に関してもバトルの歴史を大きく動かしてきたのです...

アクセス層とは – 物理的な信号の伝送【第4回 ド素人のためのネットワーク講座】
ネットワーク
TCP/IP,Wi-Fi,アクセス層,無線LAN
アクセス層とは – 物理的な信号の伝送【第4回 ド素人のためのネットワーク講座】

  第4回のド素人のためのネットワーク講座は アクセス層の物理的な役割について です。   実際にデータを送る際にはどのような信号が送られて、どういったことに影響を受けているのかを解説していきます。     信号を伝達する   TCP/IPモデルの第一層(レイヤー1)のアクセス層が担っている役割の...

ポケモン図鑑編 PHPポケモン 88
プログラミング
PHP,PHPポケモン,ポケモン
ポケモン図鑑編 PHPポケモン 88

ポケモン図鑑とは ポケモンに出会ったり、仲間にしたりすると、ポケモン図鑑のデータがどんどんと埋まっていきます。PHPポケモンでもこの仕組みを実現させるために、ポケモン図鑑を作成していきましょう。   クラスによる管理 ポケモン図鑑はクラス管理をしていきます。プレイヤー1人に対して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 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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力