Unityでのスマホゲーム開発において、「端末のスペックに合わせて画質を自動調整したい」というのは永遠の課題です。
単純にメモリ量(RAM)だけで判定すると、「メモリは多いけどGPUが弱いAndroidタブレット」でカクついたり、「メモリは少ないけど超高性能なiPhone」で低画質判定されたりと、事故が多発します。
今回は、これらの落とし穴(メモリ詐欺など)を考慮した、5段階の自動スペック判定ロジックを紹介します。
この記事で実現すること
- CPU性能は無視する(あてにならないため)
- Androidの「メモリ詐欺」対策(メモリ8GBでもGPUが弱ければ低画質にする)
- iOSの「高効率」対応(メモリが少なくても高画質判定にする)
- 5段階評価(VeryLow / Low / Medium / High / Ultra)で判定結果を返す
なぜCPUを見ないのか?
SystemInfo.processorFrequency などでCPU情報を取得することは可能ですが、スマホ開発では推奨されません。
- Androidの仕様: 正確なクロック数が取得できず「0」が返ってくることが多い。
- SoCの構造: スマホのチップ(SoC)はCPU/GPU/メモリがセットです。「GPUとメモリが優秀なら、CPUも間違いなく優秀」という相関関係があるため、CPUを個別にチェックする必要はありません。
判定の落とし穴と対策
1. Android:メモリは多いけどGPUが弱い(通称:中華タブレットの罠)
格安のAndroidタブレットなどには、カタログスペックを良く見せるために RAM 8GB を積んでいるものの、GPU性能はiPhone 7以下……という機種が存在します。
これをメモリ量だけで「Ultra(最高画質)」と判定すると、ゲームがカクカクになります。
対策:
「GPUの性能(GraphicsTier)」を見て、画質ランクに上限(キャップ)を設けます。
2. iOS:メモリは少ないけど超高性能
iPhoneはOSのメモリ管理が優秀なため、Androidと同じ基準(4GBは低スペック扱いなど)を適用すると、iPhone 12などの高性能機まで低画質にされてしまいます。
対策:
iOSの場合は判定基準のボーダーラインを大幅に下げます。
実装コード
このクラスをプロジェクトに追加し、起動時(Awakeなど)に DetermineQualityTier() を呼ぶだけで、最適なランクが返ってきます。
using UnityEngine;
using UnityEngine.Rendering;
/// <summary>
/// 端末のハードウェア性能を判定し、適切な画質ランクを返すクラス
/// </summary>
public static class PerformanceManager
{
// クオリティの5段階定義
public enum QualityTier
{
VeryLow = 0, // 最低:影なし、解像度ダウン(緊急避難用)
Low = 1, // 低:標準シェーダー、影簡易
Medium = 2, // 中:標準的な画質
High = 3, // 高:ポストプロセスあり
Ultra = 4 // 最高:フルオプション
}
/// <summary>
/// 端末スペックから適切なQualityTierを判定して返す
/// </summary>
public static QualityTier DetermineQualityTier()
{
int systemMemory = SystemInfo.systemMemorySize; // メモリ (MB)
GraphicsTier graphicsTier = Graphics.activeTier; // GPU性能 (Tier1~3)
bool isIos = Application.platform == RuntimePlatform.IPhonePlayer;
// =========================================================
// 1. GPU性能による「上限キャップ」を決める (Androidのメモリ詐欺対策)
// =========================================================
QualityTier maxAllowedTier;
if (graphicsTier == GraphicsTier.Tier1)
{
// GPUが弱い(Tier1)なら、メモリがどれだけあっても「Low」までしか許さない
// これにより、メモリだけ大量に積んだSoCの弱い格安端末を弾く
maxAllowedTier = QualityTier.Low;
}
else if (graphicsTier == GraphicsTier.Tier2)
{
// GPUが並(Tier2)なら、最高画質(Ultra)は禁止し「High」で止める
// ※Tier2の範囲は広いため、安全側に倒すなら Medium にキャップしても良い
maxAllowedTier = QualityTier.High;
}
else // Tier3
{
// GPUが最強(Tier3)なら上限なし
maxAllowedTier = QualityTier.Ultra;
}
// =========================================================
// 2. メモリ量ベースで「希望ランク」を決める
// =========================================================
QualityTier targetTier;
if (isIos)
{
// --- iOS (メモリ効率が良いので基準値を下げる) ---
if (systemMemory < 2500) targetTier = QualityTier.VeryLow; // iPhone 8等 (2GB)
else if (systemMemory < 3500) targetTier = QualityTier.Low; // iPhone X/XR等 (3GB)
else if (systemMemory < 4500) targetTier = QualityTier.Medium; // iPhone 11/12/13等 (4GB)
else if (systemMemory < 6500) targetTier = QualityTier.High; // iPhone 13 Pro/14/15等 (6GB)
else targetTier = QualityTier.Ultra; // iPad Pro/iPhone 15 Pro等 (8GB~)
}
else
{
// --- Android (メモリ消費が激しいので基準値を上げる) ---
if (systemMemory < 3500) targetTier = QualityTier.VeryLow;
else if (systemMemory < 4500) targetTier = QualityTier.Low;
else if (systemMemory < 6500) targetTier = QualityTier.Medium;
else if (systemMemory < 7500) targetTier = QualityTier.High;
else targetTier = QualityTier.Ultra; // 8GB以上
}
// =========================================================
// 3. 最終決定:希望と上限の「低い方」を採用する (安全策)
// =========================================================
// 例:Androidでメモリ8GB(希望Ultra) だが GPUが弱い(上限Low) の場合
// Min(Ultra, Low) -> Low になり、カクつきを防げる。
QualityTier finalTier = (QualityTier)Mathf.Min((int)targetTier, (int)maxAllowedTier);
Debug.Log($"Spec Check | Mem: {systemMemory}MB | GPU: {graphicsTier} | Result: {finalTier}");
return finalTier;
}
}
解説:このコードの挙動シミュレーション
Mathf.Min を使って「希望」と「現実(上限)」の低い方を採用することで、以下のようにあらゆるパターンの端末を正しく捌くことができます。
| 端末の例 | メモリ判定 (希望) | GPU判定 (上限) | 最終結果 | 判定理由 |
|---|---|---|---|---|
|
格安中華タブレット (RAM 8GB / SoC Unisoc) |
Ultra (メモリは多い) |
Low (GPUがTier1) |
Low | GPUの弱さをGPU判定で見抜いてLowに落とす。 |
|
AQUOS sense6 (RAM 6GB / SoC SD690) |
Medium |
High (Tier2) |
Medium | Tier2の上限には引っかからないのでメモリ通りの判定。 |
|
iPhone 8 (RAM 2GB / SoC A11) |
VeryLow |
High (Tier2) |
VeryLow | GPUはまだ戦えるが、メモリ不足によるクラッシュを防ぐ。 |
|
Galaxy S23 (RAM 8GB / SoC SD8Gen2) |
Ultra |
Ultra (Tier3) |
Ultra | 全てが最強なのでUltra。 |
おまけ:解像度(DPI)も見よう
GPU性能が良くても、最近のスマホは解像度が高すぎます(Xperia 1の4Kなど)。
判定されたTierに応じて、Screen.SetResolution で解像度も調整すると完璧です。
- VeryLow / Low: 720p相当にダウンスケール
- Medium: 1080p相当
- High / Ultra: ネイティブ解像度(または上限を1440pにするなど)
まとめ
スマホのスペック判定は「メモリ詐欺」と「iOS/Androidの格差」との戦いです。
ご自身のプロジェクトに合わせて微調整してみてください。