プログラミング

ゲームバランス調整編 DateTimeクラスの活用 PHPポケモン 101

PHP PHPポケモン ポケモン
ゲームバランス調整編 DateTimeクラスの活用 PHPポケモン 101

ゲームバランスの調整

まだまだ開発途中のPHPポケモンはゲームバランスの調整がほとんどされていません。現段階では機能も揃っていない関係上、完全なゲームバランスを求めて行くことにはあまり意味がありませんが、今後調整するための役割として機能自体は作成しておこうというのが今回の目的です。

 

トレーナー戦の上限回数

本番環境にはまだ未反映ですが、トレーナー戦が今回調整を加える対象となります。1日に何度も同じトレーナーと戦えてしまうと、お金を短時間に荒稼ぎしたり、経験値を狙って取得することが出来てしまいます。なので、1日に戦える回数の上限を設けることで調整していきましょう。

 

トレーナー情報の保管

まずはプレイヤークラスに対して、戦ったトレーナー情報を保管、操作するためのメソッドを用意しましょう。いくつかあるので、専用トレイトを用意します。

 

プレイヤー用トレーナー操作トレイト(/App/Traits/Class/Player/ClassPlayerTrainerTrait.php
<?php
/**==================================================================
* トレーナー情報(勝利したトレーナー情報を格納)
==================================================================**/
trait ClassPlayerTrainerTrait
{
 
    /**
    * トレーナー
    * @var array
    */
    protected $trainers = [];
 
    /**
    * トレーナーと戦える回数
    * @var integer
    */
    protected $trainer_times = 5;
 
    /**
    * 対象トレーナーと戦ったかどうかの確認
    * @param category:string
    * @param id:string
    * @return boolean
    */
    public function isTrainer(string $category, string $id): bool
    {
        return in_array($id, $this->trainers[$category], true);
    }
 
    /**
    * 対象トレーナーと戦えるかの確認
    * @param category:string
    * @return boolean
    */
    public function isFightTrainer(string $category): bool
    {
        return $this->getTrainerCount($category) < $this->trainer_times;
    }
 
    /**
    * 戦ったトレーナー名一覧を取得
    * @param category:string
    * @return array
    */
    public function getTrainers(string $category): array
    {
        return $this->trainers[$category] ?? [];
    }
 
    /**
    * 戦った全トレーナー名一覧を取得
    * @param category:string
    * @return array
    */
    public function getTrainersAll(): array
    {
        return $this->trainers ?? [];
    }
 
    /**
    * 戦ったトレーナー数を取得
    * @param category:string
    * @return integer
    */
    public function getTrainerCount(string $category): int
    {
        return count($this->trainers[$category] ?? []);
    }
 
    /**
    * 対象トレーナーと戦える残り回数を取得
    * @param category:string
    * @return integer
    */
    public function getRemainingTrainerCount(string $category): int
    {
        return $this->trainer_times - $this->getTrainerCount($category);
    }
 
    /**
    * 戦ったトレーナー情報の格納
    * @param trainer:object::Trainer
    * @return void
    */
    public function setTrainer(Trainer $trainer): void
    {
        $this->trainers[$trainer->getCategory()][] = $trainer->getId();
    }
 
}

 

トレーナー情報はカテゴリごとに配列で保管、戦える上限回数をプロパティで設定しました。上限回数を定数・静的変数ではない理由の1つとして、プレイヤーレベルに合わせてこの回数を変更したり、ある条件を満たすことで回数を増やすなどの変動があることでゲーム性がより広がると考えたからです。そういった機能を想定しないのであれば、上限回数は定数または静的変数で用意するのがメモリ消費を抑えられるため良いでしょう。

 

トレーナー情報を判別するために、トレーナーIDという値を1つ追加しました。これは、戦ったトレーナー情報配列が格納されたファイル名(拡張子抜き)の値となります。こちらはトレーナー情報配列に追加しています。

 

トレーナー情報:園児 シホ(/Storage/Database/Trainer/preschooler/shiho.php
<?php
return [
    'id' => basename(__FILE__, '.php'),
    'name' => 'シホ',
    'money' => 50,
    'lines' => [
        'lose' => '負けちゃったー',
        'win' => 'やったー!',
    ],
    'party' => [
        [   # 1匹目(ポッポ)
            'class' => 'Poppo',
            'level' => 3,
        ],
        [   # 2匹目(ゼニガメ)
            'class' => 'Zenigame',
            'level' => 3,
        ],
    ],
];

 

ファイル読み込み時に「ゆびをふる」と同じ要領でファイル名を取得する方法もありましたが、こちらの方が処理自体は簡単で間違いが起こりにくいため、ファイルに個別で設定する方式をとりました。これで、もし同じトレーナーとの戦いを制限するとなった場合でも、ファイル名による判別が容易になります。

 

トレーナーとの戦闘記録は、勝敗に関わらず格納します。なので、トレーナー戦開始時にプレイヤー情報へセットしましょう。

 

トレーナー戦開始サービス(/App/Services/Battle/StartTrainerService.php
/**
* @return void
*/
public function execute()
{
    // 味方の選出
    $this->electionFriend();
    // トレーナーの情報の作成とポケモンの選出
    $this->createTrainer();
    // トレーナーとの戦闘記録をカウント
    player()->setTrainer(trainer());

 

これでトレーナー情報の格納処理は完成です。 上限回数をチェックするために、コントローラーとフロント側でも回数を判定するための処理を追加しておきましょう。  

 

ホームコントローラー用トレイト(/App/Traits/Controller/BattleControllerTrait.php)

/**
* バトル開始(トレーナー)の検証
* @return boolean
*/
protected function validationBattleTrainer(): bool
{
    // バトル開始可能な状態かを確認
    if(!player()->isFightParty()){
        response()->setMessage('バトルに参加できるポケモンがいないので、戦えません');
        return false;
    }
    // トレーナー情報・プレイヤーレベルの確認
    if(
        empty(config('trainer.'.request('trainer'))) ||
        player()->getLevel() < config('trainer.'.request('trainer').'.level')
    ){
        response()->setMessage('プレイヤーレベルが足りていません');
        return false;
    }
    // 残り回数の確認
    if(!player()->isFightTrainer(request('trainer'))){
        response()->setMessage('本日のバトル上限回数を越えています');
        return false;
    }
    return true;
}

 

トレーナーモーダル(/Resources/Partials/Home/Modals/Trainer.php
<div class="modal-body">
    <figure class="text-center position-relative" style="height:120px;">
        <img src="" id="select-trainer-image" class="center-image d-soft-none">
    </figure>
    <div class="mb-2 p-1 text-center small bg-light rounded-sm">
        <p class="text-muted mb-0" data-trainer="default">トレーナーを選択してください</p>
        <?php foreach(config('trainer') as $key => $trainer): ?>
            <?php if(player()->isFightTrainer($key)): ?>
                <p class="text-success d-soft-none mb-0" data-trainer="<?=$key?>" data-fight="true">本日は残り<?=player()->getRemainingTrainerCount($key)?>回戦えます</p>
            <?php else: ?>
                <p class="text-danger d-soft-none mb-0" data-trainer="<?=$key?>" data-fight="false">本日はもう戦えません</p>
            <?php endif; ?>
        <?php endforeach; ?>
    </div>
    <div class="form-group">
        <select class="form-control form-control-sm" name="trainer">
            <option value="">-- 選択してください --</option>
            <?php foreach(config('trainer') as $key => $trainer): ?>
                <option value="<?=$key?>" <?php if($trainer['level'] > player()->getLevel()) echo 'disabled'; ?>><?=$trainer['name']?>(必要レベル:<?=$trainer['level']?>)</option>
            <?php endforeach; ?>
        </select>
    </div>
    <ul class="mb-0 pl-3 small">
        <li class="text-muted">プレイヤーレベルが足りていないトレーナーとは戦えません</li>
        <li class="text-muted">プレイヤーレベルは「メニュー」>「プレイヤー名」で確認できます</li>
    </ul>
</div><!-- Modal body -->

 

ホーム画面用JS/Public/Assets/js/Home/home.js
/**
* トレーナー選択
* @function on:change
* @return void
**/
var selectTrainerInit = function(){
    $('form#select-trainer-form [name="trainer"]').on('change', function(){
        // テキストを全て非表示
        $('p[data-trainer]').hide();
        // 必要情報の取得
        var trainer = $(this).val();
        var form = $('form#select-trainer-form');
        var image = $('#select-trainer-image');
        var text = $('p[data-trainer="' + trainer + '"]');
        if(trainer){
            // 画像とテキストを表示
            image.attr('src', '/Assets/img/npc/front/' + trainer + '.gif')
            .show();
            text.show();
            // 戦える状態ならボタンを有効化
            form.find('input[type="submit"]').prop('disabled', !text.data('fight'));
        }else{
            // デフォルトテキストを表示
            $('p[data-trainer="default"]').show();
            // 画像を非表示にしてフォームを無効化
            image.attr('src', '')
            .hide();
            form.find('input[type="submit"]').prop('disabled', true);
        }
    });
}

 

 

戦った回数をカウントすることができました。これでトレーナー情報の記録処理が完成です。

 

日付による更新処理

トレーナー情報の格納処理が実装できましたが、保管したままだとそれ以降は戦えなくなってしまいます。なので、日付を確認することでプレイヤー情報を更新するという処理を加えていきましょう。

  

時間の記録

まずは時間の記録についてです。こちらは、セーブ時間の記録と同じ要領でDateTimeクラスを使って保管していきます。記録のタイミングはいくつかありますが、毎回更新していても余計な処理が動くことになってしまうため、ホーム画面の読み込み時のみ更新処理を行うようにしましょう。

 

ホームコントローラー(/App/Controllers/Home/HomeController.php
// ホーム用コントローラー
class HomeController extends Controller
{
 
    use HomeControllerTrait;
 
    /**
    * @return void
    */
    public function __construct()
    {
        // 親コンストラクタの呼び出し
        parent::__construct();
        // 分岐処理
        $this->branch();
        // プレイヤー情報の最終更新日時を更新
        player()->setUpdatedAt();
    }

 

コンストラクタの分岐直後にプレイヤー情報を更新しました。デストラクタで行う場合はトースターやメッセージの格納タイミングを逃してしまうため、もし必要であればコンストラクタの処理内で実行する必要があります。

 

setUpdatedAtメソッドの内容は次項目でコード解説をします。

 

差分の確認

更新時刻の保存時に差分を確認、もし日付をまたいでいればトレーナー情報をリセットするという処理を加えます。

なので、setUpdatedAt内に分岐を追加しましょう。

 

プレイヤークラス(/Classes/Player.php
/**
* 最終更新日時の格納
* @return void
*/
public function setUpdatedAt(): void
{
    // 現在の日時を取得
    $now = new DateTime;
    // 更新前と比較
    if($this->updated_at){
        $old = new DateTime($this->updated_at);
        $old->setTime(0,0,0);
        // 差分が返ってくれば日付変更処理
        if($now->diff($old, true)->format('%a')){
            $this->changeTheDate();
            response()->setToastr('info', '残りトレーナー戦の回数がリセットされました');
        }
    }
    $this->updated_at = $now->format('Y-m-d H:i:s');
}
 
/**
* 日付変更時の処理
* @return void
*/
private function changeTheDate()
{
    // トレーナーとの対戦記録を初期化
    $this->trainers = [];
}

 

updated_at更新前に、旧時刻を取得して時刻に00:00:00をセット、diffを使って日付の差分を算出しています。差分が1日でもあればchangeTheDateメソッドを呼び出してトレーナー情報を初期化、トースターによる通知をしています。

もし日付の変更で初期化したいプロパティ等があれば、このメソッドにまとめることで一括処理が可能です。あくまで親メソッドであるsetUpdatedAtはホーム画面でのみ呼び出しているため、バトル中に初期化処理が呼び出されることはありません。

 

まとめ

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

今回のPHPポケモンでは「ゲームバランスの調整編」としてDateTimeクラスを使った「トレーナーバトル上限回数の実装方法」をご紹介しました。

ゲームづくりやプログラミングに興味がある方は、ぜひ参考にしてみてくださいね。

 

注目の記事

ブログ運営者のための「よくわかるSEO対策」フィード編
SEO対策
atom,feedly,rss,WebSub,WordPress,xml,フィード
ブログ運営者のための「よくわかるSEO対策」フィード編

  アメブロやFC2など優れたブログサービスが存在している中、好きなデザイン、こだわった機能を求めてWordPressを選択される方も多いですね。 また、コーポレートサイトにWordPressを用いる方もいるでしょう。ただしSEO対策がしっかりとされていなければ、提供されているサービスに太刀打ちできません。  もちろん...

動画編集に役立つ基本的な考え方【Adobe AfterEffects】
動画編集
Adobe,AfterEffects,PremierePro,YouTube
動画編集に役立つ基本的な考え方【Adobe AfterEffects】

  YouTubeの人気に合わせて、動画編集の需要も高まってきましたが、その大変さから挫折してしまう人も続出しています。 動画編集は奥が深く、技術的な部分に関してはプロのクリエイターであっても自分がよく使うような一部しか把握していないのが普通であり、調べても該当する情報が出てきにくいということもあ...

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

ポケモン図鑑とは ポケモンに出会ったり、仲間にしたりすると、ポケモン図鑑のデータがどんどんと埋まっていきます。PHPポケモンでもこの仕組みを実現させるために、ポケモン図鑑を作成していきましょう。   クラスによる管理 ポケモン図鑑はクラス管理をしていきます。プレイヤー1人に対して1つのポケモン図...

挫折してしまう人に共通する3つの要因〜解決方法を紹介します〜
雑記
挫折してしまう人に共通する3つの要因〜解決方法を紹介します〜

  仕事が上手くいかない 思ったように学習成果がでない   こういった理由で挫折してしまう人は、意外にも考え方や行動が共通しています。 それが何かを知り、考え方や環境、対応方法を少し変えるだけで、劇的に余裕が生まれて自己肯定ができるようになります。その結果、強い人になれるのです。   今...

ビジネス系次世代ブログ!?無料で企画書が読める「机上の空論」とは
ビジネスモデル
ビジネス系次世代ブログ!?無料で企画書が読める「机上の空論」とは

  この記事は、私の考えたビジネスモデルを紹介するコーナーです。考えるだけで辞めたものや、コストやリスクを考えて断念したもの、そこまでニーズがないと判断したものなど様々なので、読んだ方は自分なりの見解や根拠を踏まえて判断したり、各自ビジネスの参考資料としてご活用ください。   このブ...

フレンドリィショップ編 開店準備 PHPポケモン 75
プログラミング
PHP,PHPポケモン,ポケモン
フレンドリィショップ編 開店準備 PHPポケモン 75

アイテム(どうぐ) PHPポケモンもバトル機能が一通り揃ってきたので、新機能として「アイテム」の実装に取り掛かります。 アイテムにも色々ありますが、技やポケモンのクラス同様に、初代で登場したアイテムから順番に取り揃えていきましょう。   キズぐすりの実装 ポケモンのどうぐ、一番手は「キズぐすり」...

TwitterでYouTubeのリンク付きサムネイルを表示させる方法【超実践的Webプログラミング活用法】
プログラミング
HTML,JavaScript,PHP,Twitter,YouTube
TwitterでYouTubeのリンク付きサムネイルを表示させる方法【超実践的Webプログラミング活用法】

  Twitter(ツイッター)をブログや商品、イベントの宣伝目的で使用している人は多いです。そして、そのためのマーケティング方法や戦略は数多く練られています。 今回は、その中でもYouTubeの告知をするために特化させた内容をまとめました。   一般的な方法と、プログラミングの知識(HTMLやJavascript等)があ...

PHPポケモン「状態異常編〜まひ〜」23
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「状態異常編〜まひ〜」23

  今回のPHPポケモンでは「状態異常」を導入します。バトルシステムにも関係してくる部分ですが、それ以外にも影響を与える部分が多く、且つ非常に判定がややこしく、作り込む必要があったため1つずつ片付けていきます。   状態異常の実装  ポケモンの除隊異常に該当するものは以下の7通りです。 ...

カテゴリ

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