プログラミング

PHPポケモン「バトルシステム編〜経験値の獲得〜」29

PHP PHPポケモン ポケモン
PHPポケモン「バトルシステム編〜経験値の獲得〜」29

経験値の獲得

今まではポケモンに直接経験値を与えるというチートびっくりの仕様でしたが、バトルシステムも終盤に差し掛かってきたので「倒したポケモンから経験値を取得する」というごく当たりまえの仕様を導入していきます。

 

基礎経験値の設定

では、経験値の計算式に入る前に、必要なパラメーターを1つ用意しましょう。それは「基礎経験値」についてです。

 

毎度ながらポケモンwikiさんご協力(というより参考)の元、実装していきます。

ポケモンにはそれぞれ基礎経験値というものが割り当てられており、そのポケモンを倒した際にその値を元に計算がなされます。同じレベルでも、キャタピーを倒すよりトランセルを倒した方が経験値が高いというのは、この基礎経験値が影響しているためです。

 これはポケモンそれぞれに割り当てる必要があるので、各ポケモンのクラスにbase_exというプロパティを追加して、初期値に表の値をセットしていきましょう。

 

フシギダネ(/Classes/Pokemon/Fushigidane.php
/**
* 基礎経験値
* @var integer
*/
protected $base_exp = 64;

 

サンドバック野生ポケモンとして固定出現してくださるフシギダネさんに基礎経験値である64を割り当てました。上書きすることはない値のため、取得用メソッドのみGet格納トレイトに追加しましょう。

 

Get格納トレイト(/Traits/Pokemon/GetTrait.php
/**
* 基礎経験値を取得する
* @return integer
*/
public function getBaseExp()
{
    return $this->base_exp;
}

 

これで経験値の計算に必要な値が揃いました。

 

獲得経験値の計算

次に経験値の計算です。

経験値の計算方法は、世代を経るごとに大きく修正が加えられています。ダメージ計算などとは違い、旧式の計算方法を再採用したり、さらに新計算方法に戻すなど時代の変化に合わせながら実験的に導入されているようにも感じさせます。

 

PHPポケモンでは、最新世代(第8世代)を参考にカスタムします。まずはwikiに掲載された経験値の仕様を確認してみましょう。

 

 

第五世代、第七世代、第八世代

こちらでは、倒したポケモンのレベルも経験値を変化させるパラメータとして登場する。

経験値 = (EXP × A × レベル補正^2.5 + 1) × B × C × D

 

  • EXP : 倒されたポケモンのレベル × 倒されたポケモンの基礎経験値 ÷ 5
    レベル補正 = (2L + 10) / (L + Lp + 10)
  • L : 倒されたポケモン
  • Lp : 倒したポケモン
  • A : (第五世代のみ)トレーナー戦なら1.5、それ以外は1。第七世代では常に1
  • B : デルパワー・ロトポンの効果。けいけんちパワー+1.2++1.5+++2.00.80.660.5。けいけんポンは1.5
  • C : 他言語版と交換したポケモンは1.7、同言語と交換したポケモンは1.5、おやが自分であれば1
  • D : しあわせタマゴを持っていれば1.5、それ以外は1

 

経験値を貰えるポケモンが複数匹いる場合、 

  • 第五世代 : EXP × A」をその匹数で割り、その値を「EXP × A」の代わりに使ってそれ以降の計算を行う。
  • 第七世代 : 匹数で割ることはなく、貰えるポケモンそれぞれに上記の計算を行う。

 

まず、Aの値についてですが、トレーナー戦か野生かで経験値の獲得量に変化があるようです。これは第5世代のみ導入されていた計算方式のため、関係なく1をセットするものとして不要とします。

 

次にBCDについてです。デルパワーなど経験値補正仕様はもちろん実装せず、ポケモンの交換も今の所実装予定はなく、持ち物も現段階はスルーする予定なのですべて不要とします。

 

複数匹いる場合の割り振り処理についてですが、最新世代では所有ポケモン全員に獲得経験値が割り当てられるという仕様が採用されていました。ですが、これに関しては採用せず登場したポケモンだけが経験値をもらえるという方式を採用予定です。現在は複数匹所有のロジックがまだ出来ていませんので、一旦保留としましょう。

 

以上を踏まえ、今回採用する経験値の計算式は以下の通りです。

経験値 = (EXP × レベル補正^2.5 + 1)

 

それでは、前回追加したひんし判定(checkFainting)のメソッド内に経験値計算と獲得処理を追加しましょう。

 

チェック格納トレイト(/Traits/Battle/CheckTrait.php
/**
* ひんし判定
*
* @param object $pokemon
* @return boolean
*/
protected function checkFainting($pokemon)
{
    if($pokemon->getSa() === 'SaFainting'){
        // ひんし状態
        $this->setMessage($pokemon->getPrefixName().'は倒れた');
        if($pokemon->getPosition() === 'friend'){
            // 味方がひんし状態になった
            $this->setMessage('目の前が真っ暗になった');
        }else{
            // 相手をひんし状態にした
            // 経験値の計算
            $exp = $this->calExp($this->pokemon, $this->enemy);
            // 経験値をポケモンにセット(返り値をpokemonに格納)
            $this->pokemon = $this->pokemon
            ->setExp($exp);
            // ポケモンに溜まったメッセージを取得
            $this->setMessage($this->pokemon->getMessages());
        }
        // バトル終了判定用メッセージの格納
        $this->setMessage(' ', 'battle-end');
        return true;
    }else{
        // ひんし状態ではない
        return false;
    }
}

 

相手ポケモンをひんし状態にした後、取得経験値をcalExpというメソッドを使い算出し、setExpを使って勝利ポケモンに経験値を与えています。

 

ではcalExpという経験値計算用のメソッドをバトルコントローラー内に作成しましょう。

 

バトルコントローラー(Classes/Controller/BattleController.php
/**
* 経験値の計算
* (EXP × LM^2.5 + 1)
*
* @var EXP 倒されたポケモンのレベル × 倒されたポケモンの基礎経験値 ÷ 5
* @var LM レベル補正 (2L + 10) / (L + Lp + 10)
* @var L 倒されたポケモン($lose)のレベル
* @var Lp 倒したポケモン($win)のレベル
*
* @param object $win Pokemon
* @param object $lose Pokemon
* @return integer
*/
protected function calExp($win, $lose)
{
    // EXP
    $exp = $lose->getLevel() * $lose->getBaseExp() / 5;
    // レベル補正
    $lm = (2 * $lose->getLevel() + 10) / ($lose->getLevel() + $win->getLevel() + 10);
    // 経験値の計算結果を整数(切り捨て)で返却
    return (int)($exp * $lm ** 2.5 + 1);
}

 

ダメージ計算同様、作成した式に対して必要数値を当てはめていき、算出結果を整数として返却しています。これでバトルポケモンに合わせた経験値計算が実装できました。後ほど出力結果をみてみましょう。

  

経験値バーの実装

バトル中の経験値計算が完成したので、第2世代から採用された「経験値バー」についても作成していきましょう。バーのデザインは、HP同様にBootstrapのプログレスを採用します。

 

割合の算出

 経験値バーの算出に必要なのは「現在のレベルで必要になる経験値をどれだけ満たしているか」です。

 

Get格納トレイト(/Traits/Pokemon/GetTrait.php
/**
* 現在のレベルから次のレベルまでの経験値達成率
* @return integer
*/
public function getPerCompNexExp()
{
    if($this->level >= 100){
        return 100;
    }
    // 現在のレベルので経験値達成率を%の数値で返却(int)
    // 現在のレベルで必要な経験値の超過分(余り)
    $surplus = $this->exp - ($this->level ** 3);
    // 現在のレベルから次のレベルに必要な経験値量
    $need = ((($this->level + 1) ** 3) - ($this->level ** 3));
    // 割合計算(%の数値で返却)
    return $surplus / $need * 100;
}

 

大分考えたのですが、上記式を採用しました。

まず、レベルが100であれば算出は不要のため100(%)を返却します。0でも構いません。

 

まず、現在の達成率(現在のレベルで必要な経験値の超過分)を求めるために

現在の取得経験値 ― 現在のレベルで必要な経験値

の値を$surplusに格納しました。

 次に、割合の分母となる値(現在のレベルから次のレベルまでに必要な経験値)を求めるために、

次のレベルに必要な経験値 ― 現在のレベルに必要な経験値

の値を$needに格納しました。

 最後に割合を求める式として

$surplus / $need * 100

の値を返却しています。

 

べき乗の加算量を求める簡単な式があれば、お問い合わせで教えてください。高校でまともに数学を勉強していない自分には、現状これが精一杯でした。 

あとは、算出した割合をプログレスのwidthにセットするだけです。最大値は100、最小値には0、現在の値にはwidthと同じ値をセットしておきます。

 

バトル画面(/battle.php
<?php # 経験値バー ?>
<div class="progress" style="height:4px;">
    <div class="progress-bar bg-primary" role="progressbar" style="width:<?=$pokemon->getPerCompNexExp()?>%;" aria-valuenow="<?=$pokemon->getPerCompNexExp()?>" aria-valuemin="0" aria-valuemax="100"></div>
</div>

 

プログレスの設置場所はHPバーの下です。

それでは経験値の獲得と合わせて出力結果を確認してみましょう。

 

 

 

 

 

無事ポケモンを倒すことで経験値を取得できました。これでバトルによる経験値獲得処理は完成です。

  

お詫び(不備修正)

第29回にもなると、そこそこ不備も見つかったので修正報告がてらお詫びします。おそらく今後も不備修正はボロボロ出てくることになると思われるので、その辺りはプログラミングの醍醐味学習の一環としてお楽しみ下さい。とりあえずサーセン。

  

数値(計算結果)について

まず、ガバガバプログラミング言語のPHPを使っている関係上、数値の処理でそこそこ不備が見つかっています。例えば、HPで小数点込みの値が表示されるなどです。この辺りは引数で受け取る際に型指定するなどを入念にしていくことで対策します。

 

例:引数の型指定 
/**
* 残りHPの計算
*
* @param string $param
* @param integer $val (default:0)
* @return integer
*/
public function calRemainingHp($param, int $val=0)

 

おかしなレベルアップ

必要経験値を満たせていないのにレベルアップしてしまうということに今回気づきました。確認したところ、経験値の取得処理で「次のレベルまでに必要な経験値」を取得経験値加算後に行なっていたことが原因でした。

 

Set格納トレイト(/Traits/Pokemon/SetTrait.php
/**
* 経験値をセット(取得)する
* @param integer $exp
* @return object
*/
public function setExp($exp)
{
    if(!is_numeric($exp)){
        // 入力値のチェック
        $this->setMessage('数値を入力してください', 'error');
        return $this;
    }
    // 次のレベルに必要な経験値を取得
    $next_exp = $this->getReqLevelUpExp();
    // 経験値を加算
    $this->exp += (int)$exp;
    $this->setMessage($this->getNickname().'は経験値を'.$exp.'手に入れた!', 'success');
    // レベル上限の確認
    if($this->level >= 100){
        return $this;
    }
    if($next_exp <= $exp){
        $levelup = true;
        /**
        * 次のレベルに必要な経験値を超えている場合
        */
        $this->actionLevelUp();
        while($this->getReqLevelUpExp() < 0){
            $this->actionLevelUp();
        }
    }
    // 進化判定
    if(isset($levelup) && isset($this->evolve_level) && ($this->evolve_level <= $this->level)){
        return $this->evolve();
    }else{
        return $this;
    }
}

 

その他諸々修正しています。

 

攻撃の流れ

攻撃の流れについてですが、今までattackメソッド完了後にひんし判定を行なっていましたが、その場合状態異常になってからひんしになるということが発生していました。

例:ひのこ→やけどした→倒れた

 

なので、再度見直してattack処理の中でひんし判定までの一連の流れを行うように修正しました。

 

攻撃用トレイト(/Trait/Battle/AttackTrait.php
/**
* 攻撃
* (攻撃→ダメージ計算→ひんし判定)
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param object $move
* @return boolean (true:相手を戦闘不能にした)
*/
protected function attack($atk_pokemon, $def_pokemon, $move)
{
    // 攻撃メッセージを格納
    $this->setMessage($atk_pokemon->getPrefixName().'は'.$move->getName().'を使った!');
    // タイプ相性チェック
    $type_comp_msg = $this->checkTypeCompatibility($move->getType(), $def_pokemon->getTypes());
    // 「こうかがない」の判定(命中率と威力がnullではなく、タイプ相性補正が0の場合)
    if(!is_null($move->getAccuracy()) && !is_null($move->getPower()) && ($this->m === 0)){
        // こうかがない
        $this->setMessage($def_pokemon->getPrefixName().'には効果が無いみたいだ');
        return;
    }
    // 命中判定
    $hit = $this->checkHit($move->getAccuracy());
    if(!$hit){
        // 攻撃失敗
        $this->setMessage('しかし攻撃は外れた!');
        return;
    }
    // 必要ステータスの取得
    $stats = $this->getStats($move->getSpecies(), $atk_pokemon, $def_pokemon);
    // ダメージ計算
    if($move->getSpecies() !== 'status'){
        /**
        * 物理,特殊技
        */
        if(!is_null($move->getPower())){
            // 急所判定(固定ダメージ技は判定不要)
            $critical = $this->checkCritical($move->getCritical());
            if($critical){
                $this->setMessage('急所に当たった!');
            }
        }
        // 乱数補正値の計算
        $this->calRandNum();
        // タイプ一致補正の計算
        $this->calMatchType($move->getType(), $atk_pokemon->getTypes());
        // ダメージ計算
        $damage = $this->calDamage(
            $atk_pokemon->getLevel(),   # 攻撃ポケモンのレベル
            $stats['a'],                # 攻撃ポケモンの攻撃値
            $stats['d'],                # 防御ポケモンの防御値
            $move->getPower(),          # 技の威力
            $this->m,                   # 補正値
        );
        // やけど補正
        if(($move->getSpecies() === 'physical') && ($atk_pokemon->getSa() === 'SaBurn')){
            // 物理且つやけど状態ならダメージを半減
            $damage *= 0.5;
        }
        // タイプ相性のメッセージを返却
        $this->setMessage($type_comp_msg);
    }else{
        /**
        * 変化技
        */
        $damage = 0;
    }
    // ダメージ計算
    $def_pokemon->calRemainingHp('sub', $damage);
    // ひんしチェック
    if($this->checkFainting($def_pokemon)){
        // 相手を「ひんし」状態にした
        return true;
    }else{
        // 相手は「ひんし」状態ではない
        // 追加効果
        $move->effects($atk_pokemon, $def_pokemon);
        // 追加効果のメッセージをセット
        $this->setMessage($move->getMessages());
        return false;
    }
}

 

ただ、反動技(とっしん等)で両者が力尽きた場合などが加味されておらず、この辺りは近々まとめて行く予定です。一旦は応急処置として考えておいてください。

 

いきあたりばったりで開発を始めるとこういうことになるので注意しましょう。(戒め)

 

まとめ

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

今回のPHPポケモンでは「バトルによる経験値獲得」について、経験値の計算方法などをご紹介しました。

ゲームづくりに興味がある人プログラミング学習に取り組んでいる人は、ぜひ参考にしてくださいね。

 

注目の記事

甘い誘惑に気をつけよう「わからないことへの対処法」
雑記
甘い誘惑に気をつけよう「わからないことへの対処法」

  最近は開発記事が多めだったので、今回は気分転換も込めてみんな大好きコラムのコーナーです。開発疲れという理由ももちろんありますが、久々に考えを書き綴りたくなったというのが本音です。   今回のテーマは「甘い誘惑に気をつけよう」です。高校生ぐらいまではあまり縁がなかったことでも、大学生・社会...

Laravelで生成したCookie情報をjQueryで取得する方法【JavaScript】
プログラミング
ajax,api,JavaScript,jQuery,Laravel,PHP
Laravelで生成したCookie情報をjQueryで取得する方法【JavaScript】

  今回はLaravel開発備忘録です。 ajaxでapi認証してviewに記述したhtmlデータを取得するために、cookieを使ったapi_tokenの受け渡し手順をまとめてみました。   Laravelを使った開発をしている人は、ぜひ参考にしてくださいね。     Laravel側の処理   まずはcookieにデータをセットする必要があり...

SEOで強いURLとは?安易に決めると危険だった!「よく分かるSEO対策」パーマリンク編
SEO対策
WordPress,ブログ
SEOで強いURLとは?安易に決めると危険だった!「よく分かるSEO対策」パーマリンク編

  パーマリンクの設定ってSEO対策に関係あるの?   意外と気になっているポイントではないでしょうか。実は、URLとSEO対策には全くの無関係ではなく、安易に決めてしまうとせっかく積み上げた資産的価値を台無しにしてしまうかも知れない危険なポイントでもあるのです。   今回は、ブログの収益化を目指...

PHPポケモン「オートローダー編」16
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「オートローダー編」16

  今までは技やタイプを一括requireという荒業で対応していましたが、フシギダネ系列の技を実装した際に「こんなん全部読み込んでられるか!」と流石になったので、簡易ながらオートローダーを実装していきます。 そして、実装したらしたで色々と問題も浮かび上がってきたので、このあたりは回を進めて行きなが...

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

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

SNS拡散力アップ!PHPでOG画像付きリッチURLを自作する方法【Curl → Opengraph】
プログラミング
PHP,WordPress
SNS拡散力アップ!PHPでOG画像付きリッチURLを自作する方法【Curl → Opengraph】

  ブログやHPにサイトのURLをただ貼り付けても、どんなページなのか一目でわからないのでなかなか思うようにアクセスに繋がりません。 ですが、毎回のようにURL先の画像を準備したり、説明文を設定するのも大変ですし、ページタイトルや画像が差し替わってしまうことも考えられます。   今回はそういった手間...

放物線アニメーション編 PHPポケモン 81
プログラミング
PHP,PHPポケモン,ポケモン
放物線アニメーション編 PHPポケモン 81

ボールアニメーション 前回までに作成した捕獲判定処理を使って、ボールのアニメーションを作成します。 捕獲演出は以下の通りです。  味方側から相手に向かってボールを投げる 相手ポケモンの前でボールを開く 捕獲判定で算出した揺れ回数分ボールを揺らす   捕まえた際は、ボールの揺れをストップ...

PHPポケモン「UI(Bootstrap4の導入)編」コード配布・デモ有り 13
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「UI(Bootstrap4の導入)編」コード配布・デモ有り 13

  第13回のPHPポケモンは「UI編」ということで、CSSの大人気フレームワークBootstrapさんにお手伝いいただきます。 また、前回実装したレスポンス機能により進化のアクションに一部不具合が出ていたので、このあたりも合わせて修正をしながら進めていきましょう。   Bootstrapを導入するにあたり、今までin...

カテゴリ

SEO対策 イベント デザイン ネットワーク ビジネスモデル フリーランス プログラミング マーケティング ライティング 動画編集 雑記

タグ

5G Adobe AfterEffects AI ajax amazon Animate api artisan atom Automator AWS Bluetooth CSS CVR description EC-CUBE4 ECショップ ESLint Facebook feedly foreach fortify function Google Google AdSense Honeycode htaccess HTML IEEE 802.11ax Illustrator Instagram IoT JavaScript jetstream 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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 認証 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力