When starting out in Unity, the name of core base types can be confusing. Let’s untangle them!
📦 GameObject
A GameObject (UnityEngine.GameObject) is an object that:
- Lives within a Scene
- Always has a Transform (or a RectTransform, for GUI objects) so it is somewhere
- Contains multiple Components
- Cannot be extended in code
If it helps, you can think of "game objects" as being called "scene objects" instead.
If you are more familiar with ECS terminology, a GameObject is akin to an "entity": it is an empty shell that doesn't do anything by itself, but can "group" multiple components together.
The only time a GameObject can live in the project instead of the scene is when it gets turned into a prefab. In that case, the prefab asset in your project folder is also of type GameObject. In this case, people usually just call it a Prefab to avoid ambiguity.
⚙ Component / MonoBehaviour
A Component (UnityEngine.Component) is an object that:
- Is always attached to a GameObject
- Therefore also lives within the Scene
- Receives event from Unity (like
Start,Update,OnCollisionEnter, etc.) - Defines logic and data for actually doing something
- Can be extended in code (by inheriting from
UnityEngine.MonoBehaviour)
Components are the bread and butter when creating logic for your project, because they're the part of the scene that you can author with code. They're the only thing that receive events from Unity.
In other words, if you want anything custom to happen within your scenes, you should be coding classes that derive from MonoBehaviour.
It is somewhat confusing for beginners that the class to extend for components is called MonoBehaviour. It is called like that for historical reasons, when Unity supported (or intended to support) other runtimes than Mono. If you go up the hierarchy chain, you'll see that MonoBehaviour derives from Behaviour, and Behaviour derives from Component.
📄 ScriptableObject (Asset)
A ScriptableObject (UnityEngine.ScriptableObject) is an object that:
- Lives within the project folder
- Therefore does not belong to any scene
- Usually contains data and can even define some methods
- Does not receive events from Unity (like
StartorOnCollisionEnter)* - Can be extended in code (by inheriting from
UnityEngine.ScriptableObject)
The name ScriptableObject is obtuse and confusing. Instead, it's easier to think of a ScriptableObject as a custom user-defined asset. In other words, extending ScriptableObjects is the way for you to create new types of assets that you can store in your project folder.
It follows that ScriptableObjects usually contain mostly data (for example statistics for your enemies, game configuration values, etc.). They are not commonly used for logic, because they don't receive events from Unity.*
OnEnable, OnDisable, and OnDestroy, however these are highly unreliable and, in my experience, should not be used.⏺ UnityEngine.Object
UnityEngine.Object is the absolute base class in Unity. GameObjects, Components and ScriptableObjects are all derived from UnityEngine.Object.
On the technical side of things, UnityEngine.Object is the "C#-side" wrapper of a native C++ object that lives in the C++ part of the engine. Each one has a pointer (IntPtr) pointing to its "equivalent" in the native core of Unity.
Ambiguity with System.Object
UnityEngine.Object is not to be confused with System.Object, which is the absolute base class in C# as a whole. UnityEngine.Object does derive from System.Object, like everything else in C#.
This can quickly become confusing both when reading your code and when the compiler tries to parse it. You may run into this issue:
using System;
using UnityEngine;
public class Test
{
public Object myObject; // Error: "Object" is ambiguous; which do you mean?
}UnityObject alias
To resolve this ambiguity, in all my projects, I've come to rename Object to UnityObject when discussing with teammates and in my code with an alias. This makes it very clear that you're dealing with a Unity thing and not any plain old C# object:
using System;
using UnityEngine;
using UnityObject = UnityEngine.Object; // Add this line to alias
public class Test
{
public UnityObject myObject; // Solved, no longer ambiguous!
}