50 Tips for Working with Unity (Best Practices) 137


About these tips

(Edit: August 2016. I have revised these tips. You can find the new list here.)

These tips are not all applicable to every project.

  • They are based on my experience with projects with small teams from 3 to 20 people.
  • There’s is a price for structure, re-usability, clarity, and so on — team size and project size determine whether that price should be paid.
  • Many tips are a matter of taste (there may be rivalling but equally good techniques for any tip listed here).
  • Some tips may fly in the face of conventional Unity development. For instance, using prefabs for specialisation instead of instances is very non-Unity-like, and the price is quite high (many times more prefabs than without it). Yet I have seen these tips pay off, even if they seem crazy.

Process

1. Avoid branching assets. There should always only ever be one version of any asset. If you absolutely have to branch a prefab, scene, or mesh, follow a process that makes it very clear which is the right version. The “wrong” branch should have a funky name, for example, use a double underscore prefix: __MainScene_Backup. Branching prefabs requires a specific process to make it safe (see under the section Prefabs).

2. Each team member should have a second copy of the project checked out for testing if you are using version control. After changes, this second copy, the clean copy, should be updated and tested. No-one should make any changes to their clean copies. This is especially useful to catch missing assets.

3. Consider using external level tools for level editing. Unity is not the perfect level editor. For example, we have used TuDee to build levels for a 3D tile-based game, where we could benefit from the tile-friendly tools (snapping to grid, and multiple-of-90-degrees rotation, 2D view, quick tile selection). Instantiating prefabs from an XML file is straightforward. See Guerrilla Tool Development for more ideas.

4. Consider saving levels in XML instead of in scenes. This is a wonderful technique:

  • It makes it unnecessary to re-setup each scene.
  • It makes loading much faster (if most objects are shared between scenes).
  • It makes it easier to merge scenes (even with Unity’s new text-based scenes there is so much data in there that merging is often impractical in any case).
  • It makes it easier to keep track of data across levels.

You can still use Unity as a level editor (although you need not). You will need to write some code to serialize and deserialize your data, and load a level both in the editor and at runtime, and save levels from the editor. You may also need to mimic Unity’s ID system for maintaining references between objects.

5. Consider writing generic custom inspector code. To write custom inspectors is fairly straightforward, but Unity’s system has many drawbacks:

  • It does not support taking advantage of inheritance.
  • It does not let you define inspector components on a field-type level, only a class-type level. For instance, if every game object has a field of type SomeCoolType, which you want rendered differently in the inspector, you have to write inspectors for all your classes.

You can address these issues by essentially re-implementing the inspector system. Using a few tricks of reflection, this is not as hard as it seems, details are provided at the end of the article.

Scene Organisation

6. Use named empty game objects as scene folders. Carefully organise your scenes to make it easy to find objects.

7. Put maintenance prefabs and folders (empty game objects) at 0 0 0. If a transform is not specifically used to position an object, it should be at the origin. That way, there is less danger of running into problems with local and world space, and code is generally simpler.

8. Minimise using offsets for GUI components. Offsets should always be used to layout components in their parent component only; they should not rely on the positioning of their grandparents. Offsets should not cancel each other out to display correctly. It is basically to prevent this kind of thing:

Parent container arbitrarily placed at (100, -50). Child, meant to be positioned at (10, 10), then placed at (90, 60) [relative to parent].

This error is common when the container is invisible, or does not have a visual representation at all.

9. Put your world floor at y = 0. This makes it easier to put objects on the floor, and treat the world as a 2D space (when appropriate) for game logic, AI, and physics.

10. Make the game runnable from every scene. This drastically reduces testing time. To make all scenes runnable you need to do two things:

First, provide a way to mock up any data that is required from previously loaded scenes if it is not available.

Second, spawn objects that must persist between scene loads with the following idiom:

myObject = FindMyObjectInScene();
 
if (myObjet == null)
{
   myObject = SpawnMyObject();
}

Art

11. Put character and standing object pivots at the base, not in the centre. This makes it easy to put characters and objects on the floor precisely. It also makes it easier to work with 3D as if it is 2D for game logic, AI, and even physics when appropriate.

12. Make all meshes face in the same direction (positive or negative z axis). This applies to meshes such as characters and other objects that have a concept of facing direction. Many algorithms are simplified if everything have the same facing direction.

13. Get the scale right from the beginning. Make art so that they can all be imported at a scale factor of 1, and that their transforms can be scaled 1, 1, 1. Use a reference object (a Unity cube) to make scale comparisons easy. Choose a world to Unity units ratio suitable for your game, and stick to it.

14. Make a two-poly plane to use for GUI components and manually created particles. Make the plane face the positive z-axis for easy billboarding and easy GUI building.

15. Make and use test art

  • Squares labelled for skyboxes.
  • A grid.
  • Various flat colours for shader testing: white, black, 50% grey, red, green, blue, magenta, yellow, cyan.
  • Gradients for shader testing: black to white, red to green, red to blue, green to blue.
  • Black and white checkerboard.
  • Smooth and rugged normal maps.
  • A lighting rig (as prefab) for quickly setting up test scenes.

Prefabs

16. Use prefabs for everything. The only game objects in your scene that should not be prefabs should be folders. Even unique objects that are used only once should be prefabs. This makes it easier to make changes that don’t require the scene to change. (An additional benefit is that it makes building sprite atlases reliable when using EZGUI).

17. Use separate prefabs for specialisation; do not specialise instances. If you have two enemy types, and they only differ by their properties, make separate prefabs for the properties, and link them in. This makes it possible to

  • make changes to each type in one place
  • make changes without having to change the scene.

If you have too many enemy types, specialisation should still not be made in instances in the editor. One alternative is to do it procedurally, or using a central file / prefab for all enemies. A single drop down could be used to differentiate enemies, or an algorithm based on enemy position or player progress.

18. Link prefabs to prefabs; do not link instances to instances. Links to prefabs are maintained when dropping a prefab into a scene; links to instances are not. Linking to prefabs whenever possible reduces scene setup, and reduce the need to change scenes.

19. As far as possible, establish links between instances automatically. If you need to link instances, establish the links programmatically. For example, the player prefab can register itself with the GameManager when it starts, or the GameManager can find the Player prefab instance when it starts.

Don’t put meshes at the roots of prefabs if you want to add other scripts. When you make the prefab from a mesh, first parent the mesh to an empty game object, and make that the root. Put scripts on the root, not on the mesh node. That way it is much easier to replace the mesh with another mesh without loosing any values that you set up in the inspector.

Use linked prefabs as an alternative to nested prefabs. Unity does not support nested prefabs, and existing third-party solutions can be dangerous when working in a team because the relationship between nested prefabs is not obvious.

20. Use safe processes to branch prefabs. The explanation use the Player prefab as an example.

Make a risky change to the Player prefab is as follows:

  1. Duplicate the Player prefab.
  2. Rename the duplicate to __Player_Backup.
  3. Make changes to the Player prefab.
  4. If everything works, delete __Player_Backup.

Do not name the duplicate Player_New, and make changes to it!

Some situations are more complicated. For example, a certain change may involve two people, and following the above process may break the working scene for everyone until person two finished. If it is quick enough, still follow the process above. For changes that take longer, the following process can be followed:

  1. Person 1:
    1. Duplicate the Player prefab.
    2. Rename it to __Player_WithNewFeature or __Player_ForPerson2.
    3. Make changes on the duplicate, and commit / give to Person 2.
  2. Person 2:
    1. Make changes to new prefab.
    2. Duplicate Player prefab, and call it __Player_Backup.
    3. Drag an instance of __Player_WithNewFeature into the scene.
    4. Drag the instance onto the original Player prefab.
    5. If everything works, delete __Player_Backup and __Player_WithNewFeature.

Extensions and MonoBehaviourBase

21. Extend your own base mono behaviour, and derive all your components from it.

This allows you to implement some general functionality, such as type safe Invoke, and more complicated Invokes (such as random, etc.).

22. Define safe methods for Invoke, StartCoroutine and Instantiate.

Define a delegate Task, and use it to define methods that don’t rely on string names. For example:

public void Invoke(Task task, float time)
{
   Invoke(task.Method.Name, time);
}

23. Use extensions to work with components that share an interface. It is sometimes convenient to get components that implement a certain interface, or find objects with such components.

The implementations below uses typeof instead of the generic versions of these functions. The generic versions don’t work with interfaces, but typeof does. The methods below wraps this neatly in generic methods.

//Defined in the common base class for all mono behaviours
public I GetInterfaceComponent<I>() where I : class
{
   return GetComponent(typeof(I)) as I;
}
 
public static List<I> FindObjectsOfInterface<I>() where I : class
{
   MonoBehaviour[] monoBehaviours = FindObjectsOfType<MonoBehaviour>();
   List<I> list = new List<I>();
 
   foreach(MonoBehaviour behaviour in monoBehaviours)
   {
      I component = behaviour.GetComponent(typeof(I)) as I;
 
      if(component != null)
      {
         list.Add(component);
      }
   }
 
   return list;
}

24. Use extensions to make syntax more convenient. For example:

public static class CSTransform 
{
   public static void SetX(this Transform transform, float x)
   {
      Vector3 newPosition = 
         new Vector3(x, transform.position.y, transform.position.z);
 
      transform.position = newPosition;
   }
   ...
}

25. Use a defensive GetComponent alternative. Sometimes forcing component dependencies (through RequiredComponent) can be a pain. For example, it makes it difficult to change components in the inspector (even if they have the same base type). As an alternative, the following extension of GameObject can be used when a component is required to print out an error message when it is not found.

public static T GetSafeComponent<T>(this GameObject obj) where T : MonoBehaviour
{
   T component = obj.GetComponent<T>();
 
   if(component == null)
   {
      Debug.LogError("Expected to find component of type " 
         + typeof(T) + " but found none", obj);
   }
 
   return component;
}

Idioms

26. Avoid using different idioms to do the same thing. In many cases there are more than one idiomatic way to do things. In such cases, choose one to use throughout the project. Here is why:

  • Some idioms don’t work well together. Using one idiom well forces design in one direction that is not suitable for another idiom.
  • Using the same idiom throughout makes it easier for team members to understand what is going on. It makes structure and code easier to understand. It makes mistakes harder to make.

Examples of idiom groups:

  • Coroutines vs. state machines.
  • Nested prefabs vs. linked prefabs vs. God prefabs.
  • Data separation strategies.
  • Ways of using sprites for states in 2D games.
  • Prefab structure.
  • Spawning strategies.
  • Ways to locate objects: by type vs. name vs. tag vs. layer vs. reference (“links”).
  • Ways to group objects: by type vs. name vs. tag vs. layer vs. arrays of references (“links”).
  • Finding groups of objects versus self registration.
  • Controlling execution order (Using Unity’s execution order setup versus yield logic versus Awake / Start and Update / Late Update reliance versus manual methods versus any-order architecture).
  • Selecting objects / positions / targets with the mouse in-game: selection manager versus local self-management.
  • Keeping data between scene changes: through PlayerPrefs, or objects that are not Destroyed when a new scene is loaded.
  • Ways of combining (blending, adding and layering) animation.

Time

27. Maintain your own time class to make pausing easier. Wrap Time.DeltaTime and Time.TimeSinceLevelLoad to account for pausing and time scale. It requires discipline to use it, but will make things a lot easier, especially when running things of different clocks (such as interface animations and game animations).

Spawning Objects

28. Don’t let spawned objects clutter your hierarchy when the game runs. Set their parents to a scene object to make it easier to find stuff when the game is running. You could use a empty game object, or even a singleton with no behaviour to make it easier to access from code. Call this object DynamicObjects.

Class Design

29. Use singletons for convenience. The following class will make any class that inherits from it a singleton automatically:

public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
{
   protected static T instance;
 
   /**
      Returns the instance of this singleton.
   */
   public static T Instance
   {
      get
      {
         if(instance == null)
         {
            instance = (T) FindObjectOfType(typeof(T));
 
            if (instance == null)
            {
               Debug.LogError("An instance of " + typeof(T) + 
                  " is needed in the scene, but there is none.");
            }
         }
 
         return instance;
      }
   }
}

Singletons are useful for managers, such as ParticleManager or AudioManager or GUIManager.

  • Avoid using singletons for unique instances of prefabs that are not managers (such as the Player). Not adhering to this principle complicates inheritance hierarchies, and makes certain types of changes harder. Rather keep references to these in your GameManager (or other suitable God class 😉 )
  • Define static properties and methods for public variables and methods that are used often from outside the class. This allows you to write GameManager.Player instead of GameManager.Instance.player.

30. For components, never make variables public that should not be tweaked in the inspector. Otherwise it will be tweaked by a designer, especially if it is not clear what it does. In some rare cases it is unavoidable. In that case use a two or even four underscores to prefix the variable name to scare away tweakers:

public float __aVariable;

31. Separate interface from game logic. This is essentially the MVC pattern.

Any input controller should only give commands to the appropriate components to let them know the controller has been invoked. For example in controller logic, the controller could decide which commands to give based on the player state. But this is bad (for example, it will lead to duplicate logic if more controllers are added). Instead, the Player object should be notified of the intent of moving forward, and then based on the current state (slowed or stunned, for example) set the speed and update the player facing direction. Controllers should only do things that relate to their own state (the controller does not change state if the player changes state; therefore, the controller should not know of the player state at all). Another example is the changing of weapons. The right way to do it is with a method on Player SwitchWeapon(Weapon newWeapon), which the GUI can call. The GUI should not manipulate transforms and parents and all that stuff.

Any interface component should only maintain data and do processing related to it’s own state. For example, do display a map, the GUI could compute what to display based on the player’s movements. However, this is game state data, and does not belong in the GUI. The GUI should merely display game state data, which should be maintained elsewhere. The map data should be maintained elsewhere (in the GameManager, for example).

Gameplay objects should know virtually nothing of the GUI. The one exception is the pause behaviour, which is may be controlled globally through Time.timeScale (which is not a good idea as well… see ). Gameplay objects should know if the game is paused. But that is all. Therefore, no links to GUI components from gameplay objects.

In general, if you delete all the GUI classes, the game should still compile.

You should also be able to re-implement the GUI and input without needing to write any new game logic.

32. Separate state and bookkeeping. Bookkeeping variables are used for speed or convenience, and can be recovered from the state. By separating these, you make it easier to

  • save the game state, and
  • debug the game state.

One way to do it is to define a SaveData class for each game logic class. The

[Serializable]
PlayerSaveData
{
   public float health; //public for serialisation, not exposed in inspector
} 
 
Player
{
   //... bookkeeping variables
 
   //Don’t expose state in inspector. State is not tweakable.
   private PlayerSaveData playerSaveData; 
}

33. Separate specialisation configuration.

Consider two enemies with identical meshes, but different tweakables (for instance different strengths and different speeds). There are different ways to separate data. The one here is what I prefer, especially when objects are spawned, or the game is saved. (Tweakables are not state data, but configuration data, so it need not be saved. When objects are loaded or spawned, the tweakables are automatically loaded in separately)

  • Define a template class for each game logic class. For instance, for Enemy, we also define EnemyTemplate. All the differentiating tweakables are stored in EnemyTemplate
  • In the game logic class, define a variable of the template type.
  • Make an Enemy prefab, and two template prefabs WeakEnemyTemplate and StrongEnemyTemplate.
  • When loading or spawning objects, set the template variable to the right template.

This method can become quite sophisticated (and sometimes, needlessly complicated, so beware!).

For example, to better make use of generic polymorphism, we may define our classes like this:

public class BaseTemplate
{
   ...
}
 
public class ActorTemplate : BaseTemplate
{
   ...
}
 
public class Entity<EntityTemplateType> where EntityTemplateType : BaseTemplate
{
   EntityTemplateType template;
   ...
}
 
public class Actor : Entity <ActorTemplate>
{
   ...
}

34. Don’t use strings for anything other than displayed text. In particular, do not use strings for identifying objects or prefabs etc. One unfortunate exception is animations, which generally are accessed with their string names.

35. Avoid using public index-coupled arrays. For instance, do not define an array of weapons, an array of bullets, and an array of particles , so that your code looks like this:

public void SelectWeapon(int index)
{ 
   currentWeaponIndex = index;
   Player.SwitchWeapon(weapons[currentWeapon]);
}
 
public void Shoot()
{
   Fire(bullets[currentWeapon]);
   FireParticles(particles[currentWeapon]);   
}

The problem for this is not so much in the code, but rather setting it up in the inspector without making mistakes.

Rather, define a class that encapsulates the three variables, and make an array of that:

[Serializable]
public class Weapon
{
   public GameObject prefab;
   public ParticleSystem particles;
   public Bullet bullet;
}

The code looks neater, but most importantly, it is harder to make mistakes in setting up the data in the inspector.

36. Avoid using arrays for structure other than sequences. For example, a player may have three types of attacks. Each uses the current weapon, but generates different bullets and different behaviour.

You may be tempted to dump the three bullets in an array, and then use this kind of logic:

public void FireAttack()
{
   /// behaviour
   Fire(bullets[0]);
}
 
public void IceAttack()
{
   /// behaviour
   Fire(bullets[1]);
}
 
public void WindAttack()
{
   /// behaviour
   Fire(bullets[2]);
}

Enums can make things look better in code…

public void WindAttack()
{
   /// behaviour
   Fire(bullets[WeaponType.Wind]);
}

…but not in the inspector.

It’s better to use separate variables so that the names help show which content to put in. Use a class to make it neat.

[Serializable]
public class Bullets
{
   public Bullet FireBullet;
   public Bullet IceBullet;
   public Bullet WindBullet;
}

This assumes there is no other Fire, Ice and Wind data.

37. Group data in serializable classes to make things neater in the inspector. Some entities may have dozens of tweakables. It can become a nightmare to find the right variable in the inspector. To make things easier, follow these steps:

  • Define separate classes for groups of variables. Make them public and serializable.
  • In the primary class, define public variables of each type defined as above.
  • Do not initialize these variables in Awake or Start; since they are serializable, Unity will take care of that.
  • You can specify defaults as before by assigning values in the definition;

This will group variables in collapsible units in the inspector, which is easier to manage.

[Serializable]
public class MovementProperties //Not a MonoBehaviour!
{
   public float movementSpeed;
   public float turnSpeed = 1; //default provided
}
 
public class HealthProperties //Not a MonoBehaviour!
{
   public float maxHealth;
   public float regenerationRate;
}
 
public class Player : MonoBehaviour
{
   public MovementProperties movementProeprties;
   public HealthPorperties healthProeprties;
}

Text

38. If you have a lot of story text, put it in a file. Don’t put it in fields for editing in the inspector. Make it easy to change without having to open the Unity editor, and especially without having to save the scene.

39. If you plan to localise, separate all your strings to one location. There are many ways to do this. One way is to define a Text class with a public string field for each string, with defaults set to English, for example. Other languages subclass this and re-initialize the fields with the language equivalents.

More sophisticated techniques (appropriate when the body of text is large and / or the number of languages is high) will read in a spread sheet and provide logic for selecting the right string based on the chosen language.

Testing and Debugging

40. Implement a graphical logger to debug physics, animation, and AI. This can make debugging considerably faster. See here.

41. Implement a HTML logger. In some cases, logging can still be useful. Having logs that are easier to parse (are colour coded, has multiple views, records screenshots) can make log-debugging much more pleasant. See here.

42. Implement your own FPS counter. Yup. No one knows what Unity’s FPS counter really measures, but it is not frame rate. Implement your own so that the number can correspond with intuition and visual inspection.

43. Implement shortcuts for taking screen shots. Many bugs are visual, and are much easier to report when you can take a picture. The ideal system should maintain a counter in PlayerPrefs so that successive screenshots are not overwritten. The screenshots should be saved outside the project folder to avoid people from accidentally committing them to the repository.

44. Implement shortcuts for printing the player’s world position. This makes it easy to report the position of bugs that occur in specific places in the world, which in turns makes it easier to debug.

45. Implement debug options for making testing easier. Some examples:

  • Unlock all items.
  • Disable enemies.
  • Disable GUI.
  • Make player invincible.
  • Disable all gameplay.

46. For teams that are small enough, make a prefab for each team member with debug options. Put a user identifier in a file that is not committed, and is read when the game is run. This why:

  • Team members do not commit their debug options by accident and affect everyone.
  • Changing debug options don’t change the scene.

47. Maintain a scene with all gameplay elements. For instance, a scene with all enemies, all objects you can interact with, etc. This makes it easy to test functionality without having to play too long.

48. Define constants for debug shortcut keys, and keep them in one place. Debug keys are not normally (or conveniently) processed in a single location like the rest of the game input. To avoid shortcut key collisions, define constants in a central place. An alternative is to process all keys in one place regardless of whether it is a debug function or not. (The downside is that this class may need extra references to objects just for this).

Documentation

49. Document your setup. Most documentation should be in the code, but certain things should be documented outside code. Making designers sift through code for setup is time-wasting. Documented setups improved efficiency (if the documents are current).

Document the following:

  • Layer uses (for collision, culling, and raycasting – essentially, what should be in what layer).
  • Tag uses.
  • GUI depths for layers (what should display over what).
  • Scene setup.
  • Idiom preferences.
  • Prefab structure.
  • Animation layers.

Naming Standard and Folder Structure

50. Follow a documented naming convention and folder structure. Consistent naming and folder structure makes it easier to find things, and to figure out what things are.

You will most probably want to create your own naming convention and folder structure. Here is one as an example.

Naming General Principles

  1. Call a thing what it is. A bird should be called Bird.
  2. Choose names that can be pronounced and remembered. If you make a Mayan game, do not name your level QuetzalcoatisReturn.
  3. Be consistent. When you choose a name, stick to it.
  4. Use Pascal case, like this: ComplicatedVerySpecificObject. Do not use spaces, underscores, or hyphens, with one exception (see Naming Different Aspects of the Same Thing).
  5. Do not use version numbers, or words to indicate their progress (WIP, final).
  6. Do not use abbreviations: DVamp@W should be DarkVampire@Walk.
  7. Use the terminology in the design document: if the document calls the die animation Die, then use DarkVampire@Die, not DarkVampire@Death.
  8. Keep the most specific descriptor on the left: DarkVampire, not VampireDark; PauseButton, not ButtonPaused. It is, for instance, easier to find the pause button in the inspector if not all buttons start with the word Button. [Many people prefer it the other way around, because that makes grouping more obvious visually. Names are not for grouping though, folders are. Names are to distinguish objects of the same type so that they can be located reliably and fast.]
  9. Some names form a sequence. Use numbers in these names, for example, PathNode0, PathNode1. Always start with 0, not 1.
  10. Do not use numbers for things that don’t form a sequence. For example, Bird0, Bird1, Bird2 should be Flamingo, Eagle, Swallow.
  11. Prefix temporary objects with a double underscore __Player_Backup.

Naming Different Aspects of the Same Thing

Use underscores between the core name, and the thing that describes the “aspect”. For instance:

  • GUI buttons states EnterButton_Active, EnterButton_Inactive
  • Textures DarkVampire_Diffuse, DarkVampire_Normalmap
  • Skybox JungleSky_Top, JungleSky_North
  • LOD Groups DarkVampire_LOD0, DarkVampire_LOD1

Do not use this convention just to distinguish between different types of items, for instance Rock_Small, Rock_Large should be SmallRock, LargeRock.

Structure

The organisation of your scenes, project folder, and script folder should follow a similar pattern.

Folder Structure

Materials
GUI
Effects
Meshes
   Actors
      DarkVampire
      LightVampire
      ...
   Structures
      Buildings
      ...
   Props
      Plants
      ...
   ...
Plugins
Prefabs
   Actors
   Items
   ...
Resources
   Actors
   Items
   ...
Scenes
   GUI
   Levels
   TestScenes
Scripts
Textures
GUI
Effects
...

Scene Structure

Cameras
Dynamic Objects
Gameplay
   Actors
   Items
   ...
GUI
   HUD
   PauseMenu
   ...
Management
Lights
World
   Ground
   Props
   Structure
   ...

Scripts Folder Structure

ThirdParty
   ...
MyGenericScripts
   Debug
   Extensions
   Framework
   Graphics
   IO
   Math
   ...
MyGameScripts
   Debug
   Gameplay
      Actors
      Items
      ...
   Framework
   Graphics
   GUI
   ...

How to Re-implement Inspector Drawing

1. Define a base class for all your editors

BaseEditor<T> : Editor 
where T : MonoBehaviour
{
   override public void OnInspectorGUI()
   {
      T data = (T) target;
 
      GUIContent label = new GUIContent();
      label.text = "Properties"; //
 
      DrawDefaultInspectors(label, data);
 
      if(GUI.changed)
      {         
         EditorUtility.SetDirty(target);
      }
   }
}

2. Use reflection and recursion to do draw components

public static void DrawDefaultInspectors<T>(GUIContent label, T target)
   where T : new()
{
   EditorGUILayout.Separator();
   Type type = typeof(T);      
   FieldInfo[] fields = type.GetFields();
   EditorGUI.indentLevel++;
 
   foreach(FieldInfo field in fields)
   {
      if(field.IsPublic)
      {
         if(field.FieldType == typeof(int))
         {
            field.SetValue(target, EditorGUILayout.IntField(
            MakeLabel(field), (int) field.GetValue(target)));
         }   
         else if(field.FieldType == typeof(float))
         {
            field.SetValue(target, EditorGUILayout.FloatField(
            MakeLabel(field), (float) field.GetValue(target)));
         }
 
         ///etc. for other primitive types
 
         else if(field.FieldType.IsClass)
         {
            Type[] parmTypes = new Type[]{ field.FieldType};
 
            string methodName = "DrawDefaultInspectors";
 
            MethodInfo drawMethod = 
               typeof(CSEditorGUILayout).GetMethod(methodName);
 
            if(drawMethod == null)
            {
               Debug.LogError("No method found: " + methodName);
            }
 
            bool foldOut = true;
 
            drawMethod.MakeGenericMethod(parmTypes).Invoke(null, 
               new object[]
               {
                  MakeLabel(field),
                  field.GetValue(target)
               });
         }      
         else
         {
            Debug.LogError(
               "DrawDefaultInspectors does not support fields of type " +
               field.FieldType);
         }
      }         
   }
 
   EditorGUI.indentLevel--;
}

The above method uses the following helper:

private static GUIContent MakeLabel(FieldInfo field)
{
   GUIContent guiContent = new GUIContent();      
   guiContent.text = field.Name.SplitCamelCase();      
   object[] descriptions = 
      field.GetCustomAttributes(typeof(DescriptionAttribute), true);
 
   if(descriptions.Length > 0)
   {
      //just use the first one.
      guiContent.tooltip = 
         (descriptions[0] as DescriptionAttribute).Description;
   }
 
   return guiContent;
}

Note that it uses an annotation in your class code to generate a tooltip in the inspector.

3. Define new Custom Editors

Unfortunately, you will still need to define a class for each MonoBehaviour. Fortunately, these definitions can be empty; all the actual work is done by the base class.

[CustomEditor(typeof(MyClass))]
public class MyClassEditor : BaseEditor<MyClass>
{}

In theory this step can be automated, but I have not tried it.


About Herman Tulleken

Herman Tulleken is a game developer co-founder of Plinq. He thinks computers are a necessary evil to make games, and sees frying a CPU or two in the process as a worthy sacrifice.

137 thoughts on “50 Tips for Working with Unity (Best Practices)

  • Daniele C.

    It is always nice to read wonderful lists of good working behaviours. Most of them are good for general use, not only for Unity projects. What a pity too many developers will never understand how important coding conventions and safe procedures are for teamwork or multiplatform development. This makes me sad 🙂

    Thanks for sharing this. Every studio should have them etched in stone.

  • Andy

    Some advice were great, but beware, using prefabs fro anything that is not polished would lead to a waste of time. If you do some box to be broken, finish them completely then prefab them, or don’t, but eventually you’ll see what I’m talking about.

    By the way, Thanks Tulleken !

  • Leslie

    “Consider using external level tools for level editing”
    I’m working on a major update to one of my Asset Store packages to deal with tiled worlds inside Unity.
    Here is a sneak peak at a WIP version of DungeonEd http://www.youtube.com/watch?v=YKFLh0ytnBg
    It might be called “Dungeon Editor’ but will also be usefull for “outside” terrain and such 😉

  • Rhys

    Excellent post. It’s good to see some of my practices confirmed and criticized. I will take a lot of this on board.

    Regarding item 30, I can never think of a situation where a public variable is required. Instead of using public variables we explicitly serialize private vars. If you want a field that can be accessed by other scripts directly, we pascal case it and optionally hide it in the inspector.


    [SerializeField] float m_maxSpeed = 5f;
    [HideInInspector] public int Health = 10;

    The rationale for the pascal case on the public variable is that if we decide it requires side effects you can simply replace it with a public property without disrupting dependent scripts.

    • Dev.Mag

      I recently had to make a variable public to have data saved in the prefab (the data was filled in with an editor tool). Maybe a Unity bug, maybe I’m doing something stupid… but SerializeField did not work in this case 🙁

    • Aubrey Hesselgren

      you could also use “internal” instead of “public” if you want the variable accessible but not shown. I believe this is also automatically serialized, but I could be wrong.

  • Rune

    > It does not let you define inspector components on a field-type level, only a class-type level.

    Coming in Unity 4 🙂

  • steve

    I got a question on the code of “Re-implement Inspector Drawing”

    What *CSEditorGUILayout* class ‘s DrawDefaultInspectors looks like?

    As I understood, DrawDefaultInspectors needs to be recursively called to draw necessary member field, but how?

    And thank you for sharing the great article!

    • Dev.Mag

      Yup, you are right. The DrawDefaultInspectors class defined in step 2 is actually that method (it should be defined in CSEditorGUILayout). I should have said so, and will make the correction. Thanks!

    • Dev.Mag

      Drop me a mail then we can discuss it: herman dot tulleken at devmag dot org dot za.

  • gnoblin

    Hi!

    I get

    The type `T’ must have a public parameterless constructor in order to use it as parameter `T’ in the generic type or method `BaseEditor.DrawDefaultInspectors(UnityEngine.GUIContent, T)’

    error. What should I do about it?

    thanks,
    Slav

    • Dev.Mag

      It basically means T must have a default constructor without any parameters. You could just define one, for example

      public class MyClass
      {
         public MyClass() 
         {
            //initialize fields
            //Stuff
         }
      }

      It is required because of the : new() constraint. (To be honest, I can’t remember why I put the constraint in… you could try taking it out to see what happens. Otherwise I will investigate and add the explanation.)

  • gnoblin

    could you post an archived example project where this code works? (just one commented monobeh in a scene)

    thanks,
    Slav

  • Tim

    This is probably the best Unity “tips” article I’ve ever read. Very informative! I came up with a few questions while I was reading and was wondering if Herman, or any one else had any comments/answers to my questions.

    #4 – Any example projects, code examples, or direction on how to acheive this?

    #26 – “Coroutines vs. state machines.” Are you saying use Coroutines or State Machines, but not both? Or, Deciding if you are going to create state machines through the use of coroutines or if you are going to create state machines by some other method?

    In addition to #26. I’m really struggling trying to decide how I want manage state. I’ve seen several good FSMs and then there are some who say don’t both, just use coroutines. Then I’ve seen a hybrid of both. Any thoughts/tips on this, or examples of how you manage state on an object?

    #27 I understand what you are trying to achieve and why, but I don’t understand how you are explaining to do it. Could I get a short example of how you are wrapping Time.DeltaTime and Time.SinceLevelLoad?

    #34 says “Don’t use strings for anything other than displayed text”. Do you not use Tags at all? Aren’t strings the only way to reference tags/layers through code?

    Any feedback is greatly appreciated, Thanks!

    • Herman Tulleken Post author

      Thanks Tim!

      About #4. It’s quite a big chunk of code. I will see and post some if it at some stage. The basic idea is to split off data that needs to be saved into Serializable classes, implement a common interface for Savable classes (that includes pre-saving and post-loading logic, for example, to copy transforms), then implement methods that serialize and deserialize lists of your Savable objects [and do the stuff necessary to link this data up to the actual game objects] (including instantiating assets from Resources), and a method to clear Savables from the scene. The save and load needs to work both run time and edit time. It is easier to start getting things work runtime, and then specialize behaviour for edit time (so reducing the risk of stuffing up scenes).

      All in all, not worth it if your levels are simple, and you only have a few. It also requires very clear thinking, otherwise everything can become real messy.

      #26. Yes, deciding to use co-routines for states, vs a nice wrapped solution. We use light weight state machines that transition on command; each object maintains it’s own. The main reason not to mix these (or other idioms on the list) is so that there is one way states are maintained for objects. This also does not necessarily exclude hybrid approaches, as long as the hybrid is used consistently [one way would be to neatly wrap it up so that the underlying details are hidden]. So any programmer that opens up any game logic class knows what the logic entry points and flow are, instead of having to figure it out for each one case-by-case.

      #27. The simplest way to do it is something like this for example (I only show delta time, for Time since level loaded you have to maintain your own cumulative value, and make sure it executes before anything else):

      static float DeltaTime
      {
         get { if(GlobalManager.IsGamePaused) return 0; else return Time.deltaTime;}      
      }
      

      There may be a small performance impact (but usually so small it does not matter, especially in 3D render-heavy games.

      #34. In fact, I don’t use tags at all. Partially because I found string access so distasteful, and partially because tags are so limited. My preferred way to check for groups is to either check a component (or interface), or have objects register themselves in a list if a list is required. I know some good designs that DO use tags however, so it, with animation clips, is another unfortunate exception to that rule.

      I use layers for collision culling and raycasts culling, but only access this through layer masks that are set up in the inspector, never through raw strings. Of course, restricting layers to these uses is a matter of taste, but even for other uses layer mask variables can be used instead of strings. A sub good practice would be to implement extensions to make layer checks more readable.

      • Tim

        Thanks for all the great information! You said ” We use light weight state machines that transition on command” could you give a quick snippet of how your state machines work? Since there are endless ways to do state machines I would like to see one that someone has/is using within Unity. Thanks again for all the great information.

  • Henning

    Thank you SO much! As I’m new to Unity (12 days) and just putting the final tuchses on my first Unity game. It’s really nice to find some good solid guidelines/best practices.
    Now I have a better grasp of what to do different for my next game, to make the process a lot easier.

    What is you input on GUI design.. After testing my game in different resolutions and aspect ratios, I’m getting a serious urge to make all menus using Text Meshs instead of using UnityGUI/Skin placement hell.

    Thanks again.

    • Herman Tulleken Post author

      A GUI is one of the few things that is almost in every game; I can’t understand why better systems has not been available for much longer. (At least the GUI system in Unity 4 looks great).

      We always use EZGUI, because Unity’s build-in GUI framework is so slow (on mobile it makes a huge difference). It has it’s own sets of problems, but I think overall it’s a powerful system.

      • Henning

        Thanks for your input. I guess I’ll have to do some kind of configs for each resolution, with placement and scale information for my GUI menus.

        EZGUI is out of my price range for now, but you never know, everyone can make the new angry birds with Unity 🙂

        Have a very nice day.

        • Adam Reed

          I would take a look at NGUI
          http://www.tasharen.com/?page_id=140

          They have a free evaluation version that you can develop with, and the price for the real thing is 95$. (There is also a 200$ “full” license)

          If your price range is, well, non existent, you could check out
          http://code.google.com/p/bitverse-unity-gui/

          Which is opensource, though in my opinion the editors are a bit…crowded.

          I am currently working on my own solution, that wraps around Unity’s but the more I work with it the more I want to switch to 2D textures in the scene that I can control with transforms. Still debating on whether to go down that path or not.

          Hope this helped, good luck, and have fun.

  • Adam Reed

    Thanks for the tips especially the ones on the custom editor. I have one concern though: I noticed that you did not use SerializedObject for your editor, is there a reason?

    I have been planning on building a Editor base class that used reflection and the SerializedPropertys to allow the flexibility I want and also the benefits from using SerializedObject (prefab override, undo, multi editing etc) but I wonder if there is somethingthat I am not aware of that made you decide not to in your example.

    Thanks again for the wonderful tips.

    • Herman Tulleken Post author

      There is a reason, although not a very good one. I could not get it to work, or it did not work as expected (or I wanted it to work). I started using it, but gave up at some point. It would be nice if you can get it to work to post an example somewhere.

  • Jonathan MacAlpine

    What are your thoughts on how Unity mingles assets and folders together in the Project view? Do you think it’s worthwhile to set up the folder hierarchy in such a way that there are only ever folders *or* assets within a folder?

    • Herman Tulleken Post author

      Yes, it is annoying. I do think it is a good idea to set it up as you describe. Looking at our projects, I see we have not done it religiously, but is almost a natural consequence of just keeping things neat.

  • Jonathan MacAlpine

    I’ve been trying to sort out the rewriting-custom-inspector jazz, but I can’t make heads of tails of it. When I try to enter the code as-is, much of it won’t compile (for example, due to the SplitCamelCase method present). Do the three methods listed here belong under one common class (BaseEditor)?

    I’m fairly new to Unity and to C# in general, and I can’t sort it out! 🙁

  • Tim Aidley

    Re #30, is there any reason why you wouldn’t just use [HideInInspector] to prevent someone playing with a public member variable?

  • shinriyo

    The source code of #27.
    I guess it can’t be compiled. Because it return object type.

    MonoBehaviour[] monoBehaviours = FindObjectsOfType();

    I fixed below and it can be compiled.
    MonoBehaviour[] monoBehaviours = FindObjectsOfType(typeof(MonoBehaviour)) as MonoBehaviour[];

  • shinriyo

    Hi. I have just a question.
    How do you use Singleton class?
    I think it can’t attach to GameObject. it must be instance and assign some object to this

    Instance property?

    Singleton singleton = new Singleton();
    singleton.Instance = hogeObject;

    • caue

      As reference for one way this could be done, and while not sure if this would be a best practice, I’ve changed the singleton implementation for myself so we don’t need to manually attach it to an object (same way as DebugConsole works). Here’s just the part changed:

      if (_instance == null)
      {
      GameObject singleton = new GameObject();
      singleton.AddComponent();
      singleton.name = "(singleton) "+ typeof(T).ToString();

      DontDestroyOnLoad(singleton);

      _instance = FindObjectOfType( typeof(T) ) as T;

      Debug.Log("[Singleton] An instance of " + typeof(T) +
      " is needed in the scene, so '" + singleton +
      "' was created.");
      }

  • Chris Amyot

    First of all – thank you for this GREAT article. I’ve been using the pattern you’ve described in #’s 32 and 37, using serializable non-monobehavior classes to encapsulate groups of variables. I’m relatively new to C#, so you kind of blew my mind there.

    While using this technique in Unity, I came to a stumbling block – copy/pasting the code from either of those examples provided the error:
    “Assets/Scripts/TestClass.cs(9,2): error CS0246: The type or namespace name `Serializable’ could not be found. Are you missing a using directive or an assembly reference?”

    The fix I found for this is to use [System.Serializable] instead of [Serializable] – not sure if there’s a better way of doing this..?

    Also – it’s worth noting that you must include the [System.Serializable] attribute above any class declaration that you want to show up in your inspector.

    Thanks again Herman – this is exactly the kind of resource I’d been looking for!

    • Chris Amyot

      Actually – just realized that if I included “using System;” at the top of my CS file I could use [Serializable] instead of [System.Serializable]. It seems to me (as a newcomer to C#), that including the System namespace in your classes would bring in a lot of unnecessary functionality.. Is this not the case? Is the compiler smart enough to include only the required functionality from that namespace?

      • Dev.Mag

        I was just about to reply that you could do that too 🙂 I think the compiler is smart enough, I personally use using System without giving it much thought. Thanks for the comment; glad you enjoyed the article!

      • Amir Abiri

        There is absolutely no penalty for including a namespace, it’s just not how the C# language works. The using directive only helps the compiler resolve type names that appear in the code file, nothing more. It’s just a way to narrow down the search and avoid ambiguity.

        During compilation, these references to other types are resolved and enter in their fully qualified form into the resulting bytecode. From there on the using directive is meaningless. The only thing that matters is which assemblies the compiled code needs to pull in or reference.

        In that respect there is no difference between “using System” or “System.Serializable”, in both cases the code requires a reference to the assembly that contains the System.Serializable class, irrespective of what else the using directives may contain.

  • shachar Oz

    excellent.

    our team at Omek has learned not a few out of the 50 you wrote, so these tips are fully supported by us too.

  • thallippoli

    Thank you for this wonderful collection – it’s a great reference!

    About Item #29,
    public class Singleton : MonoBehaviour where T : MonoBehaviour

    wouldn’t this line work equally well as

    public class Singleton : MonoBehaviour where T : class

    I’m trying to understand why you used “where T : MonoBehaviour”

    because in my mind that looks redundant – as I understand, the expected usage would be like this

    public class World : Singleton

    Your code does work without any hitches though 🙂

    • devmag

      If you specify “where T : Monobehaviour”, you can take advantage of Monobehavior methods in your singleton class if you needed to add more functionality.

      If you use your specification, this for example, would be illegal:

      if(instance == null)
      {
      instance = (T) FindObjectOfType(typeof(T));
      instance.transform.position = Vector3.zero;

      //… other stuff
      return instance;
      }

  • Amir Abiri

    RE 46 – you can use PlayerPrefs with a custom inspector on a dummy ScriptableObject. That way debug settings are stored in user data (registry / user home dir) and there is no need to have multiple or user-named objects and mess around with Subversion ignores for those objects.

  • Whitebread

    I know this is a bit old at this point, but I had a question in regards to the MakeLabel function. I am getting an error on field.Name.SplitCamelCase() saying that string does not contain a definition for it. Is this defined somewhere else?

  • Thomas

    Hey all,

    I’m not able to recreate the steps from “How to Re-implement Inspector Drawing” into final cs files. Could someone share that final or near work?

    Thanks for this article !

    Bye

    • Matt Schoen

      This is a bit of a complicated answer (which I won’t go into too deeply), but you should look into SerializedProperty. Once you get a hang of the iterator pattern, it makes re-creating the inspector a breeze!

  • Rohit

    I found your tips very interesting. I have a problem. I made an object in Maya and imported it into unity but the movement speed on device while testing is very slow. Can you please tell me that whether is it fault with some maya fbx file or in Unity? I spent 2 days on this and reached nowhere. Please help. (Maya file is an office view)

  • caue

    I’m not sure I follow what you mean on steps 1, 2 and 30.

    1. By “branch” you mean just having an asset backup copy outside of the repository? If so, I completely agree – this should never be done.

    2. By second copy, you basically mean having the whole project with its compiled library at hand? That makes a lot of sense, but then why not also enforcing the repository usage on the rule? I don’t see why that’s not included.

    30. Why not simply using [System.NonSerialized] to hide public parameters from the inspector?

    Additionally, I’d advice using POedit for dealing with text languages, on the tip #39: http://wiki.unity3d.com/index.php?title=TextManager

    Fucking awesome list, by the way. Kinda sad it’s good being that big… Hope Unity can “fix” most of it soon enough, though I couldn’t bother taking note of how many here are things Unity could “fix”. 🙂

      • caue

        That sounds unreal! And seems like it even works with git!

        I hate the Asset Server, and I advice no one to ever use it (in its current form anyway). It’s basically older than svn in terms of version control. Just saying that because you do mention it twice on the documentation, and seems like there is an issue with scene missing references and ID mismatches in which you say AssetServer might be needed… No idea what you meant there, but that might be because I haven’t tried your tool.

        The way I see it, a missing reference is missing. There’s nothind to be done there. Just report it’s missing.

    • devmag

      1. By branch I mean making any duplicate that is not the *actual working* one (not made through a formal version controlled branching process). This occurs frequently when programmers or artists work on complicated prefabs or gameObjects and are scared that they will break something. Although not-breaking-anything is a noble pursuit, the wrong version almost *always* come and confuse the hell out of somebody, wasting many hours.

      2. Yup, that is what I mean. On some projects version control is not used, in which case the rule does not apply. (I don’t recommend going without version control of course).

      30. Yes, the “on a rare occasion” part of that rule is really rare (I have not had the need for it for over a year now). I like the extremely ugly name for something that is also extremely ugly in itself. For me, a public variable _means_ it is a tweakable. So if I am not in the class, I treat it as something that should not be modified. The double underscore signifies something fishy is going on, and puts the programmer on alert. Of course, if for you public means public in the traditional sense, then simply using the annotation is perfectly fine. In either event, it really should be very rare, and is in itself not so important compared to the main part of the rule.

      39. Thanks for the suggestion. I admit the solution I give is a bit poor-man-style.

      Thanks for the feedback; it’s very interesting to hear other’s take on these points.

      • caue

        Thank you for the post! I’m trying to apply several of the rules here and the prefabbing scene one helped a great deal already, for merging scenes.

        I just think you should enforce way more the “use version control” idea. Even for 1 person minor projects free unity, just never go without a VCS. I see both rules #1 and #2 exist because people aren’t using it or are using it poorly.

        • devmag

          Yes I agree. Especially rule #1 is broken most often because of lack in faith in the versioning system, or lack in faith in his/her own capabilities to use it. Rule #2 is similarly to compensate for poor commitment practices.

  • D

    Bookmarked so hard. I am completely new to Unity but far from new in Game development in general. This is just the type of guide I needed to get started. Thank you so very much!

    If I had a suggestion for an improvement I would recommend putting in a single sentence for all the folders in the folder structure detailing what they contain. But I bet it’s obvious if you have been using Unity for any period of time.

  • Barrie Treloar

    Can you elaborate on point “34. Don’t use strings for anything other than displayed text.” What should use for identifying objects?

  • Bruno

    Tip number 30:

    [System.NonSerialized]
    public float __aVariable;

    this makes the variable not to appear on the editor even if it is public.

  • Amilia Jones

    This article is great, but the part I was most excited about is the part with which I’m having the most difficulty. The code in “How to Re-implement Inspector Drawing” looks great, but I’m not sure how to actually set this up in a cs file. I’ve tried, but I get many errors no matter what I seem to do.

  • thakk

    VampireDark is better than DarkVampire. I want all my VampireX mobs to appear together, not spread all over the goddamn list.

      • CharlesInCharge

        It is more of a problem when debugging in the scene view. Lets say you put all your spawns in a spawns folder so you don’t clutter the hierarchy. If you call them “DarkVampire” and “MrVampire”, then your vampires are going to be all over the list. You could add a logic code to your spawning code though and have it create the empty game object folders at run time. That way your scene view would have “SpawnsVampires[Dark,Mr]”. You could then put all that in an #if UNITY_EDITOR block so your performance isn’t even remotely hit.

        • devmag

          I normally use empty game objects liberally to organise my scene as well. I would typically root all my vampires to a game object (named Vampires) if I find it’s a problem. And of course the search box is very handy for finding vampires (however they are named). So yes, the rule works well in my workflow and structure. If it does not for you, ditch it 🙂

  • David Goemans

    Great list!
    Just a pointer for #30:
    If you have public variables, but don’t want them accessible in the inspector, you can add [HideInInspector] ( C# ) or @HideInInspector (JS) before the declaration. Conversely, if you want variables to be set in the inspector, but invisible in public scope, you can add [SerializeField] (C#) or @SerializeField.

  • Samuel Asher Rivello

    FANTASTIC Overview. So when we open a new project and import any Unity Technologies package and the only folder organization enforced is an ‘Assets’ folder with a subfolder of ‘Standard Assets’. What about our custom assets?

    Here is the SOLUTION. What Unity Folder Structure Do you Use? – http://bit.ly/1b7cLyq

    -Sam!

  • am

    Could you provide an example for point 31? I think there is some tight coupling in Unity that makes modifying state of an object update it on screen automatically. Especially when using physics. In your example where you modify the player velocity, if you do that on a rigidbody in unity you’ll move the object. Or do you mean that you should have:

    controller – code which calls something like Input.GetAxis and then calls a method on the model/game logic. i.e. like Move(Vector3 inputDirection).

    model/game logic – updates the player direction and velocity in the model class, but doesn’t touch the rigidBody or anything to actually move it. Then using some sort of event manager possibly send out a notification message for this?

    view/interface : Looks up the new velocity from the model code and just modifies the physics velocity on the rigid body. Also may change the direction sprite or rotation of the 3d model?

    Would you typically make this in one monobehavior script? Or multiple?

    There is a logical order that things must happen.
    Get the input – A MonoBehaviour class which gets the input

    Update the view – The MonoBehaviour has a reference

    So is view code really just your code which knows about unity things? Like rigidBodies and transforms? But your model is just game logic and nothing related to rigidBodies?

    Where might you put code that needs to know the context of unity physics? For example you want to cast a ray to determine if you are shooting an object. Would that be in your controller? This means the controller should also know about the unity state of the game.

  • Farrell

    This is awesome. I’m a veteran game developer, but new to Unity. There were some really nice tips.

  • UKMonkey

    Looks like some of these are there to cope with no source control… eg the safe modification/branching of assets is done entirely with source control with no renaming

  • Joe

    Your point on reimplementing inspectors is missing a ton of explanation. First off, CSEditorGUILayout is a class that extends BaseEditor??? Also, when you test if a field is a class, it can be a transform and all kinds of other objects that dont need to be rerendered. you just want to call their default method. I’m hammering out how to actually get this working now. Really great post though, been digesting it for months.

  • angrywill

    an extension to tip number “2. Each team member should have a second copy of the project checked out for testing”

    In addition to having a dev copy checked out I like to have copy checked out for every build target I intend to test on, i.e. I might have a clean PC build, webplayer build and android builds, when I want to test on those platforms I can get latest and open the project for that platform. Saves me a lot of time by avoiding rebuilding all the assets when switching the platform from the build settings menu.

  • Ricardo Amores Hernández

    First of all, thank you for sharing your experiences and practices Herman.

    I have one question. In tip 17 (specialize a prefab) you say “make separate prefabs for the properties, and link them in”

    ¿Could you explain what do you mean with “link” between prefabs? AFAIK you can’t have nested prefabs, so, do you mean linking the “child” prefab with the “parent” using an script?

    Thanks in advance!

  • Panayotis Kalaitzoglou

    Very informing practices! Especially for someone new to Unity, like I am.
    A question on #4: What is the suggested way to parse XML files? The mono libraries are considered quite heavy. Do you use
    – The built-in libraries
    – Your own custom parser
    – Or some other parser?
    Thank you in advance

    • Herman Tulleken

      I personally use the built-in libraries, but that on projects where the additional space is negligible in comparison to the project size. Recently I also discovered the power of ScriptObject, so I don’t use XML very often anymore.

  • Laurent Lavigne

    #30 Like David says, or use the codeword ‘internal’, that’ll hide it from the inspector and make it public to all classes.
    internal float youCantTinkerWithMe;

    • Herman Tulleken

      #3. Yup; and there are also some very nice assets available for building prototype levels.
      #27. Yes, that is a nasty case. The simplest way I assume, is to make your own version of WaitForSeconds class that uses the appropriate delta time.
      #30. Yup, except if you don’t want other programmers on the same project to tinker with it. But this (the need to have it public) is such a rare case, I should never have mentioned the possibility in that tip!
      #31. Yes, and it can become very complicated too. I tend to loosely separate classes into “smart” and “dumb”, which almost corresponds to M and VC, but the polish layer usually interferes with the separation quite a bit (especially when it comes to smooth transitions between discrete states). There are probably more abstract ways to deal with this though.

      • Laurent Lavigne

        #31 GUI is a mess, it reminds me of navigation a few years back, before AStar and Recast came along.
        I think the right way to do GUI is to pass all states through a state machine, like mecanim. The data can be moved back and forth by a layer that does data mapping.

  • Pavel Shevaev

    This is a great list, thanks! I’m a bit new to Unity and wonder whether all of the mentioned above points are valid to Unity 4.3.4 as well? This blog post dates back to 2012…

  • Guest

    hi

    i didn’t quite understand what you mean: “27. Wrap Time.DeltaTime and Time.TimeSinceLevelLoad to account for pausing and time scale.”

    can you please explain, what is “wrap smthg to account”? or is if this is not “term”, just expand, what you are was saying. i just can’t get to the meaning of the sentence. m.b. because english is not my native, or i’m just not understanding the very sense

  • Zl Zloidooraque Drq

    hi!

    very informative, thanks

    some questions, tho.
    here you say:
    “do not use strings for identifying objects or prefabs etc”

    but before, you said:

    “Ways to locate objects: by type vs. name vs. tag vs. layer vs. reference (“links”).”

    but name, tag, layer — all this are strings.

    how do one even identify object/objects without using its names? may be i’m missing something..

    and one can’t store all created/spawned object registered in separate place (and? in fact — semi-hardcoded) just to access them by references. or can he?

    i think it’s not flexible enough or even may be unreliable in some cases.

    i’m new to unity, but i’m not new to programming. and use of this concept for me is like.. making all members of class public, e.g. i mean, smart guy will not tweak anything inappropriate, but people make mistakes or sometimes one may be tempted to make “a shortcut” in code you know.. it’s like invitation to break MVC concept =)) a one must be very self-disciplined not to misuse such things

    or may be i’m just not getting the ideas and am not right from the beginning lol

    i’m pretty stunned i’ve stuck upon the article on unity programming concept in opposite to “drag this thing here, write this in ‘Update'”

  • RW

    Can you give a little more clarification on the specifics of the implementation of #33?

  • Guest

    A bit outdated, but still mostly actual list, thanks!

    Regarding #42 (writing own FPS Counter) – you may take my supercheap Advanced FPS Counter plugin from Asset Store to save you time! http://u3d.as/6kq

  • Mark Mayer

    Great read. I really appreciated the tips around separating things out into a template, configuration and state. In just toying around, I was had started to break some of my assets out into a template and configuration but after reading this, the addition of a serializable state seems to make a lot of sense.

  • Caio Cezar

    Can somebody help with the “shake effect”? Non answers in Unity forum solved this problem. In my 2D Game, my camera moves foward using *Time.DeltaTime, but the objects do a short “shake effect” sometimes.

    I have done all the religious sugestions of:
    * pixel perfect (camera size based on screen height and pixel to unit, use material with pixel-snap)
    * round camera position in pixel to unit grid
    * use late update properly
    * etc..

    Searched all forums and no solution, and I’m not the only one with this problem…

    If someone knows how to fix that pls reply.

  • Eric

    Good post overall but there are some really bad tips in here as well.

    Like the singleton thing. Recipe for disaster. Classes for prettier inspector display – also bad. Better just write a custom renderer specifically designed for pretty inspector displays. “Use external level editor” probably the worst advice on the page. External tool for snapping? Literally 3 lines of code with [ExecuteInEditMode]. Also it’s not ideal to depend on access level to determine what will be visible in the inspector. [SerializeField] and counterparts are there for this. The XML > scene is also pretty bad advice. Scenes do pretty much exactly what you are doing with XML. Though using XML to save persistent data is fair enough.

  • Eric

    In #29, why can I access private members of the singleton class?
    (I am using Singleton.Instance.privateMember)

Comments are closed.