Class Hierarchy *************** .. hint:: .. image:: gameobj.png :scale: 70% :align: center Object ====== All core Unity classes inherit from the Object class: .. code-block:: text Object ├── GameObject ├── Component │ ├── Transform │ └── Behaviour │ ├── MonoBehaviour │ │ ├── YourScript_1 │ │ └── YourScript_2 │ ├── Renderer │ ├── Collider │ ├── Rigidbody │ ├── Camera │ ├── Light │ └── AudioSource ├── Material ├── Texture └── AudioClip If you look at the `documentation `_, you notice that this class provides some important elements: .. list-table:: :header-rows: 1 * - Category - Member - Description * - Property - string **name** - Returns the name of the object * - Public Method - int **GetInstanceID()** - Returns a unique integer identifying the object * - Static Method - void **Destroy(Object obj)** - Destroys the specified object * - Static Method - Object **Instantiate(Object obj)** - Creates and returns a copy of the specified object Since Unity scripts inherit from MonoBehaviour which inherits from *Object*, you can call *Destroy* directly in the code, like a regular method: .. code-block:: csharp using UnityEngine; public class MyScript : MonoBehaviour { void Start() {} void Update() { Destroy(...); } } The *Destroy* method can destroy any Unity object, since all Unity objects inherit from the Object class. Object modeling =============== Object-oriented programming offers two main ways to build a complex class: inheritance or aggregation. - **Inheritance** creates a specialized class from an existing base class. For example, to create a Wizard class, you derive it from a Human class and add new methods for magical abilities. - **Aggregation** assembles multiple objects to form a more complete one. To create a Wizard, inside the Human class, you add a list of talents and abilities where you insert all the powers a magician can perform. The inheritance way, especially in video games, is very limiting: - Each variation of the Wizard (Paladin, Necromancer, etc.) requires creating a new class - Classes are defined at compile time. Adding a new ability during the game, such as potion making, is not allowed - This approach can lead to managing a large number of classes, making the system harder to maintain and extend Thus, in video game development, the aggregation approach has proven to be the most flexible, intuitive, and efficient modeling technique. So, in Unity, you must see a **GameObject as an empty container**. When you want to define a specific GameObject, such as a player, a door, or a car, you add specific **Components** to this GameObject. GameObject ========== Everything that exists in a 2D/3D scene is a GameObject, for example: - a car - a wall - a camera - a character - a spawn point Documentation ------------- Let's have a look at the documentation of the `GameObject class `_. When you scroll down, you will notice a section called : **Inherited Members** where you will find all the methods and properties inherited from the Object class: name, Destroy, Instantiate... Now, let us examine the elements of a GameObject: .. list-table:: :header-rows: 1 * - Category - Member - Description * - Property - string **tag** - Describes the category the GameObject belongs to (Enemy, Ground, Spawner, etc.) * - Property - Transform **transform** - Reference to the Transform component attached to the GameObject (read-only) * - Property - bool **activeSelf** - Indicates whether the GameObject is active (true) or inactive (false) (read-only) * - Public Method - void **SetActive(bool value)** - Activates or deactivates the GameObject * - Public Method - Component **AddComponent(Type componentType)** - Instantiate and adds a component of the specified type to the GameObject * - Public Method - T **GetComponent()** - Retrieves the first component of type T attached to the GameObject Most of the time, a gameobject has only one component of each type: a single Rigidbody, a single MeshRenderer, or a single Collider. Therefore, the method *GetComponent()* allows us to find the component you need easily. .. list-table:: :header-rows: 1 * - Category - Member - Description * - Static Method - GameObject FindWithTag(string tag) - Retrieves the first active GameObject tagged with the specified tag * - Static Method - GameObject Find(string name) - Finds and returns a GameObject with the specified name As the official documentation said: *GameObject.Find* causes significant performance degradation at scale and is not recommended for performance-critical code, especially in the script/Update function. *Find* searches the entire scene, the search is linear, checking each GameObject one by one traversing the hierarchy. The result is not cached and every call performs the full search again. The more GameObjects you have and the more frequently you call *GameObject.Find*, the greater the impact on your application's performance. Instead, cache the result in a member variable at startup, or use GameObject.FindWithTag. Ranking of methods from fastest to slowest: .. list-table:: :header-rows: 1 * - Method - Performance - Notes * - Direct reference ([SerializeField]) - ⭐⭐⭐⭐⭐ - Fastest method. No search. Reference assigned directly in the Inspector. * - GetComponent - ⭐⭐⭐⭐ - Very fast. Searches only within the same GameObject. * - GameObject.FindWithTag - ⭐⭐⭐ - Moderate performance cost because your game generally uses a limited number of tags * - GameObject.Find - ⭐ - Slowest. Searches through all active GameObjects. Should be avoided in performance-critical code. Component ========= Introduction ------------ A GameObject contains Components, which define how it looks and behaves. Examples of Components: - Transform : position, rotation, scale - MeshRenderer : renders the mesh using a Material and a Shader - MeshFilter : stores the mesh data (geometry: vertices, triangles, normals, UVs) - Collider : collision detection - Rigidbody : physics simulation - Script (MonoBehaviour) : custom logic - Animator : controls animations using an Animator Controller - CharacterController : specialized Component for character movement - Light : illuminates the scene .. warning:: Any GameObject contains **exactly one** mandatory Component: the Transform. Example of a typical NPC GameObject: .. code-block:: text GameObject NPC ├── Transform (position) ├── MeshRenderer (appearance) ├── Scripts (AI, health, etc.) ├── AudioSource (footsteps, voice) └── CharacterController (movement & collision for characters) Other examples: - Player: NPC components, Script (PlayerController: keyboard/joystick input), Camera, AudioListener - House: Transform, MeshFilter (geometry), MeshRenderer (Materials and Shaders), Collider - Spawner: Transform, Script (Spawner: contains the logic to instantiate enemies), invisible entity - Light: Transform, Light (illuminates the scene) - Projectile (bullet, arrow, etc.): Transform, MeshFilter, MeshRenderer, Collider, Rigidbody, Script (damage logic) Documentation ------------- Lets have a look at the documentation of the `Component class `_. When you scroll down, you will notice a section called : **Inherited Members** where you will find all the methods and properties inherited from the Object class: name, Destroy, Instantiate... Now, let us examine the elements of a Component: .. list-table:: :header-rows: 1 * - Category - Member - Description * - Property - gameObject - reference the game object this component is attached to * - Property - transform - reference the transform component of the associated gameobject * - Property - tag - reference the tag of the associated gameobject A **syntactic sugar** is syntax designed to make code easier to read. It makes the language "sweeter" by allowing things to be expressed more concisely. The drawback is that developers might become confused. For example, the *tag* property is not the tag of the component, it is a reference to the *tag* property of the gameobject the component is attached to. Beware of confusion. An even more subtle point, when you write: .. code-block:: csharp *GetComponent()* This syntax is a shorcut for calling *gameObject.GetComponent()*. Do not get confused: a Component does NOT contain other components. When you call *GetComponent()*, the request is performed on the the gameobject. The Transform component ======================= Introduction ------------ This component: - is automatically added when the GameObject is created - cannot be removed - defines the position, rotation, and scale of the object Since the Transform component is always present, it can be accessed directly using the *transform* reference defined everywhere. This avoids the need to write *gameObject.GetComponent()*. Documentation ------------- Lets have a look at the documentation of the `Transform class `_. When you scroll down, you will notice a section called : **Inherited Members** where you will find all the methods and properties inherited from the Object class: name, Destroy, Instantiate... Scene Hierarchy --------------- The Transform component defines the position, rotation, and scale of a GameObject. But this component is special because it also describes the scene hierarchy : .. list-table:: :header-rows: 1 * - Category - Member - Description * - Property - Transform **parent** - Reference to the parent Transform of this Transform. * - Property - int **childCount** - The number of children this Transform has. * - Public Method - Transform **GetChild(int index)** - Returns the child Transform at the specified index. Thus, these properties enable traversal of the entire scene hierarchy. This may seem surprising, since one might initially assume that the hierarchy involves GameObjects, whereas it is actually based on Transforms. This is not an issue, since each Transform in the hierarchy maintains a reference to its associated GameObject. Behaviour ========= .. code-block:: text Object └── Component ├── Transform └── Behaviour ├── MonoBehaviour ├── Renderer │ ├── MeshRenderer │ └── SkinnedMeshRenderer ├── Collider │ ├── BoxCollider │ ├── SphereCollider │ └── CapsuleCollider ├── Rigidbody ├── Camera ├── Light └── AudioSource The Behaviour class adds the **enabled** property, allowing components to be enabled or disabled. Most Unity components inherit from Behaviour, such as MonoBehaviour, Renderer, and Collider. The Transform component is the only exception and cannot be disabled. Scripts ======= A script is a class that derives from MonoBehaviour. MonoBehaviour inherits from Behaviour, which in turn inherits from Component. In Unity, all gameplay logic is implemented through scripts. Message methods --------------- The *MonoBehaviour* class introduces two main message methods: *Start()* and *Update()*. But beware, these methods are NOT virtual methods inherited from MonoBehaviour. In fact, their declaration do not use the override keyword. In Unity, *Start()* and *Update()* are **Unity message methods**. The term *message method* is not linked to multiple threads execution, or message queue or asynchronous processing. Instead: - Update() is always called on the main thread - Update() is executed synchronously The key point is that the majority of MonoBehaviour instances do not implement Update(). In a typical Unity project: - only about **5% to 20%** of MonoBehaviour instances define *Update()* - the remaining 80% to 95% do not require per-frame execution (trees, buildings, or props) So, Unity looks for all the MonoBehaviour scripts that implement an *Update()* function and puts them in a list. This way, when an *Update* sequence starts, the internal logic looks like this : .. code-block:: csharp foreach script in the Script_with_Update_List: call script.Update() Thus using **message methods**, Unity only performs calls on the objects that require them. Convenient method ----------------- MonoBehaviour inherits from Component and shares the same convenient properties and methods: .. list-table:: Component shortcuts and ownership :header-rows: 1 * - Code - Actual meaning * - *gameObject* - reference the gameObject this script belongs * - *transform* - reference *gameObject.transform* * - *tag* - reference *gameObject.tag* * - *GetComponent()* - equivalent to *gameObject.GetComponent()* The following statements are strictly equivalent: .. code-block:: csharp Rigidbody rb = GetComponent(); <=> Rigidbody rb = gameObject.GetComponent(); Destroy ------- Inside a Unity script, if you write: .. code-block:: csharp Destroy(this); the *Destroy* method is called on the current object, which corresponds to the script component itself. As a result, the script component is removed from the current GameObject. The GameObject and its other components are not destroyed. If you want to destroy the entire GameObject, you must write: .. code-block:: csharp Destroy(gameObject); .. note:: The *Destroy* method does not destroy the object immediately. Instead, Unity marks it for destruction and removes it at the end of the current frame.