プログラミング

進化アニメーション 後編 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発というカメックスの装甲の硬さ(フシギダネの攻撃の貧弱さ)がわかる結果となりました。

 

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

 

注目の記事

仕事用PCは2年で買い替えろ!その理由とは?
フリーランス
MacBook
仕事用PCは2年で買い替えろ!その理由とは?

  「あなたはパソコンをどのタイミングで買い替えますか?」   私は先日、2015年モデルのMacbookPro15インチから最新モデルのMacbookPro13インチに買い替えました。その理由は、メモリが32GBになったことや、15インチが持ち運びという点で見れば向いていないと判断したということ、そこそこの年数が経...

記事を書いてもブログが伸びないのは何故?SEOの評価を下げてしまう絶対にダメな3つのこと【知らない内に損してます】
SEO対策
SEO,ブログ
記事を書いてもブログが伸びないのは何故?SEOの評価を下げてしまう絶対にダメな3つのこと【知らない内に損してます】

  1年ブログを継続したけど、全くPVが伸びない・・・   ブログを育てるためには、毎日または定期的な更新が必ず必要ですが、それでも思うように伸びないという人は、実は他に致命的にな原因があることがほとんどです。 今回はそういった「努力しても結果が出ない」と悩んでいるブロガーやブログ運営者に...

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

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

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

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

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

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

OSI参照モデルとTCP/IP 〜ネットワークモデルとは〜【第3回 ド素人のためのネットワーク講座】
ネットワーク
OSI参照モデル,TCP/IP,ネットワークモデル,プロトコル
OSI参照モデルとTCP/IP 〜ネットワークモデルとは〜【第3回 ド素人のためのネットワーク講座】

  ド素人のためのネットワーク講座! 栄えある第3回は「ネットワークモデル」についてです。   横文字や英字が多くなってきたり、歴史的経緯が関係してきたりとややこしくなってくる部分ですが、ネットワークを理解するためには押さえておきたいポイントです。   プログラミングやシステムエンジニアとし...

【Laravel7】既存makeテンプレートのカスタマイズ Requestサンプル有り
プログラミング
Laravel,Linux,PHP
【Laravel7】既存makeテンプレートのカスタマイズ Requestサンプル有り

  Laravel7では新しい機能が様々導入されており、Webアプリケーションの開発がよりスムーズなものとなってきています。その中でも、もどかしい場所へ手を届いたと感じさせてくれたのが、既存makeコマンドのstubを簡単にカスタマイズできるようになったことです。   今回は既存make用stubの取得コマンドと、リ...

HTMLの基本設計「よくわかるSEO対策」構造化(マークアップ)編
SEO対策
HTML,SEO,マークアップ
HTMLの基本設計「よくわかるSEO対策」構造化(マークアップ)編

  HTML5がリリースされて随分経ちました。 それにより多くのタグが増え、それにより今までデザインを目的と使用されていたものが「構造化」という本来の役割を持たされるようになりました。    構造化は、目に見えにくい部分です   それぞれの役割を理解していなければ、せっかくきれいなサ...

カテゴリ

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