U
タブ 05モバイルへ書き出す7 レッスン — タップで開く
03 / 07 · 11 分
モバイルへ書き出す

モバイルの性能予算

ここまでのプロジェクト

Assets/

link.xml

Scripts/

MobileBoot.csQualityAutoSetter.cs

Settings/

URP mobile asset

プロジェクトの設定

IL2CPP + ARM64texture compression

すでにあるものこのレッスンで追加

tab 04 ですでに Arena の batch と三角形の予算はクリアしています——ただしデスクトップでの話です。ミドルレンジのスマホの GPU はその 10 分の 1 くらいの力で、しかも 5 分もすると熱くなって自分でクロックを落としていきます。

アニメーション図

減らすべき数字が 4 つ、上げるべき数字が 1 つ。

モバイルの予算

  • draw call / batch:100 未満なら余裕、弱い端末では 60 未満なら安心です。

  • 画面内の三角形:100k〜300k。超えるとミドルレンジの GPU は息が切れます。

  • リアルタイムのライトは directional 1 本だけ、それで終わり。残りはすべてベイクします。

  • texture のメモリ:200 MB 未満。勘ではなく Profiler の Memory モジュールで確認します。

  • 目標は安定 60 FPS。60 を維持できない端末なら、いっそ 30 に固定してガッチリ守ります。

1. URP 設定——単一では最も効くポイント

  1. 1

    あなたの URP Asset を見つけます(Project Settings▸Graphics にどれが有効かが表示されます)。

  2. 2

    Shadows → Max Distance を 40、Cascade Count を 1、Shadow Resolution を 1024 にします。

  3. 3

    mobile 用 URP asset では HDR と MSAA のチェックを外します。タイル方式のモバイル GPU ではどちらもコストが大きいからです。

  4. 4

    低スペックの端末では Render Scale を 0.8 に——解像度を 20% 落としてもほとんど見分けがつかず、速度は大幅に上がります。

  5. 5

    品質レベルごとに URP asset を分けておきます:Mobile Low、Mobile High、Desktop。

2. Texture——ビルドサイズのおよそ半分を占める

  1. 1

    texture をすべて選択し、Max Size を 1024 にします(小道具は 512、2048 は見栄えするアセットだけ)。

  2. 2

    Compression Format → Android は ASTC 6x6。これが今の標準で、同じサイズなら ETC2 より見た目はずっと良くなります。

  3. 3

    コードで本当にピクセルを読むのでない限り、Read/Write Enabled のチェックは外してください——メモリが倍になります。

  4. 4

    UI の sprite は mipmap をオフ、3D 空間にあるものはオンのままにします。

3. Script——コマ落ちを生む間違い

do not do this
void Update()
{
    // 1. searching the whole scene, 60 times a second
    GameObject player = GameObject.Find("Player");

    // 2. allocating a new object every frame
    Enemy[] all = FindObjectsOfType<Enemy>();

    // 3. building a string every frame — this is garbage
    label.text = "Score: " + score;

    // 4. a component lookup every frame
    GetComponent<Rigidbody>().AddForce(Vector3.up);
}
do this instead
private Transform player;
private Rigidbody rb;
private int lastScore = -1;

void Awake()
{
    player = GameObject.FindWithTag("Player").transform;   // once
    rb = GetComponent<Rigidbody>();
}

void Update()
{
    rb.AddForce(Vector3.up);

    if (score != lastScore)          // only build the string when it changed
    {
        label.text = "Score: " + score;
        lastScore = score;
    }
}
  • モバイルで数秒おきにカクッとなる正体はガベージコレクションです。そのほぼ全部が Update 内の文字列と新しい確保から来ています。

  • 60 Hz で回す必要のない処理はタイマーで動かしましょう——敵 AI の判定を 0.2 秒ごとにすると、コストは 12 分の 1 です。

  • 空の Update() でも engine からの呼び出しは発生します。削除してください。

4. 端末を検出してグレードを落とす

QualityAutoSetter.cs
using UnityEngine;

public class QualityAutoSetter : MonoBehaviour
{
    void Awake()
    {
        int ram = SystemInfo.systemMemorySize;       // in MB
        int cores = SystemInfo.processorCount;

        if (ram < 3000 || cores <= 4)
        {
            QualitySettings.SetQualityLevel(0, true);          // Mobile Low
            Application.targetFrameRate = 30;
        }
        else if (ram < 6000)
        {
            QualitySettings.SetQualityLevel(1, true);          // Mobile Medium
            Application.targetFrameRate = 60;
        }
        else
        {
            QualitySettings.SetQualityLevel(2, true);          // Mobile High
            Application.targetFrameRate = 60;
        }

        Debug.Log($"Device: {SystemInfo.deviceModel}, RAM {ram}MB, quality {QualitySettings.GetQualityLevel()}");
    }
}

エディタではなく実機でプロファイルする

  • エディタはデスクトップの CPU で動いています。そこで出た数値に、スマホの話はほとんど当てになりません。

  • Build Profiles で Development Build と Autoconnect Profiler にチェックを入れ、実機へビルドしてから Profiler を開きます。

  • 5 分間しっかりプレイします。サーマルスロットリングのせいで 5 分目は 1 分目より 20〜30% 遅くなります——大事なのはそちらの数字です。