Unity offers numerous ways to organize your files or how to approach making your game. This freedom allows for fast prototyping but comes at the price of potentially making the project slower to open, to navigate or develop when it becomes bigger. This guide will give pointers and hints on how to mitigate that and tips to take into account when starting your project.
Before starting
Before even starting working on your Unity project, there are a couple of steps to think about.
Source control
Using source control is very important for any project. It provides safeguards to minimise mistakes and simplifies working with multiple people. Unity has a source control service called Unity Version Control that is seamlessly integrated in the Editor. If you are using an external source control such as Git, Svn, Mercurial or Perforce you will need to configure a couple of settings:
In your source control of choice, exclude the folders Library, Temp, obj as they are not needed to open the project on another computer and would just clutter the repository and use storage needlessly
In the Unity Editor, go to Edit > Project Settings > Version Control and set:
Version Control to Visible Meta Files: This will place meta files next to your assets, in which parameters and import settings are stored. This allows you to share import settings and references by including these files in version control. Otherwise, these settings are stored in the Library folder and cannot be shared between members of the team.
In the Unity Editor, go to Edit > Project Settings > Editor and set:
Asset Serialization to Force Text: make all unity files (ScriptableObjects, Scenes, Prefabs and many others) use a text representation instead of a binary one. This allows source control to just store modifications to these files when they change, rather than a whole new copy of the file, and allows you to manually merge any conflicts (Note : on very big projects, the additional time needed to import text asset versus binary asset could create a quite noticeable difference. Most projects will not reach that size threshold where the benefits of text over binary are overcome by time of import, so setting to text in the beginning is still recommended)
Note for Perforce Users: In the Perforce Workspace settings, use "revert unchanged files". This will prevent unnecessary commits of files that have not changed.
Project Structure
Folders
Use the Project View in the Unity Editor to organize your assets. Unity keeps a meta file (if enabled, see Source control) next to each asset file that stores the asset’s import settings. That means you should avoid moving files through your OS file explorer, always move them from inside the Unity editor as it will take care of moving everything properly. If you need to move them outside the editor, do not forget to move the meta file with them, otherwise you will lose references and import settings.
To keep the project easy to navigate, avoid placing files in the root Assets folder. Use subfolders. How you organize those subfolders is generally decided by your projects but the two main ways to do it are:
Note that the second way will help if you plan on using Asset Bundles as you can then create bundles per content themes just by adding the parent folder to a specific bundle and easily splitting your related assets through different bundles.
Prototype folders
Keep everything that is related to a prototype or placeholder in a separate folder hierarchy, and avoid cross referencing between those folders and folders which contain shippable assets/code. For example, your Character prefab in the "normal" file hierarchy should not reference a test material in your Prototype folders. Create a prototype character prefab that would reside in the Prototype folders and never use that prefab in a non prototype scenes (you could even write editor scripts that check references used by objects in the prototype folder to be sure that this rules is enforced. )
Resources
Avoid using the Resources folder as much as possible. Resources is a special folder name in Unity where assets placed can be loaded dynamically using Resources.Load in code. Some games will require dynamically loaded assets, but try to minimize the assets you place in there as much as possible, as Unity cannot strip any unused assets in there (it cannot tell if you load it via code or not) so everything in that folder will be included in the final build.
As you load assets in code with the path & filename, it also makes refactoring (moving or renaming files) harder and more error prone, as it requires you to manually change the path or name in code.
It is preferable to store those files either in a separate source control software (maybe more suited for binary files) or, if you cannot do that, store them outside of the the Assets folder, so Unity does not import them, and exports your assets to a Unity compatible format to place in the Assets folder and use in game.
Keep assets size small
Try to keep your asset size to the smallest possible (a bad example would be to export a 2048x2048 texture for a coffee mug on a table). You can reduce the size of a texture in Unity import settings but that will not reduce the import time of the texture, which makes the initial setup on fresh versions of the project way longer.
Use Compressed Format
Test locally with whatever format, but once decided, use compressed format for audio and image files. .png/.tga/.tiff for images (or even .dds if your image software supports exporting those), .ogg for audio. Unity will import (or simply use as is) way faster, cutting down the original import size of the project.
Assembly Definition Files
Assembly Definition Files (ADF) compile all scripts in a folder and its subfolders into a separate DLL. This reduces compilation time, by splitting it to only the dll to which the changed script file belongs. For example, if you have an ADF for all your Enemies scripts, changing the enemy scripts will not recompile all your helpers, player, game systems or UI scripts but rather just the scripts related to Enemies.
Define ownership
In teams of more than one, define ownership of zone/assets/features. Some assets like scenes or prefabs do not handle simultaneous changes by multiple people very well, creating conflict. Having a single person who can change (or give the right to change) a given assets helps to avoid that problem.
Prefabs
Use them. A lot. Prefabs keep a single point where you can modify settings for objects in your game. Just avoid nesting them (for now…) as changes on the original prefab will not be pushed to the one nesting it.
Scriptable Objects
Use them. A lot. Just like prefabs, they offer a single place where you can change their settings that may be shared across scenes or objects. Note that if your scriptable object references other scriptable objects or prefabs, and a GameObject in your scene references that scriptable object, when the scene GameObject is loaded, it will trigger a loading of the scriptable object and all references to the objects in a recursive fashion. This can lead to long loading times for scriptable object referencing a lot of other objects or content. But you can also use that to your advantage if your project prefers to load objects at scene load in one big load.
Scenes Structure
Hierarchy depth and count
Keep the depth of the hierarchy to a minimum. As a general rule, you should prefer multiple small hierarchies at the root to one big hierarchy.
The reason for minimal hierarchy depth is that Unity transform system has to walk that hierarchy to notify all children when a transform changes.
The reason for multiples hierarchies is that changes are tracked per hierarchy. So if you have 100 hierarchies at the the root, and change one object in those, only one hierarchy will have the transform traversal. But if you have one big hierarchy at the root, any change to any object will trigger a hierarchy change on all your objects. Additionally, Unity will multi-thread transform change checks on root GameObject hierarchies. The more hierarchies you have, the more Unity can multi-thread and speed up multiple transforms change per frame.
For the same reason, you can place all static objects (static environment meshes, lights or sounds) in a single hierarchy if that simplifies your scene navigation/organization for you. Since those will never have their transform changed, the drawback mentioned above does not apply.
To keep the hierarchy depth in check and spread your hierarchies in root:
Organization
Use an empty GameObject to organize your hierarchy. For example use a GameObject named "----Lights----" to place above your lights
Note that you should not make it the parent of your lights, but just place it above it in the hierarchy for reasons explained in Hierarchy depth and count above.
If you want for ease of working to parent all light under your Light "header" so you can collapse everything, you should write an editor script that unparents everything before build and/or entering playmode, otherwise performance could suffer on large scenes.
Dynamic Objects in Editor
If you have dynamic objects that are created and executed in the editor not in playmode (For example, a reflection camera that moves to match the scene view camera so Reflections work in the Scene View or a render target updated in the scene view assigned to a material) be sure to set the hideFlags of those objects to HideAndDontSave (or DontSave if you want to keep it visible in the hierarchy to edit it). Otherwise all changes in position or content will dirty the scene, asking to save it every time. This leads to a greater chance of creating conflict if someone saves and commits the scene to source control inadvertently as this will happen to everyone, all the time.
Game Design and Features
Create cheat function
To improve the speed of the development/test/improve cycle, add in "cheat" functions for various features as early as possible. An example could be if your project has currency, make a cheat to get huge amounts of it. If you need to gain items to open doors, make a cheat to grant said item instantly. Create a way for the player to be teleported at points of your level if levels are long. QA and debugging will become way easier. There are multiple ways to do this:
Use small feature test scenes
Create and keep small scenes that are only made to test a single feature separate. Some examples could be a scene full of enemies to test the combat, an obstacle course whitebox to test the locomotion, one with interactable objects. This allows you to develop, improve and verify features are not broken by new ones. It will also allow you to more simply isolate a problem testers may have found in the "full" game. For example, you have a character that sometimes fails to grab to a ledge? This can be much easier to reproduce in a small scene than having to replay the first 5 minutes of a level 20 times to reach the point where the problem occurs.