【TopDown Engine】TopDown Engineの処理を軽くしよう!

最適化 TopDownEngine
スポンサーリンク

前書き

TopDown Engineは多機能かついろいろと拡張性があり、初心者でも使いやすいテンプレートアセットになっていますが、反対に多機能であるため処理が重たいというデメリットも抱えています。特にいま自分が取り掛かっているようなヴァンサバ系のゲームでは敵が大量に出るため、「敵AIの処理」や「物理演算」の部分でどうしても処理が重くなってしまうことがあります。そのため、処理の最適化は必要不可欠な状況となっています。

主にTopDown Engineに絡むところではありますが、今回はTopDown Engineでパフォーマンスをよくする簡単なテクニックについて紹介していこうと思います。ゲームを作成する上で通じる最適化になりますのでTopDown Engineを持ってない方でも参考にしていただけたらと思います。

以前、衝突にの最適化についてまとめた記事は以下になります。

最適化ができる4つの手法について

まず前提としてヴァンサバ系のように「敵がたくさん出てくるようなゲーム」の場合の最適化についてほ方法になります。敵がたくさん出てくるため「敵AIの処理」や「敵同士の物理演算」が主に重たくなる要因となります。そのため、今回紹介する方法は「敵キャラに対してどのように最適化をするか」について簡単な4つの最適化について紹介していこうと思います。

  1. AI BrainのActionsFrequency、DecisionFrequency(頻度)を減らす
  2. Enemy同士の接触をなくす
  3. カメラの範囲外になったらアニメーションを停止する
  4. カメラの範囲外になったらコライダーを無効にする

AI BrainのActionsFrequency、DecisionFrequency(頻度)を減らす

TopDown EngineのAI BrainにはActionsFrequency、DecisionFrequencyという2つのパラメータが存在します。

AI Brain

それぞれ、

  • ActionsFrequency:敵AIがアクションを実行する頻度
  • DecisionFrequency:敵AIが決定を評価する頻度

となっており、デフォルトは”0″(sec)となっています。

この値を0.5~2(sec)など値を変更することで頻度が下がりパフォーマンスが上昇します。ただし、頻度を下げることでActionやDecisionの実行までに時間がかかってしまうデメリットも存在するため、実際にどんな値に設定するかは作成するゲームによって細かく変えていきましょう。

具体的にActionとDecisionの対象は以下の四角で囲った部分になります。以下の処理が実行される頻度が減る=パフォーマンスが上昇するという仕組みです。単位はsecなので”0″で即時実行、”1″にすることで1秒ごとに実行という形になります。

AI BrainのStates

Enemy同士の接触をなくす

例えば以下のように敵を大量に出現させた場合だとEnemy本体にColliederを設定しているため、Enemy同士の接触で常に物理演算が発生しパフォーマンスが低下してしまいます。

Enemy

通常のTopDown EngineのPhysics 2Dの設定では以下のようにEnemy同士の接触でも物理演算をするような設定となっています。以下のチェックを外すことでEnemy同士の接触がなくなり物理演算もしなくなるためパフォーマンスが上昇します。

Physics 2D

ただし、Enemy同士の接触がなくなるので、Enemyが重なってしまう問題もあるのでここはケースバイケースの設定になるかなと思います。

カメラの範囲外になったらアニメーションを停止する

カメラに写っている場合はキャラクターのアニメーションを実行し、カメラの範囲外(見えないところ)にいるキャラクターのアニメーションは停止するやり方になります。

そもそもなぜカメラの範囲外でアニメーションを停止するのがいいかというと、

アニメーションはCPU負荷を増やす:

  • Animatorコンポーネントは、毎フレームアニメーションの計算を行い、カメラに映っていないキャラクターもアニメーションを再生し続けると、CPUが不要な計算を行うことになります。

視覚的には不要な処理:

  • カメラに映らないキャラクターのアニメーションは、プレイヤーから見えないため動作させる必要がない。

パフォーマンス向上:

  • アニメーションを無効化することで、CPUの負荷を軽減し、特に大量のキャラクターが存在するシーンではフレームレートを向上させることができます。

と、いう理由から基本的にカメラ範囲外のアニメーションは停止させるほうがパフォーマンスがよくなります。

以下が簡単なカメラの範囲外だとアニメーションを停止するスクリプトになります。アニメーションコンポーネントと同じオブジェクトにアタッチすることでカメラの範囲外だとアニメーションが停止するようになります。

public class StopAnimationOutsideCamera : MonoBehaviour
{
    private Camera mainCamera;
    private Animator animator;

    void Start()
    {
        mainCamera = Camera.main;
        animator = GetComponent<Animator>();
    }

    void Update()
    {
        // オブジェクトの位置がカメラの範囲外にあるかどうかをチェック
        if (!IsObjectInsideCameraBounds())
        {
            // カメラの範囲外に出たらアニメーションを停止する
            animator.enabled = false;
        }
        else
        {
            // カメラの範囲内に戻ったらアニメーションを再開する
            animator.enabled = true;
        }
    }

    bool IsObjectInsideCameraBounds()
    {
        // オブジェクトの座標をスクリーン座標に変換
        Vector3 screenPoint = mainCamera.WorldToScreenPoint(transform.position);

        // カメラの範囲内にあるかどうかをチェック
        return screenPoint.x >= 0 && screenPoint.x <= Screen.width &&
               screenPoint.y >= 0 && screenPoint.y <= Screen.height;
    }
}

上記のスクリプトをアタッチした状態で動かしてみた場合の動画が以下になります。白い枠の中がカメラの領域になり、外側がカメラの領域外になります。外側にいるキャラクターのアニメーションが停止しており、カメラの内部に入るとアニメーションが実行されることが分かりますね。

敵が1体程度でしたらそこまで重たくないのですが、今回の動画のように敵が大量に出るような場合はアニメーションといえどパフォーマンス低下につながるので、なるべくカメラ外にいるキャラクターのアニメーションは停止させたほうがいいかなと思います。

カメラの範囲外になったらコライダーを無効にする

2つ目の「Enemy同士の接触をなくす」と通じるものがありますが、無駄に物理演算をすることでパフォーマンスの低下につながることになります。またカメラの範囲外で物理演算をすることも無駄なことにつながるので、見えない位置では物理演算をしないでパフォーマンスを上昇させる手法になります。

先ほどの「カメラの範囲外になったらアニメーションを停止する」と同じようにカメラの範囲を取得して範囲外だとColliderを停止するような簡単なスクリプトになっています。

public class ColliderManager : MonoBehaviour
{
    private Camera _mainCamera;
    private Collider _collider;

    void Start()
    {
        _mainCamera = Camera.main;
        _collider = GetComponent<Collider>();
    }

    void Update()
    {
        var viewportPosition = _mainCamera.WorldToViewportPoint(transform.position);
        bool isInView = viewportPosition.x >= 0 && viewportPosition.x <= 1 &&
                        viewportPosition.y >= 0 && viewportPosition.y <= 1;
        _collider.enabled = isInView;
    }
}

ただし、問題点が1つあり、このスクリプトを付けることで例えば壁の外側にキャラクターが行ってほしくないなどある場合はColliderがないので簡単に壁の外側にも行ってしまいます。

ステージが無限大に続くような場合とかに有効なスクリプトですのでこちらも必要であれば設定してみるのがいいかなと思います。

最適化の前

まずは最適化の前のパフォーマンスを見てみましょう。だいたい15秒ぐらいからFPSが15ぐらいになっていますね。動画では30秒ぐらいで切れていますがその後もFPSは落ち続けていき、最終的には10ぐらいになってゲームどころではないぐらいまでパフォーマンスが落ちてしまいます。

最適化の後

それでは最適化後のパフォーマンスを見ていきましょう。自分のゲームの作り上、今回適用したのは「AI BrainのActionsFrequency、DecisionFrequency(頻度)を減らす」と「カメラの範囲外になったらアニメーションを停止する」の2つだけになります。

15秒ぐらいからFPSが30ぐらいで安定していることが分かりますね。最適化前と比べると2倍ぐらいはパフォーマンスがよくなっている結果になります。
# Physicsが上がらないのは謎…これといって物理演算周りは変えておらず2つの最適化だけでなぜここまでパフォーマンスがよくなったのかはちょっと分からず…思いっきり敵を追加してもPhysicsが上がらず…

いろいろやりようはありますがちょっと工夫するだけでもパフォーマンスがよくなることが分かりますね。

まとめ

今回はゲームの最適化(パフォーマンスの向上)について紹介しました。

TopDown Engineは多機能でいろいろなことが出来る反面、どうしても処理が重たいということがあげられます(Google検索サジェストを見ても TopDownEngine 重い というのが見受けられます)

ただし、重たいのにも理由があり色々と処理を覗いてみると結構無駄な処理が内在しています。ゲームの作りによっては変更が効くこともありますのでProfilerを覗いて1つずつ解決していくのがいいと思います。

今回紹介した方法は比較的簡単な最適化になりますので、もしも自分のゲームが重たいなと感じた方はぜひ今回の方法で最適化を試してみてください。

コメント

タイトルとURLをコピーしました