プログラミング

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

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

 

注目の記事

WordPressのJSファイル読み込みで覚えておきたい便利ワザを2つ紹介します
プログラミング
jQuery,PHP,WordPress
WordPressのJSファイル読み込みで覚えておきたい便利ワザを2つ紹介します

  JSファイルを読み込むときに、deferやasyncを付与したい・・・ get_template_directory_uri()をJSファイル内で使用したい・・・   WordPressには便利な関数が沢山用意されていますが、その全てが万能だということはありません。 今回はSEO対策でも必要になるdeferやasyncをスクリプトタグに付与させる...

【JavaScript】ESLint下でToastr(グローバル変数)を使用する方法
プログラミング
ESLint,JavaScript,jQuery,Toastr
【JavaScript】ESLint下でToastr(グローバル変数)を使用する方法

  ESLintはシステム全体で見れば便利なツールと言えますが、ライブラリなどを読み込む際に通常紹介されているような方法では使えなかったり、慣れていない人からするとどうしても躓くポイントが多くなるのも事実です。 ですが使いこなせるようになれば、開発がスムーズになることはもちろん、保守性やコードの...

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

ポケモンの進化演出 前回に続いて、ポケモンの進化演出を実装していきます。バックエンドの処理はざっと説明をしたので、今回はフロントエンド(JavaScript)側の処理を作成していきましょう。   進化画面は新しく設けたので、こちらにもバトル画面で使っているメッセージ用JSを作成していきます。処理自体はほとん...

記事を書いてもブログが伸びないのは何故?SEOの評価を下げてしまう絶対にダメな3つのこと【知らない内に損してます】
SEO対策
SEO,ブログ
記事を書いてもブログが伸びないのは何故?SEOの評価を下げてしまう絶対にダメな3つのこと【知らない内に損してます】

  1年ブログを継続したけど、全くPVが伸びない・・・   ブログを育てるためには、毎日または定期的な更新が必ず必要ですが、それでも思うように伸びないという人は、実は他に致命的にな原因があることがほとんどです。 今回はそういった「努力しても結果が出ない」と悩んでいるブロガーやブログ運営者に...

パーティー実装編 戦闘に参加するポケモン PHPポケモン64
プログラミング
PHP,PHPポケモン,ポケモン
パーティー実装編 戦闘に参加するポケモン PHPポケモン64

先頭のポケモンを選出 前回パーティーのプロパティを準備して、複数(6匹)のポケモンを持ち歩けるようにすることを想定しました。 今回は、そこからバトル画面への連動をさせる部分までを作り込んでいきましょう。   複数のポケモンを所有している場合、戦闘が始まって繰り出されるのは「ひんし状態を除く一番上...

「発想と企画を量産する」第3回 朝活のまとめ
イベント
三宮,朝活,神戸
「発想と企画を量産する」第3回 朝活のまとめ

  第3回朝活を実施しました。   この記事では、その中で取り上げられた内容について具体的な考え方や内容をまとめていますので、ぜひ興味を持たれた方は次回朝活へお越しください。   今回の朝活のサブタイトルは「発想と企画を量産する」になります。      「話のネタが無い」は禁物   ブログで...

事業所検索サービス「児発ねっと」児童発達支援・放課後等デイサービス
ビジネスモデル
SEO対策,プログラミング,児童デイサービス,児童発達支援,放課後等デイサービス
事業所検索サービス「児発ねっと」児童発達支援・放課後等デイサービス

どうも、児発ねっとの中の人です。 この度は児童発達支援・放課後等デイサービスといった療育施設の事業所検索サービス「児発ねっと」を開始することになりました。 本ブログでは、プログラミングやデザインといった内容のコンテンツを紹介しているため、児発ねっとのサービスは少し異色になります。なので、今回...

プレイヤーのグローバル化編 PHPポケモン 78
プログラミング
PHP,PHPポケモン,ポケモン
プレイヤーのグローバル化編 PHPポケモン 78

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