プログラミング

HPバーアニメーション前編 サーバー側の対応 PHPポケモン 43

PHP PHPポケモン ポケモン
HPバーアニメーション前編 サーバー側の対応 PHPポケモン 43

動きのあるHPバーづくり

それではデモ公開に先立ち、HPバーの作り込みをしていきたいと思います。

現在のPHPポケモンは、ダメージ計算などが終わった結果をすべて返却しているため、技選択をして次の画面に移行すると、HPが減った状態でスタートしていました。これでは、どの技でどれぐらいのダメージを与え、状態変化等でどのぐらいダメージの変化があったのかなどはわかりません

今回はそこもゲームと同じように再現するため、HPバーの動きを再現していきます。

 

ちなみにですが、動き関係についてはもちろんJavaScript(jQuery)をガッツリ使用します。

 

サーバー側の処理

まず注意すべき点が1つあります。それは、どこまでをフロント側(Javascript)、どこまでをサーバ側(PHP)で担当するかということです。

あくまでPHPポケモンはWEBアプリケーションのため、DOMを直接操作されてしまうと実際には起こり得ない計算結果を発生させてしまう可能性があります。そうしないためにも、ダメージ計算などはすべてサーバー側で行い、フロントではあくまで画面上の変動は演出にとどめて置かなければなりません。

・残りHP20の相手に10ダメージを与える

PHPで今まで通りダメージ計算を行い、相手の残りHPを10にする

Javascriptで20から10になる演出をさせる(残りHPをここでは計算しない)

 

メッセージIDの発行

ポケモンがHPに対して動きを持たせるタイミングは、メッセージに合わせてあります。以下2つの例をみてみましょう。

 

攻撃技を使用した場合

「ピカチュウはでんきショックを使った」

→ でんきショックのダメージ分HPバー減少

 

状態異常(毒)の場合

毒ダメージ分HPバー減少

→「ピカチュウは毒のダメージを受けている」

 

このように、状況によってメッセージのタイミングは異なりますが、メッセージの前後で行われ、ボタンを押すことで順番にアクションが進んでいきます。なので、メッセージに対してIDをもたせ、そこにパラメーターを設定することで、どのメッセージでどのアクションを実行させればよいかを判別させます。そのために、ResponseTraitに作成したメッセージとレスポンスの2つを組み合わせて作成します。

 

アクション判定方法
  1. メッセージにIDを持たせる
  2. そのIDに該当するパラメーターをレスポンスへ格納
  3. メッセージがクリックされた時に、レスポンスに格納されたパラメーター通りアクションを実行

 

それでは、メッセージIDを生成するための仕組みを見ていきましょう。

 

レスポンストレイト(/App/Traits/ResponseTrait.php
/**
* メッセージIDの発行
*
* @return string
*/
public function issueMsgId()
{
    // IDを生成
    $id = 'msg'.substr(bin2hex(random_bytes(16)), 0, 16);
    // ユニークになるようにチェック
    while(in_array($id, $_SESSION['__message_ids'] ?? [], true)){
        $id = 'msg'.substr(bin2hex(random_bytes(16)), 0, 16);
    }
    $_SESSION['__message_ids'][] = $id;
    return $id;
}

 

重複をさせるために、ランダムでIDを生成後にセッションに格納しています。格納前に同値があれば、whileで再生成するという仕組みです。

ここでプロパティではなくセッションを使用している理由は「どのインスタンス内でIDが生成されるかわからない」からです。通常はサービス内で行われていますが、技の反動であればポケモンのインスタンス内や技のインスタンス内など様々です。これらを取り出してまとめてコントローラーのインスタンスにセットした時に、プロパティ内で一意にしていれば重複する可能性が出てきてしまいます。

16桁の文字数列で1桁程度の数であれば、重複する可能性はほぼ有りませんが100%ではありません。これを100%とするためにセッションを活用しました。

 

アクションの作成

IDの作成が出来たので、次にメッセージへのID格納と、レスポンスへのパラメーターの格納を行います。そのために、レスポンストレイトへいくつかメソッドを追加しておきましょう。

 

レスポンストレイト(/App/Traits/ResponseTrait.php
/**
* アニメーション用の自動メッセージの格納
*
* @param mixed $param
* @return array
*/
public function setAutoMessage($param)
{
    $this->msgs[] = ['', $param, 'auto'];
}
 
/**
* 空メッセージの格納
*
* @param string $param
* @return array
*/
public function setEmptyMessage($param)
{
    $this->msgs[] = ['', $param, ''];
}

 

メッセージの格納(setMessage)では空を弾くようにしています。しかし、状態異常のダメージを行う際には空メッセージを間に挟んでいる方が簡単に作成できるため、個別に作成しました。自動メッセージの格納は次回詳しく説明しますが、アニメーションに合わせて自動送りをしたいメッセージ用として準備しています。

 

これでアニメーションを起こす際に必要な値を格納する準備が整いました。

しかし、アニメーションでHPの減算・加算を行うためには、敵味方お互いのターン最初のHP状態を残して置かなければなりません。現在はgetRemainingHpで取得しているので、計算後の値は取得できますが、画面以降すればその値はダメージを逆算しなければ算出することが出来ないからです。

なので、前画面からのポケモン情報を引き継いだタイミングで、コントローラーに対してスタート時のHP状況を格納するために、プロパティを用意しておきましょう。

 

バトルコントローラー(/App/Controller/Battle/BattleController.php
<?php
$root_path = __DIR__.'/../../..';
require_once($root_path.'/App/Controllers/Controller.php');
// サービス
require_once($root_path.'/App/Services/Battle/StartService.php');
require_once($root_path.'/App/Services/Battle/RunService.php');
require_once($root_path.'/App/Services/Battle/FightService.php');
// トレイト
require_once($root_path.'/App/Traits/Controller/BattleControllerTrait.php');
 
// バトル用コントローラー
class BattleController extends Controller
{
 
--省略
 
    /**
    * 前ターンのHP
    * @var array
    */
    protected $before_remaining_hp = [
        'friend' => 0,
        'enemy' => 0,
    ];

 

次に格納方法です。計算が行われる前でなければなりませんので、両ポケモンも引き継ぎ処理後に、getRemainingHpの結果を格納しましょう。

 

バトルコントローラー用トレイト(/App/Traits/Controller/BattleControllerTrait.php
/**
 * バトルコントローラー用トレイト
 */
trait BattleControllerTrait
{
 
    /**
    * ポケモン情報の引き継ぎ
    *
    * @param Pokemon::export:array $pokemon
    * @return void
    */
    protected function takeOverPokemon($pokemon)
    {
        $class = $pokemon['class_name'];
        $this->pokemon = new $class($pokemon);
        // ランク(バトルステータス)の引き継ぎ
        if(isset($_SESSION['__data']['rank'])){
            $this->pokemon
            ->setRank($_SESSION['__data']['rank']['pokemon']);
        }
        // 状態変化の引き継ぎ
        if(isset($_SESSION['__data']['sc'])){
            $this->pokemon
            ->setSc($_SESSION['__data']['sc']['pokemon']);
        }
        // 前ターンのHP(現在の残りHP)をプロパティに格納
        $this->before_remaining_hp['friend'] = $this->pokemon
        ->getRemainingHp();
    }
 
    /**
    * 相手ポケモンの引き継ぎ
    *
    * @param Pokemon::export:array $enemy
    * @return void
    */
    protected function takeOverEnemy($enemy)
    {
        if(!empty($enemy)){
            $this->enemy = new $enemy['class_name']($enemy);
            // 前ターンのHP(現在の残りHP)をプロパティに格納
            $this->before_remaining_hp['enemy'] = $this->enemy
            ->getRemainingHp();
        }
        // ランク(バトルステータス)の引き継ぎ
        if(isset($_SESSION['__data']['rank'])){
            $this->enemy
            ->setRank($_SESSION['__data']['rank']['enemy']);
        }
        // 状態変化の引き継ぎ
        if(isset($_SESSION['__data']['sc'])){
            $this->enemy
            ->setSc($_SESSION['__data']['sc']['enemy']);
        }
    }

 

自ポケモンの場合は通常の引継ぎ処理後に一律で取得可能ですが、相手ポケモンの場合は開始ターン引き継ぎ時点ではポケモンのインスタンスが存在しません。なので、引き継ぎができれば取得して格納するようにしましょう。

 

バトルコントローラー(/App/Controller/Battle/BattleController.php
/**
* アクションに合わせた分岐
* @return void
*/
private function branch()
{
    try {
        // アクション分岐
        switch ($this->request('action')) {
            /******************************************
            * 開始
            */
            case 'battle':
            // サービス実行
            $service = new StartService;
            $service->execute();
            // 実行結果
            $this->enemy = $service->getResponse('enemy');
            // 前ターンのHP(現在の残りHP)をプロパティに格納
            $this->before_remaining_hp['enemy'] = $this->enemy
            ->getRemainingHp();
            $this->setMessage($service->getMessages());
            $this->setResponse($service->getResponses());
            break;

 

相手ポケモンの場合、開始ターンは引き継ぎ処理で格納出来ていないので、サービス実行後にインスタンスが生成された段階で格納します。

 最後に、開始時のHPを取得するためのメソッドを用意しておきましょう。

 

バトルコントローラー用トレイト(/App/Traits/Controller/BattleControllerTrait.php
/**
* 前のターンの残りHPを取得(HPバー用)
*
* @param Pokemon:object $pokemon
* @param string $param (per)
* @return numeric
*/
public function getBeforeRemainingHp($pokemon, $param='')
{
    if($param === 'per'){
        // 最大HPとの比率を%で取得(数値で返却)
        return $this->before_remaining_hp[$pokemon->getPosition()] / $pokemon->getStats('HP') * 100;
    }else{
        return $this->before_remaining_hp[$pokemon->getPosition()];
    }
}

 

こちらのメソッドが、HPバーの初期値で使用する値の算出用となります。

 

画面開始時の初期値の取得方法が整ったので、次は計算(アニメーション)用のパラメーター返却です。作成したレスポンス用メソッドを使って実装していきましょう。

 

攻撃用トレイト(/App/Traits/Service/Battle/ServiceBattleAttackTrait.php
/**
* 攻撃メッセージのパラメーターとして設定するID
* @var string
*/
private $atk_msg_id;
 
/**
* 攻撃
* (攻撃→ダメージ計算→ひんし判定)
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param object $move
* @return void
*/
protected function attack($atk_pokemon, $def_pokemon, $move)
{
 
--省略
 
    // 攻撃メッセージを格納
    $this->atk_msg_id = $this->issueMsgId();
    $this->setMessage($atk_pokemon->getPrefixName().'は'.$move->getName().'を使った!', $this->atk_msg_id);
 
--省略
 
/**
* 攻撃判定成功時の処理
*
* @param object $atk_pokemon
* @param object $def_pokemon
* @param object $move
* @return void
*/
private function attackSuccess($atk_pokemon, $def_pokemon, $move)
{
 
--省略
 
    // このターン受けるダメージをポケモンに格納
    $def_pokemon->setTurnDamage($move->getSpecies(), $damage ?? 0);
    // ダメージ計算
    $def_pokemon->calRemainingHp('sub', $damage ?? 0);
    // HPバーのアニメーション用レスポンス
    if(isset($damage)){
        $this->setResponse([
            'param' => $damage,
            'action' => 'hpbar',
            'target' => $def_pokemon->getPosition(),
        ], $this->atk_msg_id);
    }

 

攻撃用トレイトであれば、メッセージのタイミングがかなり早くに発生して、ダメージ計算が別のメソッド内で行われるため、プロパティを用意してメッセージIDを格納しておきます。メッセージを格納する際にパラメーターとしてIDを格納、その後ダメージ計算後に同じIDをキーにしてsetResponseに対して必要なパラメータを持たせています。

 

レスポンスとして返す値には、以下の3つを用意しました。

action:アクションの種類

target:対象

param:ダメージ量(回復量)

 

回復技なども同じアクションで処理ができるように、アクションの種類はhpbarとしました。これが1つ目のactionです。経験値の増加アニメーションであれば、expbarとする予定です。これはJavascriptで分岐させるために使うだけなので、設定値は自由です。

 

2つ目のtargetは、敵か味方かを判別するための値です。こちらは防御ポケモンのgetPositionを使用の値を使って判別しています。

 

3つめのparam変動値です。HPであればほとんどがダメージとなるので、減算処理では正の数回復には負の数を指定して同じJavascriptの処理で実装します。

 

この要領で、状態異常によるダメージなども指定していきます。例として「やどりぎのタネ」のパラメーター格納を見てみましょう。

 

バトル用チェックトレイト(/App/Traits/Service/Battle/ServiceBattleCheckTrait.php
/**
* アタック後の状態変化チェック
*
* @param object Pokemon
* @return void
*/
protected function checkAfterSc($sicked_pokemon, $enemy_pokemon)
{
 
--省略
 
    /**
    * やどりぎのタネ
    */
    if(isset($sc['ScLeechSeed'])){
        // メッセージIDの生成(ダメージ用と回復用)
        $ls_msg_id1 = $this->issueMsgId();
        $ls_msg_id2 = $this->issueMsgId();
        // 最大HPの1/8HPを吸収する
        $leech_seed = new ScLeechSeed;
        // 小数点以下切り捨て
        $damage = (int)($sicked_pokemon->getStats('HP') / 8);
        if($damage){
            // 最小ダメージ数は1
            $damage = 1;
        }
        // ダメージ計算
        $sicked_pokemon->calRemainingHp('sub', $damage);
        // HPバーのアニメーション用レスポンス
        $this->setResponse([
            'param' => $damage,
            'action' => 'hpbar',
            'target' => $sicked_pokemon->getPosition(),
        ], $ls_msg_id1);
        // 回復
        $enemy_pokemon->calRemainingHp('add', $damage);
        // HPバーのアニメーション用レスポンス
        $this->setResponse([
            'param' => $damage * -1, # 加算するため負の数に変換してセット
            'action' => 'hpbar',
            'target' => $enemy_pokemon->getPosition(),
        ], $ls_msg_id2);
        // メッセージ(アニメーション用に空メッセージを2つ用意)
        $this->setAutoMessage($ls_msg_id1);
        $this->setAutoMessage($ls_msg_id2);
        $this->setMessage($leech_seed->getTurnMessage($sicked_pokemon->getPrefixName()));
        // HPが0になっていればチェック終了
        if(!$sicked_pokemon->getRemainingHp()){
            return;
        }

 

やどりぎのタネのHPバーのアニメーションとメッセージの流れは

  1. 相手のHPが減る
  2. 自分のHPが増える
  3. メッセージ表示「やどりぎのタネが相手の〇〇から体力を吸収した」

 という3ステップになります。なので、最初の2つには空の自動メッセージに合わせてIDをのパラメーターを結びつけておき、最後にメッセージを表示させています。

 

あとは、ダメージの種類に合わせて準備するだけです。余白を置きたいときには自動メッセージや空メッセージ、メッセージ後にアクションを起こしたい場合は通常メッセージにパラメーターをセットすれば、可能な限り近い再現が可能となります。

 

まとめ

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

今回のPHPポケモンは「HPバーアニメーション」の作り方について、サーバー側での処理方法をご紹介しました。

次回はフロント側の処理として、JavascriptjQuery)を使ったアニメーションの作り方を説明します。

 

現在WEBアプリケーションを学習中の方や興味がある人は、ぜひ参考にしてくださいね。

 

注目の記事

教師が勝ち組!?出会い&学習の超正統派マッチングサービスとは【ビジネス企画書】
ビジネスモデル
マーケティング
教師が勝ち組!?出会い&学習の超正統派マッチングサービスとは【ビジネス企画書】

  「出会いがない」   社会人になると多くの人が抱く悩みの1つです。職場の男女比率や年齢層が理由の人もいれば、同じ職場での出会いは求めていない人もいるでしょう。 今回はそんな「出会い」を解決するための企画「出会い&学習の超正統派マッチングサービス」について、企画案と考察をまとめました。   ...

簡単に良質なブログ記事を量産する3箇条【ネタがないとは言わせない】
ライティング
ブロガー,ブログ
簡単に良質なブログ記事を量産する3箇条【ネタがないとは言わせない】

  ブログの毎日のテーマ決めが大変・・・ そもそも良質な記事をどうやって書けるようになるのかわからない   こんな悩みを抱えていませんか? 始めたばかりで伸び悩んでいる人には多いのではないでしょうか。 今回はブログで収益化や、アクセス数を伸ばそうと考えている人へ向けて「簡単に良質なブログ...

ピカチュウから学ぶオブジェクト指向 〜ステータス導入編〜 6
プログラミング
PHP,PHPポケモン,オブジェクト指向,ポケモン
ピカチュウから学ぶオブジェクト指向 〜ステータス導入編〜 6

  PHPをピカチュウ(ポケモン)から学ぶ大人気コーナー、第6回目は「ステータス機能の導入編」です。   前回(第5回)で終了段階でのサンプルコードを公開しているので、もし本記事から始める人はぜひそちらを参考にしてください。    ※お詫び   調べたところによると、ポケモンの経験...

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

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

ピカチュウから学ぶオブジェクト指向 〜レベルシステム導入編〜 3
プログラミング
PHP,PHPポケモン,オブジェクト指向,ポケモン
ピカチュウから学ぶオブジェクト指向 〜レベルシステム導入編〜 3

  大人気?シリーズ「ピカチュウから学ぶオブジェクト指向」、今回はレベルシステムの導入編です。   第1回(基礎編)、第2回(クラス継承編)で作成したPokemonとPikachuのクラスを使用するので、最初から学習したい人はぜひご参考ください。      レベルシステムの導入   ポケモンのゲ...

トレーナー戦編 バトルシステムへの追加 PHPポケモン 98
プログラミング
PHP,PHPポケモン,ポケモン
トレーナー戦編 バトルシステムへの追加 PHPポケモン 98

トレーナー戦闘の追加 前回トレーナー情報を作成したので、今回はその情報をバトルシステムへ組み込んでいきます。 現在、野生ポケモンとの戦闘では「battle」という値actionの値で受け取っています。同じサービス内で分岐を作ると複雑になってしまうので、battle_trainerという新しい分岐を使ってサービス分けをし...

動画にカラオケテロップを入れる編集方法【AfterEffectsで色変わりの文字】
動画編集
Adobe,AfterEffects
動画にカラオケテロップを入れる編集方法【AfterEffectsで色変わりの文字】

  動画に声と同じタイミングでテロップを入れたい カラオケのような文字はどうやっていれればいいの?   一見簡単に見えるものも、いざ導入しようとすればどうやればいいかわからない、そんなこと多いのではないでしょうか? 今回はAdobe AfterEffectsを使った方法をご紹介します。些細な編集が動画のク...

フリーランスなら心がけておきたい3つのルール【仕事と遊びは両立させろ】
フリーランス
フリーランス,独立
フリーランスなら心がけておきたい3つのルール【仕事と遊びは両立させろ】

  フリーランスになっても不安がいっぱい   会社というものに縛られないというのは楽なイメージがありますが、それ相応の不安がついてまわります。 その結果、会社員へと舞い戻ってしまうと再度そこから抜け出すことは非常に困難です。   今回は、現在活動している人や、これから独立しようとしている...

カテゴリ

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