Flying Gorilla Studios

モバイルゲーム最適化の極意:Flying Gorillaで実践した10のテクニック

モバイルゲーム最適化の極意:Flying Gorillaで実践した10のテクニック

こんにちは、Flying Gorilla StudiosのテクニカルリードのChen Weiです。今回は、『Flying Gorilla』の開発で実際に使用した最適化テクニックを共有したいと思います。400万ダウンロードを支える技術の裏側をお見せします!

なぜ最適化が重要なのか

モバイルゲーム開発において、最適化は単なる「あったらいいもの」ではありません。必須です。なぜなら:

  • デバイスの多様性 - ハイエンドからローエンドまで幅広い
  • バッテリー寿命 - 熱くなるゲームは嫌われる
  • データ通信量 - 特に新興国市場では重要
  • ストレージ容量 - アプリサイズは小さいほど良い

1. テクスチャアトラス最適化

Before

// 悪い例:個別のスプライトを読み込む
Sprite banana = Resources.Load<Sprite>("Sprites/banana");
Sprite coin = Resources.Load<Sprite>("Sprites/coin");
Sprite powerup = Resources.Load<Sprite>("Sprites/powerup");

After

// 良い例:アトラスから一括で読み込む
Sprite[] gameSprites = Resources.LoadAll<Sprite>("Sprites/GameAtlas");
Dictionary<string, Sprite> spriteDict = new Dictionary<string, Sprite>();
foreach(var sprite in gameSprites) {
    spriteDict.Add(sprite.name, sprite);
}

結果: ドローコールが120から15に削減!

2. オブジェクトプーリング

Flying Gorillaでは、バナナや障害物が頻繁に生成・破壊されます。これを最適化するためにオブジェクトプーリングを実装しました。

public class ObjectPool<T> where T : MonoBehaviour {
    private Queue<T> pool = new Queue<T>();
    private T prefab;
    private Transform parent;
    
    public ObjectPool(T prefab, int initialSize, Transform parent) {
        this.prefab = prefab;
        this.parent = parent;
        
        for(int i = 0; i < initialSize; i++) {
            T obj = GameObject.Instantiate(prefab, parent);
            obj.gameObject.SetActive(false);
            pool.Enqueue(obj);
        }
    }
    
    public T Get() {
        T obj = pool.Count > 0 ? pool.Dequeue() : GameObject.Instantiate(prefab, parent);
        obj.gameObject.SetActive(true);
        return obj;
    }
    
    public void Return(T obj) {
        obj.gameObject.SetActive(false);
        pool.Enqueue(obj);
    }
}

結果: GCスパイクが90%削減、フレームレートが安定

3. LOD(Level of Detail)システム

遠くのオブジェクトは簡易モデルに切り替えます。

public class SimpleLOD : MonoBehaviour {
    public Mesh highDetailMesh;
    public Mesh lowDetailMesh;
    public float switchDistance = 50f;
    
    private MeshFilter meshFilter;
    private Transform player;
    
    void Start() {
        meshFilter = GetComponent<MeshFilter>();
        player = GameObject.FindWithTag("Player").transform;
    }
    
    void Update() {
        float distance = Vector3.Distance(transform.position, player.position);
        meshFilter.mesh = distance > switchDistance ? lowDetailMesh : highDetailMesh;
    }
}

4. カリングの活用

フラスタムカリング

Unityのデフォルト機能ですが、適切に設定することが重要です。

// カメラの設定
Camera.main.farClipPlane = 100f; // 必要以上に遠くを描画しない

オクルージョンカリング

静的なオブジェクトにはOcclusion Cullingを設定します。

5. シェーダー最適化

モバイル向けの軽量シェーダーを作成しました。

Shader "Mobile/SimpleDiffuse" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _Color ("Color", Color) = (1,1,1,1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100
        
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_fog
            
            #include "UnityCG.cginc"
            
            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };
            
            struct v2f {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };
            
            sampler2D _MainTex;
            float4 _MainTex_ST;
            fixed4 _Color;
            
            v2f vert (appdata v) {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target {
                fixed4 col = tex2D(_MainTex, i.uv) * _Color;
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

6. メモリ管理

テクスチャ圧縮

// iOS向け
textureImporter.textureCompression = TextureImporterCompression.CompressedHQ;
textureImporter.format = TextureImporterFormat.PVRTC_RGB4;

// Android向け
textureImporter.format = TextureImporterFormat.ETC2_RGB4;

オーディオ圧縮

audioImporter.defaultSampleSettings = new AudioImporterSampleSettings {
    loadType = AudioClipLoadType.CompressedInMemory,
    compressionFormat = AudioCompressionFormat.MP3,
    quality = 0.7f
};

7. バッチング最適化

Static Batching

静的なオブジェクトには必ずStatic flagを設定:

GameObject.isStatic = true;

Dynamic Batching

  • 頂点数を300以下に抑える
  • 同じマテリアルを使用する
  • スケールは統一する

8. UI最適化

Canvas分割

// 頻繁に更新されるUI(スコアなど)
Canvas dynamicCanvas;

// 静的なUI(ボタンなど)
Canvas staticCanvas;

Raycast Target無効化

// クリックされないUIエレメントは無効に
image.raycastTarget = false;
text.raycastTarget = false;

9. アニメーション最適化

Animatorの軽量化

// 不要な時はAnimatorを無効化
if (!isVisible) {
    animator.enabled = false;
}

Simple Animationの使用

複雑なステートマシンが不要な場合:

public class SimpleRotate : MonoBehaviour {
    public float speed = 30f;
    
    void Update() {
        transform.Rotate(Vector3.up * speed * Time.deltaTime);
    }
}

10. プロファイリングとモニタリング

実機でのプロファイリング

public class PerformanceMonitor : MonoBehaviour {
    private float deltaTime = 0.0f;
    
    void Update() {
        deltaTime += (Time.unscaledDeltaTime - deltaTime) * 0.1f;
    }
    
    void OnGUI() {
        int fps = Mathf.Ceil(1.0f / deltaTime);
        GUI.Label(new Rect(10, 10, 100, 20), "FPS: " + fps);
    }
}

メモリ使用量の監視

string memoryInfo = string.Format(
    "Total: {0:F2} MB, Used: {1:F2} MB", 
    SystemInfo.systemMemorySize / 1024f,
    GC.GetTotalMemory(false) / 1048576f
);

最適化の成果

これらの最適化により、Flying Gorillaは:

  • 起動時間: 8秒 → 3秒
  • メモリ使用量: 180MB → 95MB
  • バッテリー消費: 40%削減
  • 平均FPS: 45fps → 安定した60fps
  • アプリサイズ: 120MB → 65MB

デバイス別の最適化

ローエンドデバイス向け

if (SystemInfo.systemMemorySize < 2048) {
    QualitySettings.SetQualityLevel(0); // Low Quality
    Application.targetFrameRate = 30;
}

ハイエンドデバイス向け

if (SystemInfo.graphicsMemorySize > 2048) {
    QualitySettings.SetQualityLevel(2); // High Quality
    // パーティクルエフェクト有効化
    particleSystem.gameObject.SetActive(true);
}

まとめ

モバイルゲームの最適化は、継続的なプロセスです。一度やって終わりではなく、新機能を追加するたびに見直す必要があります。

重要なのは:

  1. 計測する - 推測ではなくデータに基づいて判断
  2. 優先順位をつける - 最も影響の大きい部分から着手
  3. テストする - 様々なデバイスで確認
  4. バランスを取る - 品質と性能のバランス

これらのテクニックが、あなたのゲーム開発に役立つことを願っています。質問があれば、ぜひコメントやDiscordでお聞かせください!

Happy Optimizing! 🚀


Chen Wei
Technical Lead, Flying Gorilla Studios

モバイルゲーム最適化の極意:Flying Gorillaで実践した10のテクニック
Prev post

インディーゲームスタジオの1日:Flying Gorilla Studiosの舞台裏

Next post

インディーゲーム開発の道のり:Flying Gorillaが400万ダウンロードに至るまで

モバイルゲーム最適化の極意:Flying Gorillaで実践した10のテクニック

お問い合わせ

ご意見、ご感想、お仕事のご依頼など、お気軽にお問い合わせください。