PERFORMANCE_OPTIMIZATION_GUIDE
Performance Optimization Guide
Unity Tower Defense - Complete Reference
Version: 2.0 Last Updated: December 2025 Target Platform: Unity 2022.3 LTS + URP Optimization Results: +125% FPS (63.9 โ 144.1)
๐ Table of Contents
1. Introduction
1.1 Document Purpose
This guide documents the complete performance optimization process for a Unity tower defense game, achieving +125% FPS improvement (63.9 โ 144.1 FPS) through systematic CPU and GPU optimization.
Target Audience:
Unity developers optimizing performance
Technical leads reviewing optimization strategies
Game engineers learning profiling techniques
1.2 Project Context
Initial State:
FPS: 63.9 (Wave 7, 15 enemies)
CPU Time: 15.6ms per frame
Batches: 7,277
Shadow Casters: 9,125
Update() Calls: 11,900/second
Final State:
FPS: 144.1 (+125%)
CPU Time: 6.9ms (-56%)
Batches: 1,917 (-74%)
Shadow Casters: 83 (-99%)
Update() Calls: 50/second (-99.5%)
1.3 Optimization Philosophy
Core Principles
1. Measure Before Optimizing
Never optimize based on assumptions!
2. Target Bottlenecks First
3. Incremental Optimization
4. Know When to Stop
2. Profiling Methodology
2.1 Unity Profiler
Accessing the Profiler
Key Modules
CPU Usage Module:
Rendering Module:
Memory Module:
Profiling Workflow
CPU Profiler Example
Before Optimization:
After Optimization:
Key Insight: Batch processing reduced Update() overhead by 98%!
Performance Markers
Add custom markers for profiling specific sections:
Result: Custom marker appears in Profiler hierarchy for precise measurement.
2.2 Frame Debugger
Accessing Frame Debugger
What It Shows
Usage Workflow
Frame Debugger Example
Before Optimization:
After Optimization (SRP Batcher):
Key Insight: SRP Batcher combined 400 grid nodes into 6 batches!
Identifying SRP Batcher Compatibility
2.3 Stats Window
Enabling Stats
Key Metrics
Interpretation:
FPS (Frames Per Second):
Batches:
Saved by Batching:
SetPass Calls:
Shadow Casters:
Stats Window Caveats
โ ๏ธ Warning: Stats window has limitations!
Problem 1: SRP Batcher Not Reported
Problem 2: Batches Count Inconsistent
2.4 Profiling Best Practices
1. Profile in Build, Not Editor
Why?
How to Profile Build:
2. Test on Target Hardware
3. Profile Worst-Case Scenarios
4. Compare Apples to Apples
5. Record Multiple Samples
3. CPU Optimization Techniques
3.1 Batch Processing (Eliminating Update())
The Problem
Traditional Unity Pattern:
Overhead:
The Solution: System-Based Batch Processing
New Pattern:
Benefits:
Performance Impact
Test Setup:
100 enemies moving
60 FPS target
Unity 2022.3 LTS
Results:
Individual Update()
1.2ms
High
1ร (baseline)
Batch Processing
0.06ms
Low
20ร
Implementation Steps
Step 1: Define Interface
Step 2: Create System
Step 3: Refactor Entities
Step 4: Orchestrate Systems
3.2 Eliminating LINQ in Hot Paths
The Problem
LINQ is Slow:
Hidden Costs:
Performance Test:
The Solution: Manual Iteration
Targeting Strategy (Without LINQ):
Closest Target (Without LINQ):
Key Optimization: SqrMagnitude
Performance Impact
Test: 10 towers selecting targets (50 enemies each)
LINQ (OrderBy + First)
2.5ms
2.8 KB
High
Manual Iteration
0.1ms
0 bytes
None
Improvement
-96%
-100%
โ
Annual Impact (60 FPS):
When LINQ Is Acceptable
Outside Hot Paths:
Rule of Thumb:
3.3 Coroutines for Infrequent Tasks
The Problem
Expensive Operations in FixedUpdate:
Issues:
The Solution: Coroutine with Delay
Optimized Target Scanning:
Benefits:
Why It Works:
Performance Impact
Test: 10 towers scanning 50 enemies
50/sec (FixedUpdate)
500/sec
3.0ms
Baseline
10/sec (0.1s coroutine)
100/sec
0.6ms
+8 FPS
5/sec (0.2s coroutine)
50/sec
0.3ms
+12 FPS
2/sec (0.5s coroutine)
20/sec
0.12ms
+15 FPS
Sweet Spot: 5 scans/second (0.2s delay)
Responsive enough for gameplay
90% reduction in queries
No noticeable delay
When to Use Coroutines
Good Candidates:
Bad Candidates:
Guidelines:
3.4 Object Pooling
The Problem
Instantiate/Destroy Overhead:
Impact:
The Solution: Object Pool
Pool Manager Implementation:
Usage:
Critical: OnEnable vs Start
โ ๏ธ Pooled Objects Gotcha:
Why?
Performance Impact
Test: 100 projectiles spawning/despawning per second
Instantiate/Destroy
0.5ms
0.5ms
20 ร 100ms
2,000ms/min
Object Pooling
0.01ms
0.01ms
0
120ms/min
Improvement
-98%
-98%
-100%
-94%
Pre-Warming Pools
Initialize pools at game start:
Benefits:
3.5 Component Caching
The Problem
GetComponent in Hot Paths:
The Solution: Cache in Awake
Performance Impact:
Best Practices
Cache All Frequently Used Components:
Even transform Benefits from Caching:
4. GPU Optimization Techniques
4.1 Shadow Optimization
The Problem
Excessive Shadow Casters:
The Solution: Disable Unnecessary Shadows
Runtime Shadow Disabling:
Performance Impact:
Which Objects Should Cast Shadows?
Guidelines:
Visual Test:
4.2 Static Batching
The Problem
Individual Draw Calls for Static Objects:
The Solution: Static Batching
Method 1: Mark Static in Inspector (Editor-Only)
Method 2: StaticBatchingUtility (Runtime)
Performance Impact:
Caveats:
4.3 SRP Batcher (URP)
What is SRP Batcher?
SRP Batcher = Scriptable Render Pipeline Batcher (Unity 2018.3+)
Purpose:
How It Works:
Enabling SRP Batcher
Global Setting:
Or in Inspector:
Material Compatibility
Requirements for SRP Batcher:
Checking Compatibility:
Performance Impact
Test: 400 Grid Nodes
Frame Debugger Evidence:
Stats Window Caveat
โ ๏ธ Stats Window Doesn't Show SRP Batcher Savings!
4.4 GPU Instancing
What is GPU Instancing?
GPU Instancing = Rendering many copies of same mesh/material in single draw call.
Use Case:
Enabling GPU Instancing
On Material:
Via Script:
Requirements
GPU Instancing Works When:
GPU Instancing Breaks When:
Per-Instance Properties (Advanced)
Problem: Want unique colors but same material?
Solution: MaterialPropertyBlock with instancing
Shader Support Required:
Performance Impact
Test: 50 Identical Enemies
4.5 Dynamic Batching
What is Dynamic Batching?
Dynamic Batching = Unity automatically combines small meshes at runtime.
Requirements:
Limitations:
Enabling Dynamic Batching
When to Use
Good for:
Not good for:
Recommendation:
Performance Impact
5. Memory Optimization
5.1 Garbage Collection (GC)
Understanding GC
What is Garbage Collection?
GC in Unity:
Identifying GC Issues
Unity Profiler:
Memory Profiler:
Reducing GC Pressure
Rule 1: Avoid Allocations in Hot Paths
Rule 2: Avoid String Concatenation
Rule 3: Cache Arrays
Rule 4: Avoid Boxing
Rule 5: Use Object Pooling
GC Performance Impact
Test: 60 seconds of gameplay
5.2 Texture Memory
Texture Compression
Problem:
Solution:
How to Compress:
Recommended Formats:
Mipmaps
What are Mipmaps?
Benefits:
When to Use Mipmaps:
Texture Atlasing
Problem:
Solution: Texture Atlas
Unity Tools:
6. Common Performance Pitfalls
6.1 Update() Overuse
Problem:
Solution:
6.2 FindObjectOfType in Update
Problem:
Solution:
6.3 Camera.main in Hot Paths
Problem:
Solution:
6.4 SendMessage / BroadcastMessage
Problem:
Solution:
6.5 Empty Update Methods
Problem:
Solution:
6.6 Physics.OverlapSphere Every Frame
Problem:
Solution:
7. Troubleshooting Guide
7.1 Low FPS Despite Optimizations
Symptom: FPS still low after optimization.
Diagnosis:
Solutions:
7.2 Frame Spikes
Symptom: Intermittent frame drops (100 FPS โ 30 FPS โ 100 FPS).
Diagnosis:
Solutions:
7.3 SRP Batcher Not Working
Symptom: Frame Debugger shows "RenderLoop.Draw" instead of "DrawSRPBatcher".
Diagnosis:
Solutions:
7.4 High Batch Count
Symptom: Batches > 3,000 despite optimization.
Diagnosis:
Solutions:
7.5 Memory Leaks
Symptom: Memory usage grows over time, eventually crashes.
Diagnosis:
Common Causes:
Solutions:
8. Best Practices
8.1 Profiling Workflow
8.2 Optimization Priority
8.3 When to Stop Optimizing
9. Tools Reference
9.1 Unity Profiler
Keyboard Shortcut: Ctrl+7 (Cmd+7 on Mac)
Key Modules:
CPU Usage: Method execution times
Rendering: Draw calls, batches
Memory: Allocations, GC
Physics: Collision checks
Audio: Sound overhead
Tips:
Record in Build mode (not Editor)
Use "Deep Profiling" for detailed call stacks (slow!)
Compare multiple samples for accuracy
9.2 Frame Debugger
Keyboard Shortcut: Ctrl+F7 (Cmd+F7 on Mac)
Use Cases:
Verify SRP Batcher working
Identify rendering order issues
Count actual draw calls
Debug shader issues
Tips:
Step through draws to find anomalies
Check for redundant state changes
Verify batching groups
9.3 Stats Window
Location: Game View โ Stats (top-right)
Key Metrics:
FPS: Target 60+ (gameplay), 100+ (competitive)
Batches: <2,000 (good), <1,000 (excellent)
SetPass: <50 (excellent), <100 (good)
Tris: Depends on hardware
Caveats:
Doesn't show SRP Batcher savings
Includes all rendering (shadows, post-processing)
Use Frame Debugger for accuracy
9.4 Memory Profiler Package
Installation:
Use Cases:
Find memory leaks
Analyze heap allocations
Compare snapshots over time
Identify large objects
10. Case Studies
10.1 Tower Defense Optimization
Project: Procedurally Generated TD (this project)
Initial State:
FPS: 63.9 (Wave 7)
CPU: 15.6ms
Batches: 7,277
Shadow Casters: 9,125
Optimizations Applied:
Final State:
FPS: 144.1 (+125%)
CPU: 6.9ms (-56%)
Batches: 1,917 (-74%)
Shadow Casters: 83 (-99%)
Lessons Learned:
10.2 Key Takeaways
๐ Document Revision History
1.0
December 2024
Initial release (.docx)
2.0
December 2025
Complete rewrite (Markdown), expanded examples, case study
This Performance Optimization Guide is a living document. Contributions and feedback welcome.
Maintainer: Senior Unity Developer Last Review: December 2025 Next Review: June 2026
Last updated