A phone has no fan. It has a slab of glass, a chip rated for a few watts of sustained load, and a battery that will happily turn into a hand warmer if you ask too much of it. Your Unity game ran at a glorious 60 frames per second in the editor on a desktop with a dedicated GPU. Ten minutes into a session on a mid-range Android phone, it runs at 24, and the device is hot enough to be uncomfortable. Nothing is broken. The hardware is behaving exactly as designed, and the game was never built for it.

This is a field manual for closing that gap. It is ordered the way an experienced team triages a slow build: measure first, find the real bottleneck, then fix things in order of payoff. Every number below comes from Unity’s own guidance or from widely cited mobile rules of thumb, and the rules of thumb are labeled as such, because the only budget that matters in the end is the one you measure on your lowest supported device.

Heat Is the Real Frame Budget

Desktop performance is a race. Mobile performance is an endurance event. A phone can hit its peak clock speeds for a short burst, then the system lowers them to keep temperatures safe, and your carefully tuned frame rate falls with it. This is thermal throttling, and it explains why a game can pass a two-minute benchmark and fail a thirty-minute playtest.

Two consequences shape every decision that follows. First, efficiency beats peak speed. A game that finishes each frame with spare time lets the chip idle between frames and stay cool. A game that barely makes 16 milliseconds will cook the device. Second, the target frame rate is a design choice with a thermal price tag. Unity defaults to 30 frames per second on mobile for good reason, and many successful titles stay there. Choosing 60 should be a decision you pay for, with a performance budget to match, rather than a default you inherit.

Rule Zero: Measure on the Device

Profiling in the editor measures your workstation. Every optimization decision should come from a development build running on real hardware with the Profiler attached. Unity’s toolkit covers most of what you need:

  • Unity Profiler: CPU usage, rendering, memory, and audio timelines, connected to a device over USB or the network.
  • Frame Debugger: steps through a frame draw call by draw call so you can see exactly what is being rendered and why batches break.
  • Memory Profiler package: snapshots of managed and native memory, with the ability to compare two snapshots and find leaks.
  • Platform tools: Xcode Instruments on iOS, and Android Studio profilers or vendor GPU tools on Android, for GPU time, thermals, and power draw that Unity’s profiler cannot see.

One caution from experience: Deep Profiling mode adds overhead to every call and distorts the numbers. Use it to chase a specific script problem, then switch it off. For general work, stay with the standard profiler and judge frame time consistency instead of just average frame rate. A steady 30 frames per second feels better than an average of 45 that swings between 25 and 60.

What Can Actually Be Outsourced?

Game art outsourcing covers a wide range of disciplines: 2D and 3D character design, environment art, UI and UX design, concept art, marketing and key art, animation, and technical art like rigging and shader work. A studio can outsource a single asset category or run an entire art pipeline through an external partner, depending on internal capacity and budget.

Call To Action

Know Your Frame Budget

Everything starts with arithmetic. The time available per frame is one second divided by the target frame rate.

Target FPS Time Per Frame Typical Use
30 33.3 ms Default mobile target, strategy, puzzle, casual, many 3D titles
60 16.7 ms Action games, racing, anything where input latency matters
90 or 120 11.1 ms or 8.3 ms High refresh devices, usually for menus or simple scenes

 Your frame has to fit CPU work and GPU work inside that window, and the two run in parallel, so the slower side sets the pace. This is why the next step is working out which side is the bottleneck.

Find the Bottleneck Before Touching Anything

Optimizing the wrong side is the most common way to waste a week. If the GPU is the limit, reducing script cost changes nothing. If the CPU is the limit, shrinking textures does the same.

What You See in the Profiler Likely Bottleneck First Things to Try
Main thread is full, GPU has idle time CPU-bound Reduce draw calls and scripts, enable batching, pool objects
Main thread waits on render thread or GPU GPU-bound Reduce overdraw, resolution, shader cost, and shadow work
Frame time spikes at irregular intervals Garbage collection Remove per-frame allocations, pool, enable incremental GC
Game crashes or restarts after a few scenes Memory Memory Profiler, compress textures, unload unused assets
Fine at first, slow after ten minutes Thermal throttling Lower sustained workload, lower frame rate target, adaptive quality

Run the check on your slowest supported device, not your best one. That device is the real specification.

Rule One: Draw Calls Unity Spends Your CPU On

Every draw call asks the CPU to prepare state and submit work to the GPU. On mobile, where CPU time is scarce, draw calls Unity issues per frame are the first suspect in most CPU-bound games. A commonly cited rule of thumb for mobile is to keep draw calls in the range of 50 to 100 for a typical scene. Treat that as a starting point and tune against your own profiling, since the real limit depends on the device, the render pipeline, and what else the CPU has to do.

Unity offers several tools to cut calls. Use them in this order:

  1. SRP Batcher. In the Universal Render Pipeline, enable the SRP Batcher in the URP Asset under Advanced. It reduces the CPU cost of rendering many objects that share shader variants, even when it does not reduce the raw count of calls. Unity highlights it as a significant CPU rendering speedup.
  2. GPU instancing. For a large number of identical meshes, such as foliage, crowds, or props, instancing draws them in one call. Enable it on the material.
  3. Static batching. Combines static meshes sharing a material into larger meshes. It reduces calls at the cost of more memory, so use it for level geometry, not for everything.
  4. Dynamic batching. Works only on small meshes. Unity’s guidance sets the limit at fewer than 900 vertex attributes and no more than 300 vertices per mesh, which excludes most modern characters and props. It also costs CPU to batch at runtime, so measure before leaving it on.
  5. Texture atlasing. Packing many small textures into one lets objects share a material and batch together. This is the standard fix for UI and 2D, and Sprite Atlases handle it automatically for sprites.
  6. GPU Resident Drawer. In Unity 6, this feature moves instance batching to the GPU for object-dense scenes, with reported CPU rendering savings of up to half in the right conditions. It requires disabling static batching for the objects it handles, and it suits CPU-bound games with thousands of repeated objects.

Use the Frame Debugger to find out why batches break. The usual culprits are different materials, different shader keywords, objects crossing a lightmap boundary, and unnecessary transparency sorting. The tool names the reason for each break, which turns a mystery into a to-do list.

Rule Two: Textures Are Usually the Memory Hog

On most mobile games, textures take the largest share of memory and a large share of bandwidth. Fixing them returns more per hour of work than almost anything else.

Use ASTC. Adaptive Scalable Texture Compression is Unity’s recommendation for both iOS and Android on modern devices. The older alternatives are PVRTC for iOS devices with A7 chips or older, and ETC2 for Android devices from before 2016. Compression is a stored-in-GPU-format decision, so it saves runtime memory and bandwidth, not just disk space. Commonly cited figures put the memory saving at up to about 75 percent compared with uncompressed textures, and Unity’s own asset guidance shows an uncompressed texture using almost 26 times the memory of a well-compressed equivalent with little visible difference.

Cap Max Size. A 4096 by 4096 texture on a phone with a 6-inch display is almost always wasted. A common range for mobile is 512 to 1024 pixels for most assets, with larger sizes reserved for hero items seen up close. Max Size in the import settings is non-destructive, so you can reduce it and judge the result on a device before committing.

Turn off Read/Write. When enabled, Unity keeps a copy of the texture in both CPU-accessible and GPU memory, doubling the footprint. Leave it off unless a script has to read the pixels at runtime.

Disable mipmaps where they have no use. Textures that stay at a constant size on screen, such as UI and 2D sprites, do not need them. For 3D world textures, mipmaps save bandwidth and reduce shimmering, so leave them on.

Check the 16-bit option. If a compressed format causes visible banding on a specific asset, a 16-bit uncompressed texture can be a cheaper compromise than 32-bit.

The same discipline applies to meshes and animation. Turn off Read/Write on meshes that do not need runtime access, and remove rigs, blend shapes, normals, and tangents from meshes that never use them. Mesh compression reduces disk size, though it does not shrink runtime memory, which surprises many teams. Our companion guide on the Unity asset pipeline covers the import settings in depth.

Rule Three: Unity Memory Optimization Is About Peaks

Mobile operating systems kill apps that exceed their memory allowance, and they do it without warning. A typical rule of thumb puts a safe working budget somewhere in the 256 to 512 megabyte range for a game on mid-range hardware, though the actual limit varies by device and by operating system version. The worst case is the oldest, lowest-memory device you support, so budget against that one.

Effective Unity memory optimization focuses on peaks and leaks rather than averages:

  • Take snapshots with the Memory Profiler. Capture one at the main menu, another after loading a level, and another after returning to the menu. Anything that grew and never shrank is a leak.
  • Unload what you are done with. Scenes loaded additively, audio clips, and large textures should be released when a level ends. Unity does not always free assets on its own.
  • Stay away from the Resources folder. Everything inside gets included in the build and indexed at startup, which hurts both size and launch time.
  • Use Addressables for large content. The Addressable Asset System loads assets by address, asynchronously, from local storage or a remote content delivery network. It enables a smaller initial download and lets you stream levels, skins, and large audio on demand.
  • Mind your audio. Long music tracks should stream from disk instead of loading fully into memory. Short, frequent effects can sit compressed in memory. Mono assets halve the footprint of anything that does not need stereo.
  • Watch the managed heap. Unity’s garbage-collected memory only grows. Fragmentation can leave you with free space in total but no block big enough for a new allocation.

Rule Four: Garbage Collection Hitches Feel Like Lag

Managed allocations eventually trigger garbage collection, and a collection can freeze the main thread. Reported hitches range from a few milliseconds to 30 milliseconds or more on mobile, which is enough to drop one or two frames at 60. Players describe it as random stutter, and it is the main reason for mobile FPS Unity builds that look fine on average and feel bad in the hand.

The cure is to stop allocating during gameplay. The usual offenders:

  • String concatenation in Update. Cache strings, or use a StringBuilder, and avoid updating UI text when the value has not changed.
  • LINQ and closures in hot paths. They allocate. Use plain loops in code that runs every frame.
  • Instantiate and Destroy during play. Replace them with object pools for bullets, effects, enemies, and anything spawned often.
  • GetComponent and Find calls in Update. Cache references in Awake or Start.
  • Boxing. Passing value types into methods that take objects allocates on the heap.
  • Per-frame list and array creation. Reuse collections and clear them instead of making new ones.

Unity also provides an incremental garbage collector, which spreads collection work across several frames to avoid one long pause. It reduces the severity of spikes, though it is no replacement for removing the allocations. Profile with the Memory section of the Profiler and look for the GC.Alloc column; any frame that allocates in steady state is a bug worth fixing.

Rule Five: Overdraw, Shadows, and Resolution

When the GPU is the bottleneck on mobile, the cause is almost always fill rate. Phones render at very high pixel counts on small chips, and each pixel costs shader work. Three habits protect it.

Reduce overdraw. Layers of transparent sprites, particles, and UI stack up and force the GPU to shade the same pixel multiple times. Use the Scene view overdraw mode to find the worst areas, prefer opaque or alpha-tested materials where possible, and keep particle counts and sizes under control. Mobile GPUs use tile-based rendering, which handles opaque overdraw efficiently but punishes heavy transparency.

Spend shadows carefully. Real-time shadows are expensive. Limit shadow distance, use a single cascade or none, restrict shadow casters to important lights, and consider fake shadows for small objects, such as a blurred texture on a simple mesh under a character. Bake lighting for static scenes wherever possible.

Lower the render resolution. Many modern phones have screens far denser than anything a player can distinguish in motion. In URP, the Render Scale setting lets you render at 80 or 90 percent of native resolution and upscale, which saves a large share of GPU time with little visible loss. Resolution scaling is one of the highest-return fixes for a GPU-bound game, and it can be adjusted dynamically.

Also review the cost of cameras and post-processing. Unity notes that each camera can use up to about a millisecond of CPU time on lower-end platforms, so avoid extra cameras and full-screen effects that you do not need. Bloom, depth of field, and color grading at full resolution are expensive on phones, and a stripped-down set of effects often looks nearly identical.

Rule Six: Choose a Frame Rate You Can Keep

Locking a frame rate is a design decision, and the right answer depends on the game. Set it explicitly with Application.targetFrameRate and manage vertical sync with QualitySettings.vSyncCount, because Unity’s mobile defaults can differ from what you expect, and leaving it unset hands the decision to the platform.

Practical guidance for this layer:

  • Match the rate to the genre. Action and racing games benefit from 60. Puzzle, card, and many strategy titles look and play perfectly at 30 and run cooler.
  • Offer a quality toggle. A 30 and 60 option in settings lets players choose between smoothness and battery life.
  • Drop dynamically when the device heats up. Unity’s Adaptive Performance package, built with device makers such as Samsung, exposes thermal and performance state so the game can lower resolution or effect quality before the system throttles it for you.
  • Respect low battery. When the player is under roughly 20 percent charge, a lower frame rate can extend a session considerably.
  • Run menus at lower rates. A static menu has no reason to spend 60 frames per second of heat.

The goal is a smooth, steady experience over a full session, which often means a lower target than the peak a device can theoretically reach.

Rule Seven: Build Settings That Pay for Themselves

A few project-level settings change performance without touching a line of game code:

  • IL2CPP scripting backend. It compiles C# to native code, generally giving better runtime performance than Mono, and it is required to ship 64-bit ARM builds. Google Play requires 64-bit support for new apps.
  • ARM64 architecture. Target it for current phones.
  • Managed code stripping. Removes unused code from the build to reduce size, with care around reflection.
  • Graphics API choice. Vulkan can lower CPU overhead on capable Android devices, with OpenGL ES as a fallback for older ones. Test on both.
  • Strip unused shaders and variants. Shader variants multiply quickly and bloat both build size and load time.
  • Quality levels. Define separate quality tiers for low, mid, and high devices and choose between them at startup based on hardware.

These are the quiet wins of a Unity performance optimization pass, and they often return more than weeks of scripting work.

Build a Real Device Matrix

You cannot optimize for hardware you have never held. A practical test matrix for a global release covers at least these cases:

Device Class Why It Matters
Oldest supported iPhone Sets the iOS memory and performance floor
Low-end Android with 3 to 4 GB of memory The most common cause of crashes and poor reviews
Mid-range Android, current generation The largest share of real players
Current flagship Confirms high-quality tiers and high refresh rates
Tablet Catches layout and resolution problems

 Run each for a full thirty-minute session and record frame time, temperature, and battery drain, not just launch performance. If a device cannot hold the target, either lower the quality tier for that class or raise the minimum specification. Teams that skip this step find out from one-star reviews. Our game testing practice builds device matrices around the actual install base of a target market.

Cobweb Games’ Approach to Unity Mobile Optimization

Unity mobile optimization at Cobweb Games begins with a performance budget written during pre-production, before an asset is built: target devices, target frame rate, memory ceiling, and draw call and triangle budgets per scene. Every artist and engineer works against that document, and profiling on real devices happens at every milestone instead of in the final weeks. Studios exploring Unity game development with us get a build pipeline where performance regressions show up the week they are introduced.

For projects aimed at phones and tablets, the same discipline extends into mobile game development planning, including thermal strategy, tiered quality settings, and store-ready build configuration. Fixing performance late is expensive. Building to a budget from the start is cheap.

Common Unity Mobile Optimization Mistakes

Profiling in the editor. The editor measures your desktop, not a phone.

Optimizing the wrong side. Spending a week on scripts in a GPU-bound game changes nothing on screen.

Shipping max-size textures. Oversized textures waste memory and bandwidth on every device.

Ignoring heat. A game that passes a short test and throttles at minute ten will still get reviewed at minute ten.

Allocating in Update. Per-frame garbage creates the stutter players describe as random lag.

Testing only on flagships. The device that crashes is always the one nobody tested.

Leaving frame rate to chance. Unset targets mean the platform picks for you.

Frequently Asked Questions

What is the most effective first step in Unity mobile optimization?

Profile a development build on a real low-end device and identify if the CPU or the GPU is the bottleneck. Every fix after that depends on the answer, and optimizing the wrong side wastes time.

How many draw calls is too many on mobile?

A commonly cited rule of thumb is 50 to 100 for a typical scene, though the true limit depends on the device, the render pipeline, and the rest of the frame’s CPU work. Use the SRP Batcher, instancing, and atlasing, then measure on hardware.

Should a mobile Unity game target 30 or 60 FPS?

It depends on the genre. Unity defaults to 30 on mobile, which suits puzzle, strategy, and many 3D titles and runs cooler. Action and racing games usually justify 60 and the performance budget that comes with it.

Why does my game run fine for a few minutes and then slow down?

Thermal throttling. The device lowers its clock speeds to control heat. Reducing sustained workload, lowering the target frame rate, or using Adaptive Performance to scale quality prevents it.

Which texture format should I use for mobile in Unity?

ASTC for modern iOS and Android devices. PVRTC and ETC2 remain fallbacks for very old iPhones and pre-2016 Android devices respectively, which most projects no longer need to support.

Does turning on dynamic batching improve performance?

Only for very small meshes, under 300 vertices and 900 vertex attributes, and it costs CPU to run. In most modern projects, the SRP Batcher and GPU instancing do more, so test before leaving dynamic batching enabled.