プログラミング

進化アニメーション 後編 PHPポケモン 60

PHP PHPポケモン ポケモン
進化アニメーション 後編 PHPポケモン 60

ポケモンの進化演出

前回に続いて、ポケモンの進化演出を実装していきます。バックエンドの処理はざっと説明をしたので、今回はフロントエンド(JavaScript)側の処理を作成していきましょう。

 

進化画面は新しく設けたので、こちらにもバトル画面で使っているメッセージ用JSを作成していきます。処理自体はほとんど変わりませんが、最終処理や分岐が少し異なるため、ファイル分けしています。

 

進化画面用メッセージJS/Public/Assets/js/Evolve/message.js
/*----------------------------------------------------------
// 初期化する関数
----------------------------------------------------------*/
var supportedFlag = $.keyframe.isSupported();
// 自動メッセージ判定用
var auto_msg;
// メッセージボックスのクリック判定用
var click_flg;
 
/**
* 画面読み込み時の関数
* @function ready
* @return void
**/
var startInit = function(){
    // 現在のメッセージ
    var now = $('.result-message.active');
    if((now.length === 0) || now.hasClass('last-message')){
        doLastMsg();
        return;
    }
    doNotLastMsg();
}
 
/**
* メッセージボックスクリック時の関数
* @function click
* @return void
**/
var clickMsgBoxInit = function(){
    click_flg = true;
    // 変数をリセット
    auto_msg = false;
    $('.action-message-box').on('click', async function(){
        if(click_flg === false) return;
        $(".message-scroll-icon").hide();
        // メッセージボックスを処理終了まで無効化
        click_flg = false;
        // 現在のメッセージ
        var now = $('.result-message.active');
        await actionMsgBox(now);
        // 次がオートメッセージの場合は再度実行
        while(auto_msg){
            now = now.next();
            await actionMsgBox(now);
        }
        // メッセージボックスを有効可
        click_flg = true;
    });
}
 
/**
* 進化キャンセルボタン
* @function click
* @return void
**/
var clickCancelEvolveInit = function(){
    $('#cancel-evolve').on('click', async function(){
        // 自身のボタンを非表示化
        $(this).hide()
        // アニメーションの強制終了(コールバックを無効化)
        $('#pokemon-before').resetKeyframe(function(){});
        $('#pokemon-before').css('opacity', 1);
        await nextMsg($('.result-message.active'));
        // cancelボタンで発火しないように1秒後にメッセージボックスを有効可
        setTimeout(function() {
            click_flg = true;
        }, 1000);
    });
}
 
/*----------------------------------------------------------
// 処理内で呼び出す関数
----------------------------------------------------------*/
/**
* メッセージアクション
* @param now element
* @return Promise
**/
var actionMsgBox = function(now){
    return new Promise( async (resolve, reject) => {
        // 最終メッセージかどうか確認
        if((now.length === 0) || now.hasClass('last-message')){
            await doLastMsg();
        }else{
            // メッセージにアクションがセットされていれば実行
            switch (now.data('action')){
                // ==============================================
                // 進化アニメーション ===========================
                //
                case 'evolve':
                $('#cancel-evolve').show()
                await evolvePokemon();
                break;
                // ==============================================
                // 進化キャンセル ===============================
                //
                case 'cancel':
                $('#remote-form-action').val('cancel');
                $('#remote-form').submit();
                break;
            }
            // 次のメッセージへ
            await nextMsg(now);
        }
        resolve();
    });
}
 
// ==============================================
// 進化演出 =====================================
//
/**
* 5秒間の進化アニメーション
* @param mixed param
* @return Promise
**/
var evolvePokemon = function(param){
    return new Promise ((resolve, reject) => {
        var keyframe = {};
        var per = 100;
        var opacity = 1;
        while (per) {
            if(opacity){
                keyframe[per + '%'] = {opacity: 0};
                opacity = 0;
            }else{
                keyframe[per + '%'] = {opacity: 1};
                opacity = 1;
            }
            per -= 3;
            if(per < 0){
                per = 0;
            }
        }
        keyframe['0%'] = {opacity: 1};
        keyframe.name = 'evolve-animation';
        // キーフレームの用意
        $.keyframe.define(keyframe);
        // 経験値バーのアニメーション
        $('#pokemon-before').playKeyframe({
            name: 'evolve-animation',
            duration: '5000ms',
            timingFunction: 'ease',
            delay: '0s',
            iterationCount: 1, // 繰り返し回数
            direction: 'normal',
            fillMode: 'forwards',
            complete: function(){
                $('#cancel-evolve').hide()
                $('#remote-form-action').val('evolve');
                $('#remote-form').submit();
            }
        });
    });
}
 
// ==============================================
 
/**
* 次のメッセージへ移行する処理
* @param now element
* @return Promise
**/
var nextMsg = function(now){
    return new Promise( async (resolve, reject) => {
        // 現在のメッセージのactiveを解除
        now.removeClass('active');
        // 次のメッセージにactiveを付与
        var next = now.next();
        next.addClass('active');
        /**
        * メッセージのステータスに合わせた分岐
        **/
        // 最終メッセージかどうかの判別
        if(next.hasClass('last-message')){
            // 最終メッセージ
            await doLastMsg();
        }else{
            // 最終メッセージではない
            doNotLastMsg();
        }
        // 次のメッセージがオートメッセージかどうかの判定
        if(next.data('auto')){
            auto_msg = true;
        }else{
            auto_msg = false;
        }
        // 処理終了
        resolve();
    });
}
 
/**
* 最終メッセージの処理
* @return void
**/
var doLastMsg = function(){
    // スクロールアイコンを非表示
    $('.message-scroll-icon').hide();
    // 終了
    $('#remote-form-action').val('');
    setTimeout(function() {
        $('#remote-form').submit();
    }, 500);
}
 
/**
* 最終メッセージではない場合の処理
* @return void
**/
var doNotLastMsg = function(){
    // スクロールアイコンを表示
    $('.message-scroll-icon').show();
}
 
/*----------------------------------------------------------
// 初期化
----------------------------------------------------------*/
jQuery(function($){
    startInit();
    clickMsgBoxInit();
    clickCancelEvolveInit();
});

 

それでは、1つずつ処理を見ていきましょう。

 

点滅処理

まずは進化時の点滅処理についてです。ピカチュウがライチュウに進化する場合、ピカチュウが点滅してライチュウがチラチラと見えている状態になりますね。これをCSSで演出するには、ライチュウの画像にピカチュウを重ね、ピカチュウの透明度を0→1とループさせることで演出ができます。

ただ、ピカチュウはgif画像のため背景がなく、ただ重ねただけでは標準状態でライチュウが見えてしまいます。そうならないためにも、ピカチュウの背景色を白にして、paddingを大きめに設定することで、後ろに用意されているライチュウを隠します。

 

進化画面(/Resources/Pages/Evolve.php
<figure class="position-relative d-inline-block">
    <img
    src="/Assets/img/pokemon/dots/front/<?=$pokemon->getAfterClass()?>.gif"
    alt="進化先"
    class="bg-white p-5">
    <img
    id="pokemon-before"
    src="/Assets/img/pokemon/dots/front/<?=get_class($pokemon)?>.gif"
    alt="<?=$pokemon->getName()?>"
    class="position-absolute bg-white p-5"
    style="left:0;top:0;">
</figure>

 

点滅処理はキーフレームで行いますが、こちらはHPバーと同じくjQueryのKeyframeライブラリを使用してセットしています。

// ==============================================
// 進化演出 =====================================
//
/**
* 5秒間の進化アニメーション
* @param mixed param
* @return Promise
**/
var evolvePokemon = function(param){
    return new Promise ((resolve, reject) => {
        var keyframe = {};
        var per = 100;
        var opacity = 1;
        while (per) {
            if(opacity){
                keyframe[per + '%'] = {opacity: 0};
                opacity = 0;
            }else{
                keyframe[per + '%'] = {opacity: 1};
                opacity = 1;
            }
            per -= 3;
            if(per < 0){
                per = 0;
            }
        }
        keyframe['0%'] = {opacity: 1};
        keyframe.name = 'evolve-animation';
        // キーフレームの用意
        $.keyframe.define(keyframe);
        // 経験値バーのアニメーション
        $('#pokemon-before').playKeyframe({
            name: 'evolve-animation',
            duration: '5000ms',
            timingFunction: 'ease',
            delay: '0s',
            iterationCount: 1, // 繰り返し回数
            direction: 'normal',
            fillMode: 'forwards',
            complete: function(){
                $('#cancel-evolve').hide()
                $('#remote-form-action').val('evolve');
                $('#remote-form').submit();
            }
        });
    });
}

 

点滅処理は透明度の0→1を単純にループさせるわけではなく、0%から100%まで0→1を刻んで設定しています。PHPポケモンの場合は3%区切りです。この設定にした理由は、playKeyframeの完了コールバックで進化処理を実行させるためです。

ポケモンでは、進化演出が始まり放っておくことで進化が確定します。操作が必要なのは中断させる場合のみです。また、演出処理は中断させる判断への余裕を持たせるためにも5秒間としています。

完了コールバックでは、リモートフォームにアクションをセットしてサブミットしています。

 

進化の中断

次に進化の中断処理についてです。進化演出が始まり、5秒後には進化が確定します。それを中断させられるように、演出が始まればメッセージボックス上にキャンセルボタンを配置します。

<div class="message-box action-message-box border p-3 mb-3">
    <?php # メッセージエリア ?>
    <?php foreach($controller->getMessages() as $key => list($msg, $status, $auto)): ?>
        <?php $class = $key === $controller->getMessageFirstKey() ? 'active' : ''; ?>
        <?php $last_class = $key === $controller->getMessageLastKey() ? 'last-message' : ''; ?>
        <p class="result-message <?=$class?> <?=$last_class?> <?=$status ?? ''?>"
            data-action='<?=$responses[$status]['action'] ?? ''?>'
            data-target='<?=$responses[$status]['target'] ?? ''?>'
            data-param='<?=$responses[$status]['param'] ?? ''?>'
            data-toggle='<?=$responses[$status]['toggle'] ?? ''?>'
            data-auto='<?=$auto ?? ''?>'>
            <?=$msg?>
        </p>
    <?php endforeach; ?>
    <span class="message-scroll-icon small">【CLICK】</span>
    <button type="button" id="cancel-evolve" class="btn btn-danger" style="display:none;">進化させない</button>
</div>

 

操作パネルはメッセージボックス欄のみにしておきたかったため、今回メッセージボックス上にボタンを配置するというレイアウトにしました。

 

中断ボタンが押されれば、進化演出と送信処理を止める必要があります。なので、クリック判定でキーフレームを破棄して、事前に用意した中断時のメッセージへ進めます

 

/**
* 進化キャンセルボタン
* @function click
* @return void
**/
var clickCancelEvolveInit = function(){
    $('#cancel-evolve').on('click', async function(){
        // 自身のボタンを非表示化
        $(this).hide()
        // アニメーションの強制終了(コールバックを無効化)
        $('#pokemon-before').resetKeyframe(function(){});
        $('#pokemon-before').css('opacity', 1);
        await nextMsg($('.result-message.active'));
        // cancelボタンで発火しないように1秒後にメッセージボックスを有効可
        setTimeout(function() {
            click_flg = true;
        }, 1000);
 
    });
}

 

中断後、タイミングによっては進化先のポケモンが見えてしまっている場合があります。それを防ぐためにも、キーフレーム中断後に透明度を1に変更しています。

さらに、中断後は無効化されているメッセージボックスを有効にする必要があります。そのためにグローバル化したクリックフラグを1秒後に有効化させました。

 ※すぐに有効化させると、中断ボタンを押した判定が同時に動いているためメッセージが2つ進むという現象が起こりました。別の対策方法もありそうですが今回は簡易的にこの方法で対処しました。

 

それでは実際に、PHPポケモンの進化演出を見てみましょう。

 

進化の中断

 

進化

 

しっかり期待通りの演出がされましたね。点滅はもっと細かく抑揚がほしいところですが、今回はこれぐらいにしておきましょう。

より本家らしく再現したい人は、ぜひ挑戦してみてください。

 

まとめ

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

今回のPHPポケモンは「進化アニメーション・演出」についてご紹介しました。

 

検証用に、レベルが1発で上がる経験値をくれる同レベルのカメックスを用意して、さらにササッと倒せるように「つるのムチ」の威力を245まで引き上げたのですが、まさかの確定2発というカメックスの装甲の硬さ(フシギダネの攻撃の貧弱さ)がわかる結果となりました。

 

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

 

注目の記事

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

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

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

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

PHPポケモン「バトルシステム実装編〜ダメージ計算〜」20
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「バトルシステム実装編〜ダメージ計算〜」20

バトルシステムの実装  今回はバトルシステムの中でもメインとなるダメージ計算と、命中判定の部分を実装していきます。   ダメージ計算  ポケモンのダメージ計算は初代から現在までそこまで大きな変化はありません。最新世代ではダメージに関係する要素(アイテム等)が多く、それにより補正値の修正はあります...

壁技編(リフレクター・ひかりのかべ)PHPポケモン 51
プログラミング
PHP,PHPポケモン,ポケモン
壁技編(リフレクター・ひかりのかべ)PHPポケモン 51

壁技とは 前回実装した「しろいきり」と同じく、初代から実装されたフィールド効果技が存在します。それが「壁技」です。 壁(ポケモンwiki) https://wiki.ポケモン.com/wiki/壁 壁とは、バトルにおける場の状態の一種、およびその場を形成するわざの総称。わざとしては第一世代から存在するが、場の状...

Laravel7系でTraitのmakeコマンドを作成する方法
プログラミング
artisan,Laravel,Linux,PHP,Trait
Laravel7系でTraitのmakeコマンドを作成する方法

Laravel7がリリースされて、さっそくそれを使った開発の機会がやってきましたので、使用頻度の高いものをまとめていきたいと思います。 5系や6系の情報は多く出回っていますが、6系がLTSということもあって7系の情報は少なめですね。   今回は「Laravel7系でTraitのmakeコマンドを作成する方法」をご紹介します...

なぜプロは有線のマウスやキーボードを選ぶのか?【有線VS無線】
雑記
Bluetooth
なぜプロは有線のマウスやキーボードを選ぶのか?【有線VS無線】

  無線が普及する現代、何故有線のマウスやキーボードは売れているのか   いろんなものが製品の進化と共に無線化している一方、有線の需要も高く、とくにプロなど上層で活躍する人は有線を選択するケースが少なくありません。 今回は、そんな有線と無線の違いや、それぞれのメリットについて解説していき...

食に関する考察 自炊と節約は成立するのか?
雑記
食に関する考察 自炊と節約は成立するのか?

スキル関係の記事が多めの当ブログですが、今回は「食」をテーマに触れていきます。突き詰めていけば食も立派なスキルであり、奥が深いものです。更に、生活の基盤となるため多くの人がこの問題に直面することであり、健康とも直結するため仕事などにおけるパフォーマンスにも大きく関係します。   これから一人暮ら...

【脱初心者向けプログラミング学習】処理を追え!「今、変数には何が入ってる?」
プログラミング
HTML,PHP,プログラミング学習
【脱初心者向けプログラミング学習】処理を追え!「今、変数には何が入ってる?」

  初心者から中々抜け出せない 1から処理をかけない   そんな悩みを抱えたプログラミング学習者に向けて、「脱初心者のためのプログラミング学習」について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 独立 神戸 福祉 秘密鍵 翻訳 自己啓発 英語 見積書 計算機 読書 起業 迷惑メール 配列 銀の弾丸 集客 雑学力