Lesson 8: Performance & Battery Optimization

Your puzzle loops are fun and sticky, but if the game melts phones after ten minutes, players churn. In this lesson you will profile the project, remove wasted milliamps, and guarantee 60 FPS on mid-range Android/iOS hardware. Expect to dive into Unity Profiler, Adaptive Performance, batching, texture compression, and power-aware scripting.


Learning Outcomes

By the end you will:

  1. Capture actionable profiling data on a real device (not just in Editor).
  2. Apply frame-rate and thermal budgets using Unity Adaptive Performance.
  3. Cut CPU/GPU spikes with batching, sprite atlases, and scheduled coroutines.
  4. Measure power impact and lock in battery-friendly rendering defaults.

1. Profile on Device (Not in Editor)

  1. Connect your Android/iOS device via USB and enable Developer Mode.
  2. In Unity go to Window → Analysis → Profiler, click Attach to Player, select your device.
  3. Record a 30-second session while completing a typical puzzle loop.
  4. Switch to the Timeline view, mark spikes >16 ms, and tag them (Input, Rendering, Scripts).

Pro Tip
Use the Profiler Marker API to label expensive custom code:

using Unity.Profiling;
static readonly ProfilerMarker HintMarker = new("HintHighlight");

void HighlightHintTiles()
{
    HintMarker.Begin();
    // expensive logic
    HintMarker.End();
}

Checklist

  • [ ] Tested on at least one Android and one iOS device
  • [ ] Captured CPU, GPU, and Memory modules
  • [ ] Identified top 3 spikes by subsystem

2. Apply Adaptive Performance Budgets

Unity’s Adaptive Performance (AP) package (Package Manager → Adaptive Performance XR or vendor-specific provider) lets you react to thermal events before the OS throttles.

using UnityEngine.AdaptivePerformance;

public class PerformanceGovernor : MonoBehaviour
{
    IAdaptivePerformance ap;

    void Awake()
    {
        ap = Holder.Instance?.AdaptivePerformance;
    }

    void Update()
    {
        if (ap == null) return;

        // Drop VFX when thermal level hits warning
        bool thermalWarning = ap.ThermalStatus.ThermalMetrics.WarningLevel 
                              >= WarningLevel.ThrottlingImminent;
        VFXManager.Instance.SetBudget(thermalWarning ? 0.4f : 1f);

        // Cap frame rate dynamically
        Application.targetFrameRate = ap.PerformanceStatus.PerformanceMetrics.CurrentCpuLevel > 2
            ? 45 : 60;
    }
}

Why it matters: Lowering VFX density and frame caps keeps the device cool, preventing forced throttling that would tank your player experience anyway.


3. Reduce Draw Calls and Overdraw

  1. Sprite Atlas: Window → 2D → Sprite Atlas, add UI/puzzle sprites to a single atlas. Enable Tight Packing and Use SRP Batcher (if using URP).
  2. Batch UI: Group static UI under one Canvas, set Canvas Additional Shader Channels to minimal.
  3. Disable Transparency where possible—transparent UI with gradients over the whole screen is a battery killer.
  4. Use Frame Debugger to confirm draw-call reductions.

Common Mistake
Animated background gradients running at 60 FPS on every scene. Clamp their update loop to every 0.25s or fade them out entirely during gameplay.


4. Schedule Workloads

Coroutines and async tasks should avoid per-frame garbage.

IEnumerator DrainPowerFriendly()
{
    var wait = new WaitForSecondsRealtime(0.2f);
    while (true)
    {
        HintManager.TickHints();   // cheap check
        yield return wait;         // runs 5x/sec instead of every Update
    }
}
  • Move analytics pings, hint scans, and “next puzzle” preloads into job queues running a few times per second.
  • Replace InvokeRepeating with a single scheduler MonoBehaviour to cut allocations.

5. Optimize Assets & Rendering

Asset Type Action Tool
Textures Switch to ASTC 6x6 (Android) / PVRTC (iOS) for static art Texture Import Settings
Audio Convert long music tracks to Vorbis, 96 kbps, stream from disk Audio Clip Import
Shaders Use URP Lit (Simple), enable SRP Batcher Graphics Settings
Post Effects Disable per-camera Bloom/Chromatic Aberration in puzzle scenes Volume Profiles

Test each change using Build Report Inspector to ensure APK/IPA size stays manageable.


6. Battery Verification

  1. Android: use ADB command
    adb shell dumpsys batterystats --reset
    adb shell am start -n com.company.puzzlegame/com.unity3d.player.UnityPlayerActivity

    Play for 10 minutes, then adb shell dumpsys batterystats > report.txt.

  2. iOS: profile with Instruments → Energy Log.
  3. Compare before/after results; target < 250 mW average drain on mid-range hardware.

If drain is still high, revisit:

  • Physics settings (reduce Fixed Timestep to 0.02 for simple puzzles)
  • Background coroutines (ensure they suspend when app is paused)
  • Ads/SDKs (disable refresh timers while paused)

Mini Challenge

  1. Record a profiler session before optimizations.
  2. Implement two batching improvements plus one adaptive-performance hook.
  3. Record a new session and post your before/after frame-time charts in the community Discord.

Troubleshooting

  • “Adaptive Performance instance is null” → Ensure the provider package is installed (Samsung, Android, etc.) and enabled via Project Settings → Adaptive Performance.
  • Texture atlases look blurry → Increase Max Size to 2048 and disable Generate Mip Maps for UI atlases.
  • Battery drain unchanged → Check if third-party SDKs (ads, analytics) keep the CPU awake. Wrap them in OnApplicationPause.

Recap & Next Lesson

You profiled on hardware, budgeted thermal limits, removed rendering waste, and verified wins using battery logs. Next up is [Lesson 9: In-App Purchases & Monetization] where you’ll wire up consumables, coins, and a storefront that runs smoothly thanks to today’s optimizations.

Bookmark this lesson and share it with your teammates responsible for build QA—performance improvements compound when the whole team understands the playbook.


Further Reading

Ready? Push a “performance-optimized” commit and shout it out in community so other puzzle builders can copy your workflow.