プログラミング

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

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

 

注目の記事

XserverにSequel ProでSSH接続する方法を4ステップでわかりやすく解説
ネットワーク
phpMyAdmin,Sequel Pro,SSH,Xserver,データベース,公開鍵,秘密鍵
XserverにSequel ProでSSH接続する方法を4ステップでわかりやすく解説

  AWSなどが注目される中、Xserver(エックスサーバー)は操作やセットアップが簡単で、ポートフォリオや業務、個人ブログ(WordPess)で使用している方に需要が高いサービスです。 そしてMacのPCを使っている方はデータベースの管理ツールとして、無料で使えるSequel Proが人気があり、多くのユーザーが使用し...

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

2日ほどwiki作成にいそいそと励んでいましたが、開発をお休みしていたわけではありません。追加機能を実装するに辺り、色々と改善点が挙がってきたので、このタイミングでしっかりと見直しをしました。   本格的なシステム開発では、最初に仕様書や設計書が作成され、それに沿って作成していくことになります...

本当に価値のある宣伝方法 〜多くの人が実践している【間違った努力】とは〜
マーケティング
Facebook,note,Twitter,YouTube,集客
本当に価値のある宣伝方法 〜多くの人が実践している【間違った努力】とは〜

  YouTubeやTwitter、Facebookやnoteなど、いろんなサービスを使った集客方法があります。集客という視点ではなく、それ自体に人を集めたいという人も多いでしょう。   フォロワーやチャンネル登録者数を増やすために、多くの人が様々な取り組みをしていますが、その中でもひときわ意味のないことをただ繰り返して...

【完全未経験OK】プログラミングの始め方 〜学習方法を解説します!〜
プログラミング
CSS,HTML,JavaScript,MAMP,PHP,XAMPP,フリーランス,プログラミング,独立,起業
【完全未経験OK】プログラミングの始め方 〜学習方法を解説します!〜

  プログラミングって難しい   そういう人は多いですね。しかしそんなことは有りません。 言語という点で比較すれば、英語やフランス語など第二言語を学ぶ方が難しいと言えます。   プログラミングの需要は年々高まり、今や最高潮とも言える域まで来ています。 なぜそこまで需要が高いのか?   ...

YouTuber・ブロガー必見!知る人ぞ知るサムネイルの重要性とは
デザイン
Facebook,Instagram,Twitter,YouTuber,サムネイル,ブロガー
YouTuber・ブロガー必見!知る人ぞ知るサムネイルの重要性とは

  サムネイルって本当に重要? ブログの場合はフリー画像でもいいんじゃない?   そう考えている人はいませんか? 残念ですが、それは大きな間違いです。サムネイルを作り込むことは非常に重要であり、集客ポイントを拡大させるのはもちろん、ブランディングにもつながるのです。   今回は「知る人ぞ...

Toastr(トースト)活用編 PHPポケモン 100
プログラミング
JavaScript,PHP,PHPポケモン,ポケモン
Toastr(トースト)活用編 PHPポケモン 100

記念すべき第100回目です!   色々考えましたが、100回目だからと言って特別な内容ではなく、いつもの流れの延長での開発進行となります。ご了承ください。 今回は、何人かのプレイユーザーの声も参考にしながら、ユーザビリティをあげるための機能追加を進めていきます。   Toastr(トースト)と...

今からできる!ブログのアクセスを爆UPさせる3大SNS活用法
マーケティング
Facebook,Instagram,Twitter,ブロガー,ブログ
今からできる!ブログのアクセスを爆UPさせる3大SNS活用法

  ブログを収益化させたいけど、なかなかアクセス数が増えない 記事の質は高いのに、その良さをどうやって伝えれば良いかわからない   SEO対策をする上でも、収益化するためにも記事のクオリティは重要です。 しかし、せっかく良い記事を書いていても、そのブログや記事の存在を伝えることができなけれ...

構成見直し編(クラス名) PHPポケモン49
プログラミング
PHP,PHPポケモン,ポケモン
構成見直し編(クラス名) PHPポケモン49

構成の見直し 今回は全体構成の見直しをします。ディレクトリについては変更ありませんが、ファイル名とクラス名について大幅な修正をかけていきます。   クラス名の重複回避 まず、クラス名の重複についてです。状態異常・状態変化の子クラスでは重複回避のために接頭語を着けて管理していましたが、他にも重複...

カテゴリ

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