Plan & research your game project
This is the most important part of any software development project: decisions made in this phase set the course for your entire development cycle.
Map features to target platforms
- Check that all planned features work on all target platforms. This is especially relevant for WebGL, which has some constraints regarding memory management, multithreading, networking and plugin support.
Specify the minimally supported devices for your project
- Have them available to your development and QA teams.
- Test early and throughout development to maintain a realistic performance and frame budget.
Set frame and asset budgets early on
Define budgets for:
- Models: how many vertices is the target device able to render?
- Assets: how detailed should models and textures be?
- Scripts and rendering: what % of the frame do you have for logic, rendering,
Effects and other subsystems?
Set Scene and prefab decomposition early on
- Split levels into (additively loaded) scenes.
- Move separate objects into Prefabs. Nested Prefabs further increase scene decomposition and reduce merge conflicts.
- Agree on the main scene locking mechanism.
Plan your asset pipeline process
The import process needs to accommodate artists’ specs for assets.
- If possible, involve a technical artist from the beginning to define this process.
- Define clear guidelines on asset formats and specs.
- Use Asset Postprocessors to automate asset importing.
Line up your Build and QA process
- Set up a build machine, or, turn on and set up Unity Teams.
- Establish a process for:
- Publishing features to the release build
- Testing new builds
- Automating tests
- Recording statistics
Prototype ideas, but prepare to start actual project from scratch
After building a prototype and getting it approved by the management, strongly consider starting it from scratch.
- Decisions made during prototyping usually favor speed.
- Basing your game on a bunch of hacks is not a good start for any project.
Speed up development
Follow these best practices to speed up development and boost the quality of the final product.
Set up correct version control
- Use text serialization (by default in Unity).
- Set up built-in YAML merge tool. See more about SmartMerge here.
- Set up commit hooks. See more here.
Use the Cache Server
- Switching platforms decreases development speed.
- Make sure to set up the Cache Server for your team.
Avoid storing static data in JSON or XML files
- This results in slow loading.
- Parsing generates garbage.
- Instead, for built-in static data use ScriptableObjects with custom editor tools.
Don’t leave unused assets, plugins and duplicated libraries in your project
Unused assets in your project can still be built into the game. Make sure that you don’t leave garbage in your project: if you set up a version control system, restoring files should be easy.
- Check what dependencies assets from the Asset Store drag into the project. For example, you might be surprised to find that you have five different JSON libraries in the project.
- Check for outdated assets and scripts from early prototypes.
- Moving old assets to the “removed” folder still results in resources and scripts being built into the game.
Repetitive actions require manual work
- For every repetitive task there should be a script automating it.
- Make sure that you can “play” the game or interactive content from any scene.
- Consider a solution, such as Cloud Build, that automates the build process.
Profile your project on target devices as well as in the editor
- Always profile the content on your target device; if you profile in the editor only, you can miss performance bottlenecks.
Use both built-in and platform-specific profiling and debugging tools
Here are resources to learn about Unity’s profiling tools:
- The Profiler
- The Profiler Analyzer
- The Memory Profiler
- Understanding optimization in Unity
- Optimizing graphics performance
- General best practices (including extensive tips on optimizing Unity’s UI system)
Profile and optimize early on
- The longer you wait with profiling, the larger the performance costs can become.
- Start profiling early on, so you’re sure your project fits into the frame, memory and disk size budgets.
- Profile before your optimize, so that you have the data you need to optimize actual bottlenecks.
Make sure you know as much as possible about your target platform(s)
- Desktop, mobile and console platforms have very different bottlenecks.
Choose the correct Asset settings
Assets take up most of the size of your game. Save time on optimizing by thinking carefully about Asset settings.
Set up Sprite Atlases correctly
- Make sure that you use Sprite Atlases for sprites used together in a scene. This will reduce the number of draw calls in the game/interactive content.
- Or, consider using Tilemap, which can improve performance of 2D games. Get some tips for optimizing with Tilemaps here.
Set up texture settings correctly
- Make sure that you know the right texture settings for the target platform:
- What compression does the platform support?
- Do the textures need mip maps?
- Set up an automated way to apply these settings for new textures using AssetPostprocessor API as shown by this project.
- Prevent artists from committing textures with wrong settings.
Try the Addressable Asset System
The new Addressable Asset System provides a framework to solve a set of common problems related to addressing, building, and loading assets. It handles asset management overhead by simplifying content pack creation and deployment. Try these resources for more information:
Boost programming productivity
Boost productivity with best practices for code architecture.
Know how to use your code editor efficiently
- Make sure to know all the features and shortcuts of your code editor as it can speed up everyday tasks.
- If you use Visual Studio, see this video on tips and tricks when working with a Unity project. Or, watch this video for tips on JetBrains Rider.
Avoid abstract code
- Abstract Enterprise code is rarely justified because it runs slowly and requires IL2CPP to generate more code.
Establish and document clear architectural conventions
- When writing code, don’t use different methods to accomplish the same task, e.g.
- Config formats (files, properties, assets).
- Events (Unity events, C# events, SendMessage).
- Define which manager is responsible for which objects?
Understand the Unity frame loop
- When Awake, OnEnable, Update and other methods are called.
- When coroutines are updated.
- How FixedUpdate is executed.
Avoid relying on Unity execution order for script initialization logic
- When a MonoBehaviour is instantiated during the game Unity calls its Awake and Start methods. While the game is simple enough the order in which MonoBehaviours are initialized when the first scene is loaded might not matter. Later you might want to change this order using the Script Execution Order list in Project Settings. But once you have more than a few scripts in this list, relying on it can make it a source of hard-to- track bugs. In this case you should create a script which calls initialization functions for all your other MonoBehaviours at startup.
Account for framerate when scripting logic or animation
- Use Time.deltaTime for FPS independent scripts.
Optimize CPU Performance
Optimize CPU usage for smooth performance and longer battery life.
Use Update() method sparingly
- Native -> Managed calls have some overhead. Use custom managers instead
- Avoid having custom Behaviours inherit from an abstract class with Update/Awake/Start methods defined
Define how frequently you need to update different systems in your game
Not all systems need to be updated every frame. Areas to look at include:
- Moving objects
- AI and pathfinding
- Logging and saving game state
- Other “heavy” systems
Cache data you need frequently, such as:
Pool frequently instantiated objects
- Instantiating objects is slow.
- Create pools of objects at the start of the game/content.
- Reuse objects instead of creating new ones.
Don’t allocate memory for every frame
- Small allocations every frame will cause a GC spike.
- Try to eliminate ALL allocations.
Try alternatives to memory-allocating APIs, such as:
- String concatenation.
- Unity APIs returning arrays:
- Physics.RaycastAll, Mesh.vertices, GetComponents, etc.
Optimize GPU Performance
Avoid spikes in graphics performance with these tips.
Avoid overdraw in mobile games because it causes performance bottlenecks
- Mobile GPUs can only draw so many pixels per second.
- Don’t draw unnecessary transparent images.
- Use more complex meshes to crop fully transparent areas.
Use the Universal Render Pipeline for your shaders in mobile projects
- Use the shaders that are compatible with the Universal Render Pipeline (formerly known as the Lightweight Render Pipeline).
- When using Shader Graph, make sure to set Precision to half where full precision is not needed; this will make your shaders more performant.
Watch for incorrect settings that can break dynamic batching
- Objects must be “similar” to be dynamically batched.
- The Frame Debugger shows why certain objects were not batched.
- Use the SRP Batcher with the Universal Render Pipeline to increase the number of batched objects.
Set up LODs correctly
- LODs enable economic rendering of objects further from the camera.
Optimize UI Performance
Unity UI is an artist-friendly tool. Follow these tips to keep your UIs performant.
Account for different resolutions and aspect ratios
- Test UI on devices with different resolutions and aspect ratios
- Sometimes it is better to create different UI screens for different devices
Move animated elements to separate Canvases
- The Canvas creates a new combined mesh when an element changes, which can be costly for complex Canvases.
Avoid lag when a new UI window is opened
- Your game experiences lag when a new window or a big chunk of UI is created. Minimize this effect by:
- Making UI windows less complex.
- Splitting UI in parts.
- Caching windows.
Make sure your lists don’t contain a large amount of items
- Dynamically reuse list items instead of creating all of them at once.
- Create a nested Canvas in the list.
Use open source implementations, such as this.