プログラミング

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

 

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

 

注目の記事

パーティー実装編 トレーナーの作成 PHPポケモン 63
プログラミング
PHP,PHPポケモン,ポケモン
パーティー実装編 トレーナーの作成 PHPポケモン 63

パーティーとは ポケモンでは、ゲーム・アニメ共に最大6匹のパートナーポケモンと旅をすることができます。7匹以上は持ち歩くことができず、ボックスに転送されてしまうという仕組みです。そして、その6匹のパートナーポケモンの構成をパーティーと呼んでいます。 現在のPHPポケモンでは1匹のポケモンしか管理で...

フリーランスの最大の敵は休日!?正しい休息の取り方教えます
フリーランス
フリーランス,独立
フリーランスの最大の敵は休日!?正しい休息の取り方教えます

  フリーランスになりたての人は、仕事のペースがわからなかったり、無理に自分へ追い込みをかけて頑張ろうとしてしまうことがあります。 毎日を仕事でいることが、フリーランスにとっての心がけや意識のあり方としては大切ですが、それをそのままの意味で実行してしまうと明らかなオーバーペースになるのは事...

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

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

両隣のポケモン取得編(配列の隣の要素)PHPポケモン 92
プログラミング
PHP,PHPポケモン,ポケモン
両隣のポケモン取得編(配列の隣の要素)PHPポケモン 92

両隣のポケモンを判別する パーティーの中からID指定でポケモンを特定するためのメソッドは実装されていましたが、その両隣にどんなポケモンが控えているのかを判別する手段がありませんでしたね。 なので、今回は両隣のポケモンを判別するためのメソッドを準備して活用していきましょう。   パーティー まずは...

わざマシン編 習得 PHPポケモン105
プログラミング
PHP,PHPポケモン,ポケモン
わざマシン編 習得 PHPポケモン105

わざマシンによる技習得 前回、わざマシンのアイテムクラスを作成したので、今回は実際に使用するまでの処理を作成していきます。 まずは、アイテムクラスに使用時の処理(effectsメソッド)を増設しましょう。   わざマシン01(/Classes/Item/ItemTM01.php) <?php require_once(root_path('Classes').'...

ポケモン入れ替え編 PHPポケモン 83
プログラミング
PHP,PHPポケモン,ポケモン
ポケモン入れ替え編 PHPポケモン 83

ポケモンの入れ替え 複数ポケモンの所有、そして並び替えの機能が整ったので、いよいよバトルでのポケモン交代機能を実装します。ポケモンがバトル中に交代する方法は大きく分けて以下の4つです。 プレイヤー操作による交代 ひんしによる交代 相手ポケモンの技による交代 味方ポケモンの技による交代 ...

PHPポケモン「技ポイント(PP)編」36
プログラミング
PHP,PHPポケモン,ポケモン
PHPポケモン「技ポイント(PP)編」36

技ポイント(PP)とは ポケモンではそれそれの技に使用回数が定められています。それが技ポイント(PP)と呼ばれているものです。 PP(ポケモンwiki) https://wiki.ポケモン.com/wiki/PP   技のクラスを実装した際に、それぞれにppというプロパティをもたせて回数をセットしています。これが、対象の技...

【Laravel】1対1リレーションをわかりやすく解説(hasOne)
プログラミング
Laravel,PHP
【Laravel】1対1リレーションをわかりやすく解説(hasOne)

  Laravelの1対1リレーションのhasOneについて、公式マニュアルでは専用単語ばかりでどうしてもわかりにくいと感じてしまっている方へ向けて、わかりやすく解説しました。 ※例で紹介しているコードについては、一部英語を日本語表記で使用している部分もありますので、コピペで使用する方は必要に応じて置...

カテゴリ

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