• Games
  • Industry
  • Resources
  • Community
  • Learning
  • Support
Development
Unity Engine
Build 2D and 3D games for any platform
DownloadPlans and pricing
Monetization
In-App Purchase (IAP)
Discover and manage IAP across stores
Mediation
Maximize revenue and optimize monetization
Ad Quality
Protect your app’s user experience
Tapjoy
Build long-term user loyalty
All monetization products
User acquisition
User acquisition
Get discovered and acquire mobile users
Unity Vector AI
Connect players with the right games
Aura on-device advertising
Reach users on-device at peak engagement
All growth products
Use cases
3D collaboration
Build and review 3D projects in real time
Immersive training
Train in immersive environments
Customer experiences
Create interactive 3D experiences
All industry solutions
Industries
Manufacturing
Achieve operational excellence
Retail
Transform in-store experiences into online ones
Automotive
Elevate innovation and in-car experiences
All industries
Technical library
Documentation
Official user manuals and API references
Developer tools
Release versions and issue tracker
Roadmap
Review upcoming features
Glossary
Library of technical terms
Insights
Case studies
Real-world success stories
Best practice guides
Expert tips and tricks
All resources
What's new
Blog
Updates, information, and technical tips
News
News, stories, and press center
Community Hub
Discussions
Discuss, problem-solve, and connect
Events
Global and local events
Community stories
Made with Unity
Showcasing Unity creators
Livestreams
Join devs, creators, and insiders
Unity Awards
Celebrating Unity creators worldwide
For every level
Unity Learn
Master Unity skills for free
Professional training
Level up your team with Unity trainers
New to Unity
Getting started
Kickstart your learning
Unity Essential Pathways
New to Unity? Start your journey
How-to Guides
Actionable tips and best practices
Education
For students
Kickstart your career
For educators
Supercharge your teaching
Education Grant License
Bring Unity’s power to your institution
Certifications
Prove your Unity mastery
Support options
Get help
Helping you succeed with Unity
Success plans
Reach your goals faster with expert support
FAQ
Answers to common questions
Contact us
Connect with our team
Plans and pricing
Language
  • English
  • Deutsch
  • 日本語
  • Français
  • Português
  • 中文
  • Español
  • Русский
  • 한국어
Social
Currency
Purchase
  • Products
  • Unity Ads
  • Subscription
  • Unity Asset Store
  • Resellers
Education
  • Students
  • Educators
  • Institutions
  • Certification
  • Learn
  • Skills Development Program
Download
  • Unity Hub
  • Download Archive
  • Beta Program
Unity Labs
  • Labs
  • Publications
Resources
  • Learn platform
  • Community
  • Documentation
  • Unity QA
  • FAQ
  • Services Status
  • Case Studies
  • Made with Unity
Unity
  • Our Company
  • Newsletter
  • Blog
  • Events
  • Careers
  • Help
  • Press
  • Partners
  • Investors
  • Affiliates
  • Security
  • Social Impact
  • Inclusion & Diversity
  • Contact us
Copyright © 2025 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell or Share My Personal Information

"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.

Hero background image

Advanced programming and code architecture

This is the fourth in a series of articles that unpacks optimization tips for your Unity projects. Use them as a guide for running at higher frame rates with fewer resources. Once you’ve tried these best practices, be sure to check out the other pages in the series:

  • Configuring your Unity project for stronger performance
  • Performance optimization for high-end graphics
  • Managing GPU usage for PC and console games
  • Enhanced physics performance for smooth gameplay

See our latest optimization guides for Unity 6 developers and artists:

  • Optimize your game performance for mobile, XR, and the web in Unity
  • Optimize your game performance for consoles and PCs in Unity
  • Understand the Unity PlayerLoop
  • Build a custom Update Manager
  • Minimize code that runs every frame
  • Cache the results of expensive functions
  • Avoid empty Unity events and debug log statements
  • Disable Stack Trace logging
  • Use hash values instead of string parameters
  • Pool your objects
  • Harness the power of ScriptableObjects

Understand the Unity PlayerLoop

Make sure you understand the execution order of Unity’s frame loop. Every Unity script runs several event functions in a predetermined order. You should understand the difference between Awake, Start, Update, and other functions that create the lifecycle of a script. You can utilize the Low-Level API to add custom logic to the player’s update loop.

See the Script lifecycle flowchart for the event functions’ specific order of execution.

Get to know the PlayerLoop and the lifecycle of a script.
Get to know the PlayerLoop and the lifecycle of a script.

Build a custom Update Manager

A common usage pattern for Update or LateUpdate is to run logic only when some condition is met. This can lead to a lot of per-frame callbacks that effectively run no code except for checking this condition.

Everytime Unity calls a Message method like Update or LateUpdate, it makes an interop call, a call from the C/C++ side to the managed C# side. For a small number of objects, this is not an issue. When you have thousands of objects, this overhead starts becoming significant.

Consider creating a custom UpdateManager if you have a large project using Update or LateUpdate in this fashion (e.g. an open-world game). Have active objects subscribe to this UpdateManager when they want callbacks, and unsubscribe when they don’t. This pattern could reduce many of the interop calls to your Monobehaviour objects.Subscribe active objects to this Update Manager when they need callbacks, and unsubscribe when they don’t. This pattern can reduce many of the interop calls to your Monobehaviour objects.

Refer to the Game engine-specific optimization techniques for examples of implementation.

Custom Update Manager diagram
Building a custom Update Manager reduces interop calls.

Minimize code that runs every frame

Consider whether code must run every frame. Move unnecessary logic out of Update, LateUpdate, and FixedUpdate. These event functions are convenient places to put code that must update every frame, while extracting any logic that does not need to update with that frequency. Whenever possible, only execute logic when things change.

If you do need to use Update, consider running the code every n frames. This is one way to apply time slicing, a common technique of distributing a heavy workload across multiple frames. In this example, we run the ExampleExpensiveFunction once every three frames:

private int interval = 3;

void Update()

{

if (Time.frameCount % interval == 0)

{

ExampleExpensiveFunction();

}

}

Better yet, if ExampleExpensiveFunction performs some operation on a set of data, consider using time slicing to operate on a different subset of that data every frame. By doing 1/n of the work every frame rather than all of the work every n frames, you end up with performance that is more stable and predictable overall, rather than seeing periodic CPU spikes.

The trick is to interleave this with other work that runs on the other frames. In this example, you could “schedule” other expensive functions when Time.frameCount % interval == 1 or Time.frameCount % interval == 2.

Alternatively, use a custom UpdateManager class (below) and update subscribed objects every n frames.

Cache the results of expensive functions

It’s best to cache references in either Awake or Start in order to avoid calling them in the Update method.

Here’s an example that demonstrates the inefficient use of a repeated GetComponent call:

void Update()

{

Renderer myRenderer = GetComponent<Renderer>();

ExampleFunction(myRenderer);

}

It’s more efficient to invoke GetComponent only once, as the result of the function is cached. The cached result can be reused in Update without any further calls to GetComponent.

private Renderer myRenderer;

void Start()

{

myRenderer = GetComponent<Renderer>();

}

void Update()

{

ExampleFunction(myRenderer);

}

Note: In Unity versions prior to Unity 2020.2 GameObject.Find, GameObject.GetComponent, and Camera.main used to be very expensive, however this is no longer the case. That said, it's best to avoid calling them in Update methods and follow the practice above by caching the results.

Avoid empty Unity events and debug log statements

Log statements (especially in Update, LateUpdate, or FixedUpdate) can bog down performance. Disable your Log statements before making a build.

To do this more easily, consider making a Conditional attribute along with a preprocessing directive. For example, create a custom class like this:

public static class Logging

{

[System.Diagnostics.Conditional("ENABLE_LOG")]

static public void Log(object message)

{

UnityEngine.Debug.Log(message);

}

}

Generate your log message with your custom class. If you disable the ENABLE_LOG preprocessor in the Player Settings, all of your Log statements disappear in one fell swoop.

The same thing applies for other use cases of the Debug Class, such as Debug.DrawLine and Debug.DrawRay. These are also only intended for use during development and can significantly impact performance.

This blog post on 10,000 Update calls explains how Unity executes the Monobehaviour.Update.

Adding a custom preprocessor directive lets you partition your scripts.
Adding a custom preprocessor directive lets you partition your scripts.

Disable Stack Trace logging

Use the Stack Trace options in the Player Settings to control what type of log messages appear.

If your application is logging errors or warning messages in your release build (e.g., to generate crash reports in the wild), disable stack traces to improve performance.

Learn more about Stack Trace logging.

Stack Trace options
Stack Trace options

Use hash values instead of string parameters

Unity does not use string names to address animator, material, and shader properties internally. For speed, all property names are hashed into property IDs, and these IDs are actually used to address the properties.

When using a Set or Get method on an animator, material, or shader, harness the integer-valued method instead of the string-valued methods. The string methods simply perform string hashing and then forward the hashed ID to the integer-valued methods.

Use Animator.StringToHash for Animator property names and Shader.PropertyToID for material and shader property names. Get these hashes during initialization and cache them in variables for when they’re needed to pass to a Get or Set method.

Related is the choice of data structure, which impacts performance as you iterate thousands of times per frame. Follow the MSDN guide to data structures in C# as a general guide for choosing the right structure.

Pool your objects

Instantiate and Destroy can generate garbage and garbage collection (GC) spikes, a generally slow process. Apply object pooling techniques when you need to instantiate a large number of objects to help avoid GC spikes.

Object pooling is a design pattern that can provide performance optimization by reducing the processing power required of the CPU to run repetitive create and destroy calls. Instead, with object pooling, existing GameObjects can be reused over and over.

The key function of object pooling is to create objects in advance and store them in a pool, rather than have them created and destroyed on demand. When an object is needed, it’s taken from the pool and used. When it’s no longer needed, it’s returned to the pool rather than being destroyed.

Rather than regularly instantiating and destroying GameObjects (e.g., shooting bullets from a gun), use pools of preallocated objects that can be reused and recycled.

This reduces the number of managed allocations in your project and can prevent garbage collection problems.

Unity includes a built-in object pooling feature via the UnityEngine.Pool namespace. Available in Unity 2021 LTS and later, this namespace facilitates the management of object pools, automating aspects like object lifecycle and pool size control.

Learn how to create a simple object pooling system in Unity here. You can also see the object pooling pattern, and many others, implemented in a Unity scene in this sample project available on the Unity Asset Store.

Pooling objects in Unity
Pooling objects in Unity

Harness the power of ScriptableObjects

Store static unchanging values or settings in a ScriptableObject instead of a MonoBehaviour. The ScriptableObject is an asset that lives inside of the project that you only need to set up once.

MonoBehaviours carry extra overhead since they require a GameObject – and by default a Transform – to act as a host. That means that you need to create a lot of unused data before storing a single value. The ScriptableObject slims down this memory footprint by dropping the GameObject and Transform. It also stores the data at the project level, which is helpful if you need to access the same data from multiple scenes.

A common use case is having many GameObjects that rely on the same duplicate data, which does not need to change at runtime. Rather than having this duplicate local data on each GameObject, you can funnel it into a ScriptableObject. Then, each of the objects stores a reference to the shared data asset, rather than copying the data itself. This is a benefit that can provide significant performance improvements in projects with thousands of objects.

Create fields in the ScriptableObject to store your values or settings, then reference the ScriptableObject in your MonoBehaviours.

Using fields from the ScriptableObject can prevent unnecessary duplication of data every time you instantiate an object with that MonoBehaviour.

In software design, this is an optimization known as the flyweight pattern. Restructuring your code in this way using ScriptableObjects avoids copying a lot of values and reduces your memory footprint. Learn more about the flyweight pattern and many others, as well as design principles in the e-book Level up your code with design patterns and SOLID.

Watch this Introduction to ScriptableObjects tutorial to see how ScriptableObjects can benefit your project. Reference Unity documentation here as well as the technical guide Create modular game architecture in Unity with ScriptableObjects.

In this example, a ScriptableObject called Inventory holds settings for various GameObjects.
In this example, a ScriptableObject called Inventory holds settings for various GameObjects.
Unity e-books for developers, artists, and technical artists
More tips for Unity 6

You can find many more best practices and tips for advanced Unity developers and creators from the Unity best practices hub. Choose from over 30 guides, created by industry experts, and Unity engineers and technical artists, that will help you develop efficiently with Unity’s toolsets and systems.

More best practices