Eng unity mobile optimization
Skill IdoCohen560/claude-unity-game-studio/unity-middleware-skills/skills/eng-unity-mobile-optimization
Mobile-specific Unity optimization patterns for memory, battery, thermal, and performance.From its SKILL.md
npx -y skills add IdoCohen560/claude-unity-game-studio --skill eng-unity-mobile-optimizationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 12 stars12 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
14.7 KB, ~3.2k tokens by cl100k_base, as published. Nobody here has run it
Unity Mobile Optimization
Overview
Mobile platforms have unique constraints: limited memory, battery life, thermal throttling, and variable hardware. This skill covers mobile-specific optimization techniques.
When to Use
- iOS and Android development
- Battery-sensitive applications
- Memory-constrained devices
- Thermal management
- Variable quality settings
Memory Management
Memory Budget System
public class MobileMemoryManager : MonoBehaviour
{
[SerializeField] private long _warningThresholdMB = 800;
[SerializeField] private long _criticalThresholdMB = 1000;
public event Action OnMemoryWarning;
public event Action OnMemoryCritical;
private void Awake()
{
// iOS memory warning callback
Application.lowMemory += OnLowMemory;
}
private void OnDestroy()
{
Application.lowMemory -= OnLowMemory;
}
private void OnLowMemory()
{
Debug.LogWarning("Low memory warning received");
OnMemoryCritical?.Invoke();
// Emergency cleanup
Resources.UnloadUnusedAssets();
GC.Collect();
}
public MemoryStatus GetMemoryStatus()
{
long usedMB = Profiler.GetTotalAllocatedMemoryLong() / 1_000_000;
if (usedMB >= _criticalThresholdMB)
return MemoryStatus.Critical;
if (usedMB >= _warningThresholdMB)
return MemoryStatus.Warning;
return MemoryStatus.Normal;
}
public void RequestCleanup()
{
// Aggressive cleanup
StartCoroutine(CleanupRoutine());
}
private IEnumerator CleanupRoutine()
{
// Unload unused assets
var operation = Resources.UnloadUnusedAssets();
yield return operation;
// GC with minimal pause
GC.Collect(0, GCCollectionMode.Optimized);
yield return null;
GC.Collect(1, GCCollectionMode.Optimized);
}
}
public enum MemoryStatus { Normal, Warning, Critical }
Texture Memory Control
public class TextureMemoryController
{
public static void SetQualityForDevice(DeviceTier tier)
{
switch (tier)
{
case DeviceTier.Low:
QualitySettings.globalTextureMipmapLimit = 2; // 1/4 resolution
QualitySettings.anisotropicFiltering = AnisotropicFiltering.Disable;
break;
case DeviceTier.Medium:
QualitySettings.globalTextureMipmapLimit = 1; // 1/2 resolution
QualitySettings.anisotropicFiltering = AnisotropicFiltering.Enable;
break;
case DeviceTier.High:
QualitySettings.globalTextureMipmapLimit = 0; // Full resolution
QualitySettings.anisotropicFiltering = AnisotropicFiltering.ForceEnable;
break;
}
}
public static void ForceReduceTextureMemory()
{
// Increase mipmap limit temporarily
QualitySettings.globalTextureMipmapLimit++;
// Force GPU to release memory
Resources.UnloadUnusedAssets();
}
}
Battery Optimization
Frame Rate Control
public class BatteryOptimizer : MonoBehaviour
{
[SerializeField] private int _highPerformanceFps = 60;
[SerializeField] private int _balancedFps = 30;
[SerializeField] private int _batterySaverFps = 24;
private PerformanceMode _currentMode = PerformanceMode.Balanced;
public void SetPerformanceMode(PerformanceMode mode)
{
_currentMode = mode;
switch (mode)
{
case PerformanceMode.HighPerformance:
Application.targetFrameRate = _highPerformanceFps;
QualitySettings.vSyncCount = 0;
break;
case PerformanceMode.Balanced:
Application.targetFrameRate = _balancedFps;
QualitySettings.vSyncCount = 1;
break;
case PerformanceMode.BatterySaver:
Application.targetFrameRate = _batterySaverFps;
QualitySettings.vSyncCount = 2;
// Additional battery saving
Screen.brightness = 0.5f;
break;
}
}
public void SetAdaptiveFrameRate(bool inCombat)
{
// Dynamic frame rate based on gameplay
if (inCombat)
{
Application.targetFrameRate = _highPerformanceFps;
}
else
{
Application.targetFrameRate = _balancedFps;
}
}
}
public enum PerformanceMode { HighPerformance, Balanced, BatterySaver }
Background Behavior
public class BackgroundHandler : MonoBehaviour
{
private bool _wasInBackground;
private void OnApplicationPause(bool pauseStatus)
{
if (pauseStatus)
{
// Going to background
OnEnterBackground();
}
else
{
// Returning from background
OnExitBackground();
}
_wasInBackground = pauseStatus;
}
private void OnEnterBackground()
{
// Pause audio
AudioListener.pause = true;
// Stop expensive updates
Time.timeScale = 0;
// Release non-essential resources
Resources.UnloadUnusedAssets();
}
private void OnExitBackground()
{
AudioListener.pause = false;
Time.timeScale = 1;
// Check if device overheated while backgrounded
CheckThermalState();
}
}
Thermal Management
Thermal State Monitoring
public class ThermalManager : MonoBehaviour
{
public event Action<ThermalState> OnThermalStateChanged;
private ThermalState _currentState = ThermalState.Normal;
private float _checkInterval = 5f;
private void Start()
{
InvokeRepeating(nameof(CheckThermalState), 0, _checkInterval);
}
private void CheckThermalState()
{
#if UNITY_IOS
var newState = GetIOSThermalState();
#elif UNITY_ANDROID
var newState = GetAndroidThermalState();
#else
var newState = ThermalState.Normal;
#endif
if (newState != _currentState)
{
_currentState = newState;
OnThermalStateChanged?.Invoke(newState);
ApplyThermalMitigation(newState);
}
}
#if UNITY_IOS
private ThermalState GetIOSThermalState()
{
// iOS provides thermal state via native plugin
// ProcessInfo.ThermalState
return ThermalState.Normal; // Implement native plugin
}
#endif
#if UNITY_ANDROID
private ThermalState GetAndroidThermalState()
{
// Android thermal API (API 29+)
// PowerManager.getCurrentThermalStatus()
return ThermalState.Normal; // Implement native plugin
}
#endif
private void ApplyThermalMitigation(ThermalState state)
{
switch (state)
{
case ThermalState.Normal:
// Full performance
Application.targetFrameRate = 60;
QualitySettings.SetQualityLevel(2);
break;
case ThermalState.Fair:
// Slight reduction
Application.targetFrameRate = 45;
break;
case ThermalState.Serious:
// Significant reduction
Application.targetFrameRate = 30;
QualitySettings.SetQualityLevel(1);
break;
case ThermalState.Critical:
// Emergency mode
Application.targetFrameRate = 24;
QualitySettings.SetQualityLevel(0);
ShowThermalWarning();
break;
}
}
private void ShowThermalWarning()
{
Debug.LogWarning("Device overheating - reducing performance");
// Show UI warning to user
}
}
public enum ThermalState { Normal, Fair, Serious, Critical }
Device Tier Detection
Hardware Classification
public class DeviceClassifier
{
public static DeviceTier ClassifyDevice()
{
int systemMemoryMB = SystemInfo.systemMemorySize;
int processorCount = SystemInfo.processorCount;
int graphicsMemoryMB = SystemInfo.graphicsMemorySize;
// Calculate tier based on specs
int score = 0;
// Memory score
if (systemMemoryMB >= 6000) score += 3;
else if (systemMemoryMB >= 4000) score += 2;
else if (systemMemoryMB >= 2000) score += 1;
// CPU score
if (processorCount >= 8) score += 3;
else if (processorCount >= 6) score += 2;
else if (processorCount >= 4) score += 1;
// GPU score
if (graphicsMemoryMB >= 4000) score += 3;
else if (graphicsMemoryMB >= 2000) score += 2;
else if (graphicsMemoryMB >= 1000) score += 1;
// Classify
if (score >= 7) return DeviceTier.High;
if (score >= 4) return DeviceTier.Medium;
return DeviceTier.Low;
}
public static void LogDeviceInfo()
{
Debug.Log($"Device: {SystemInfo.deviceModel}");
Debug.Log($"OS: {SystemInfo.operatingSystem}");
Debug.Log($"RAM: {SystemInfo.systemMemorySize}MB");
Debug.Log($"CPU: {SystemInfo.processorType} x{SystemInfo.processorCount}");
Debug.Log($"GPU: {SystemInfo.graphicsDeviceName} ({SystemInfo.graphicsMemorySize}MB)");
Debug.Log($"Tier: {ClassifyDevice()}");
}
}
public enum DeviceTier { Low, Medium, High }
Quality Presets
public class QualityPresetManager
{
public static void ApplyPresetForTier(DeviceTier tier)
{
var preset = GetPreset(tier);
// Graphics
QualitySettings.SetQualityLevel(preset.QualityLevel);
QualitySettings.shadows = preset.Shadows;
QualitySettings.shadowResolution = preset.ShadowResolution;
QualitySettings.antiAliasing = preset.AntiAliasing;
// Rendering
QualitySettings.pixelLightCount = preset.PixelLights;
QualitySettings.globalTextureMipmapLimit = preset.TextureMipLevel;
// Performance
Application.targetFrameRate = preset.TargetFps;
// Physics
Time.fixedDeltaTime = 1f / preset.PhysicsRate;
Debug.Log($"Applied quality preset for {tier}: {preset.QualityLevel}");
}
private static QualityPreset GetPreset(DeviceTier tier)
{
return tier switch
{
DeviceTier.Low => new QualityPreset
{
QualityLevel = 0,
Shadows = ShadowQuality.Disable,
ShadowResolution = ShadowResolution.Low,
AntiAliasing = 0,
PixelLights = 1,
TextureMipLevel = 2,
TargetFps = 30,
PhysicsRate = 30
},
DeviceTier.Medium => new QualityPreset
{
QualityLevel = 2,
Shadows = ShadowQuality.HardOnly,
ShadowResolution = ShadowResolution.Medium,
AntiAliasing = 2,
PixelLights = 2,
TextureMipLevel = 1,
TargetFps = 45,
PhysicsRate = 45
},
_ => new QualityPreset
{
QualityLevel = 4,
Shadows = ShadowQuality.All,
ShadowResolution = ShadowResolution.High,
AntiAliasing = 4,
PixelLights = 4,
TextureMipLevel = 0,
TargetFps = 60,
PhysicsRate = 60
}
};
}
private struct QualityPreset
{
public int QualityLevel;
public ShadowQuality Shadows;
public ShadowResolution ShadowResolution;
public int AntiAliasing;
public int PixelLights;
public int TextureMipLevel;
public int TargetFps;
public int PhysicsRate;
}
}
Render Pipeline Optimization
Dynamic Resolution
public class DynamicResolutionController : MonoBehaviour
{
[SerializeField] private float _targetFrameTime = 16.67f; // 60 FPS
[SerializeField] private float _minScale = 0.5f;
[SerializeField] private float _maxScale = 1f;
private float _currentScale = 1f;
private float _smoothedFrameTime;
private void Update()
{
// Smooth frame time
float frameTime = Time.unscaledDeltaTime * 1000f;
_smoothedFrameTime = Mathf.Lerp(_smoothedFrameTime, frameTime, 0.1f);
// Adjust scale
if (_smoothedFrameTime > _targetFrameTime * 1.2f)
{
// Too slow - reduce resolution
_currentScale = Mathf.Max(_minScale, _currentScale - 0.05f);
}
else if (_smoothedFrameTime < _targetFrameTime * 0.8f)
{
// Room for improvement - increase resolution
_currentScale = Mathf.Min(_maxScale, _currentScale + 0.02f);
}
ScalableBufferManager.ResizeBuffers(_currentScale, _currentScale);
}
}
LOD Bias
public class LODController
{
public static void SetLODForTier(DeviceTier tier)
{
switch (tier)
{
case DeviceTier.Low:
QualitySettings.lodBias = 0.5f;
QualitySettings.maximumLODLevel = 1;
break;
case DeviceTier.Medium:
QualitySettings.lodBias = 1f;
QualitySettings.maximumLODLevel = 0;
break;
case DeviceTier.High:
QualitySettings.lodBias = 1.5f;
QualitySettings.maximumLODLevel = 0;
break;
}
}
}
Best Practices
- Profile on actual devices - Not just Editor
- Set memory budgets per device tier
- Monitor thermal state and adapt
- Use dynamic frame rate based on gameplay
- Implement quality tiers automatically
- Handle background/foreground transitions
- Pool and reuse everything
- Compress textures appropriately
- Limit draw calls to <100 on low-end
- Test on minimum spec devices
Troubleshooting
| Issue | Solution |
|---|---|
| App killed by OS | Reduce memory, handle lowMemory |
| Device overheating | Monitor thermal, reduce quality |
| Battery drain | Lower frame rate, disable features |
| Stuttering | Profile GC, use pools |
| Long load times | Stream assets, show progress |
| Crash on low-end | Test minimum spec, reduce quality |
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.
Gives 0 of the 12 instructions most performance cost skills give in ~3.2k tokens
Counted across 797 of the 1,117 authors here whose files we hold, read 2026-09-06
- Check for product marketing context firstin 46 of 797, across 20 files
- Measure before optimizingin 31 of 797, across 25 files
- Profile first to identify the actual bottleneckin 23 of 797, across 22 files
- Verify your robots.txt allows AI crawlersin 21 of 797, across 12 files
- Import directly and avoid barrel filesin 19 of 797, across 15 files
- Spawn all runs in the same turnin 18 of 797, across 11 files
- Write a draft of the skillin 17 of 797, across 10 files
- Understand the user's intentin 17 of 797, across 10 files
- Use React.cache for per-request deduplicationin 16 of 797, across 11 files
- Profile before optimizingin 16 of 797, across 14 files
- Include specific numbers with sourcesin 15 of 797, across 8 files
- Add lazy loading to below-fold imagesin 15 of 797, across 10 files
Said here and by no other author read
- Profile on actual devices
- Set memory budgets per device tier
- Monitor thermal state and adapt
- Use dynamic frame rate
- Implement quality tiers automatically
- Handle background transitions
Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.