プログラミング

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

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

 

注目の記事

Zoomも飲み屋もなくならない 〜コロナ収束後の本当の世界〜
雑記
YouTube,Zoom,コロナ,テレワーク,リモートワーク
Zoomも飲み屋もなくならない 〜コロナ収束後の本当の世界〜

  新型コロナウイルスのニュースが絶えない毎日を過ごす中、コロナの内容に一度も触れていませんでしたがそろそろ触れておこうと思います。   マイナスな内容は不安を煽るような推測はSNSやニュースでイヤというほど目にしているので、可能性を感じられる内容と現実をお届けします。   コロナが収束した時、この...

そらをとぶ&あなをほる編 PHPポケモン46
プログラミング
PHP,PHPポケモン,ポケモン
そらをとぶ&あなをほる編 PHPポケモン46

チャージ中の回避技 以前は「ロケットずつき」や「ソーラービーム」をサンプルとしてチャージ技を実装しましたが、今回は少し特別な効果をもったチャージ技を実装します。それが「そらをとぶ」と「あなをほる」です。これらは初代ポケモンでも重宝される技であり、チャージ中に相手からの攻撃を回避することができま...

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

オブジェクト指向とは  オブジェクト指向プログラミング https://ja.wikipedia.org/wiki/オブジェクト指向プログラミング オブジェクト指向プログラミングとは、互いに密接な関連性を持つデータとメソッドをひとつにまとめてオブジェクトとし、それぞれ異なる性質と役割を持たせたオブジェクトの様々な定...

進化直後の技習得編 PHPポケモン 65
プログラミング
PHP,PHPポケモン,ポケモン
進化直後の技習得編 PHPポケモン 65

進化直後の技習得 パーティー機能を導入に合わせて、至る場所を修正することになり、合わせて未実装だった機能を導入していきます。 見た目にはわからない部分や、とある条件が重ならなければ起こらない部分の作り込みが多いため、プレイユーザー目線からすると少し面白みが無いかも知れません。 ですが、そういっ...

プログラミングを優しく解説!学んで得する3つの理由
プログラミング
プログラミング教育
プログラミングを優しく解説!学んで得する3つの理由

  プログラミング教育が始まるけど、そもそもよくわかっていない   2020年からは小学校がプログラミング学習が必修化され、翌年には中学校でも導入予定です。 しかし、保護者からすると全くわからず困惑していたり、教える先生たち教師陣からしてもよくわかっていないケースは少なくありません。   今回...

飲食店がホームページを作る5つのメリット 〜デメリットについても解説します〜
マーケティング
Google AdSense,SEO
飲食店がホームページを作る5つのメリット 〜デメリットについても解説します〜

  お店のホームページを作りたい   そういった飲食店の方は意外にも多く、イタリアンや居酒屋、日本料理店など種類も様々です。   今回のテーマは「飲食店がホームページを作る上で知っておきたいメリットとデメリットについて」です。   食べログやホットペッパーなどといったグルメサイトが主流とな...

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

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

目先の利益に気をつけろ!貧乏ビジネスという落とし穴
フリーランス
目先の利益に気をつけろ!貧乏ビジネスという落とし穴

  目先の利益を求めてしまい、来たるべきビジネスチャンスに対応できないというケースは貧乏ビジネスに陥る大きな要因になります。また、相手が下す評価に左右されてしまうことも、自らの評価を下げてしまったり、見積もりを作る上でも大きく影響を及ぼしてしまいます。   今回は「目先の利益に気をつけろ!貧...

カテゴリ

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