Unity instantiate

Instantiation (Instantiate) dans Unity

Dans le monde de Unity, la fonction Instantiate se présente comme un outil indispensable, nous permettant de copier un gameObject ou divers objets Unity sur notre scène active.

La méthode Instantiate est statique et émane de la classe UnityEngine.Object.

On peut voir cette méthode comme une duplication; prenant, comme paramètre principal, un objet Unity (une classe héritant de UnityEngine.Object), le dupliquant, puis l’initialisant dans votre scène.

Table des matières

Unity Instantiate Gameobject

Pour instancier ou cloner un gameObject, nous passons par un script, en utilisant la fonction statique « Instantiate« .

Cette fonction renvoie le clone instancié.

Utilisons un exemple simple où nous invoquons un gameObject déjà présent dans notre scène depuis un MonoBehaviour :

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    public GameObject referenceGameObject;

    private void Start()
    {
        GameObject instance = Instantiate(referenceGameObject);
        // Actions avec l'instance
    }
}
    

Info : Étant donné que notre classe est un MonoBehaviour héritant de Object, nous pouvons utiliser directement la méthode statique sans le préfixe.

Nous utilisons l’Inspecteur pour définir notre gameObject par référence : Unity inspector reference

Comme on peut le voir dans l’image ci-dessus, une copie de notre gameObject a été créée.

Il est à noter qu’un suffixe « (Clone) » est ajouté par défaut.
Vous pouvez modifier directement le nom d’un gameObject via le code en utilisant la propriété ‘name‘.

Compréhension de la méthode Instantiate

Il y a plusieurs choses intéressantes à propos de la fonction « Instantiate« , tout d’abord l’objet dupliqué est détaché de l’objet de référence.

Toutes les modifications apportées à la copie ne se répercutent pas sur l’objet de référence.

Instantiate peut être utilisé en dehors d’une classe Unity ; son application n’est pas limitée.

using UnityEngine;

public class Invoker
{
    public void Invoke(GameObject gameObject)
    {
        Object.Instantiate(gameObject);
    }
}
    

Il est crucial de souligner qu’Instantiate fonctionne parfaitement avec n’importe quel élément héritant de Object.

Lorsque vous passez un MonoBehaviour en tant que paramètre, l’élément retourné sera également un Monobehaviour, éliminant ainsi le besoin de conversion ou de GetComponent.

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    // Player est un MonoBehaviour
    public Player player;

    private void Start()
    {
        Player instance = Instantiate(player);
        // Actions avec l'instance
    }
}
    

Il convient de noter l’ajout, depuis la version 2022.3.11 d’Unity, d’une nouvelle méthode spécialement conçue pour les GameObjects.
Pour en savoir plus, consultez mon article dédié à InstantiateGameObjects.

Instancier en tant qu’enfant

Il est possible d’instancier un gameObject en tant qu’enfant (ajout d’un parent) en utilisant la méthode Instantiate, en associant un deuxième paramètre, un Transform, indiquant le parent.

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    public GameObject referenceGameObject;

    private void Start()
    {
        // L'instance devient l'enfant de notre MonoBehaviour
        GameObject instance = Instantiate(referenceGameObject, transform);
    }
}
    

unity gameObject parent

On peut ajouter un troisième paramètre après notre transform ; il s’agit d’un booléen, « instantiateInWorldSpace« .
Par défaut, sa valeur est false.

Lorsqu’un parent est ajouté, la position, l’échelle et la rotation du nouvel objet deviennent relatives au parent.

Pour positionner votre gameObject par rapport à l’espace mondial (y compris la rotation et l’échelle), vous devez indiquer true.

GameObject instance = Instantiate(referenceGameObject, transform, true);
    

Exemple avec des positions :

instantiateInWorldSpaceWorld position référence GameObjectWorld position parentWorld position duplicatePosition locale duplicate
falseVector3(1, 0, 0)Vector3(5, 5, 5)Vector3(6, 5, 5)Vector3(1, 0, 0)
trueVector3(1, 0, 0)Vector3(5, 5, 5)Vector3(1, 0, 0)Vector3(-4, -5, -5)

Il est essentiel de noter que lorsque « instantiateInWorldSpace » est true, la position mondiale du duplicate est identique à celle de l’objet de référence.
Avec false, elle change et devient relative à celle du parent.

Cela s’applique également à l’échelle et à la rotation.

Instancier à une position et une rotation

Vous avez la possibilité de modifier directement la position et la rotation de vos GameObjects instanciés en associant des paramètres supplémentaires à la fonction « Instantiate« .

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    public GameObject referenceGameObject;

    private void Start()
    {
        // Instancier à Vector3(5, 0, 0) et sans rotation
        GameObject instance = Instantiate(referenceGameObject, Vector3.right * 5f, Quaternion.identity);
    }
}
    

Si vous souhaitez ne pas avoir de rotation, vous pouvez associer « Quaternion.identity » avec le troisième paramètre.

Vous pouvez également associer directement un parent tout en spécifiant la position et la rotation en ajoutant un dernier, quatrième paramètre de type Transform.

Notez que les valeurs ajoutées restent en coordonnées mondiales (world) même avec un parent spécifié.

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    public GameObject referenceGameObject;

    private void Start()
    {
        // Instancier à Vector3(5, 0, 0) et sans rotation
        GameObject instance = Instantiate(referenceGameObject, Vector3.right * 5f, Quaternion.identity, transform);
    }
}
    

Unity Instancier Prefab

Qu’est-ce qu’un prefab ?

Le système Prefab Unity vous permet de créer, configurer et enregistrer un GameObject avec tous ses composants, valeurs de propriétés ainsi que ses GameObjects enfants, formant alors un asset réutilisable.

L’asset Prefab sert de modèle à partir duquel vous pouvez générer de nouvelles instances dans la scène.

Unity prefab Hierarchy

Sur Unity, les prefabs sont représentés avec une couleur bleuâtre sur le cube et l’écriture.

Vous pouvez créer un prefab en faisant glisser et déposer un GameObject de votre scène dans un dossier de votre projet.

Unity create prefab

Sur Unity, pour instancier un prefab, nous nous appuyons toujours sur un script C#. Instancier notre prefab est identique à instancier un GameObject déjà présent dans notre scène.

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    public GameObject referencePrefab;

    private void Start()
    {
        GameObject instance = Instantiate(referencePrefab);
    }
}
    

Instancier un Prefab dans l’Éditeur en préservant les connexions

Pendant une instantiation standard d’un prefab, vous avez peut-être remarqué que le lien entre le prefab et l’objet dupliqué est rompu.

Unity prefab no linkNous avons perdu la couleur bleuâtre

En utilisant la méthode « PrefabUtility.InstantiatePrefab« , la connexion entre le prefab et l’objet dupliqué est préservée.

Cela reflète le comportement lorsque vous faites glisser et déposez votre prefab dans la scène.

using UnityEngine;
using UnityEditor;

public class EditorHelper
{
    [MenuItem("Window/Instantiate")]
    private static void InstantiatePrefab()
    {
        // Chargez le prefab hello depuis Resources
        GameObject gameObject = Resources.Load<GameObject>("hello");
        Object instance = PrefabUtility.InstantiatePrefab(gameObject);
    }
}
    

Instancier un Scriptable Object

Revenons sur ce qu’est un ScriptableObject.

Un ScriptableObject sert de conteneur de données, capable non seulement de stocker des quantités importantes de données, mais aussi de fonctionner comme intermédiaire (très pratique pour les events)…

Il permet le stockage de quantités importantes de données indépendamment des instances de classe.

Unity scriptableObject in project

Un des cas d’utilisation principaux des ScriptableObjects est de réduire l’utilisation de la mémoire dans votre projet en évitant la duplication de valeurs.

Pour instancier (ou dans ce cas, dupliquer) un ScriptableObject, la méthode Instantiate peut être utilisée.
Cependant, si vous souhaitez créer un ScriptableObject vide, « ScriptableObject.CreateInstance » peut être utilisé.

Pour une utilisation dans l’environnement de l’éditeur, il doit être associé à « AssetDatabase.CreateAsset » pour créer l’asset dans votre projet.

using UnityEngine;
using UnityEditor;

public class EditorHelper
{
    [MenuItem("Window/Scriptable")]
    private static void CreateScriptable()
    {
        // Data est un ScriptableObject
        Data data = ScriptableObject.CreateInstance<Data>();

        AssetDatabase.CreateAsset(data, "Assets/data.asset");
        // Écrit tous les changements d'actifs non enregistrés sur le disque.
        AssetDatabase.SaveAssets();
    }
}
    

Cette méthode peut également être utilisée en temps réel. Cependant, personnellement, je trouve son utilisation assez situationnelle.

La possibilité de modifier les données directement dans l’inspecteur peut être séduisante, seulement dans de nombreux cas, une classe ou une structure C# vous permet d’obtenir le même résultat avec de meilleures performances.

Instancier avec plusieurs scènes

Dans un contexte multi-scène, où vous avez une scène active (principale) ainsi qu’une ou plusieurs scènes additives, l’appel de la méthode « Instantiate » entraînera toujours le clonage du gameObject dans la scène active, quel que soit l’origine de l’appel.

Unity instancier plusieurs scènes

Vous pouvez définir votre scène active via l’éditeur en faisant un clic droit sur votre scène dans la hiérarchie des scènes et en sélectionnant « Set Active Scene.« 

Unity setActiveScene

L’activation en temps réel est également possible en utilisant la méthode « SceneManager.SetActiveScene« .

De plus, il est possible de déplacer un gameObject d’une scène à une autre grâce à la méthode statique « SceneManager.MoveGameObjectToScene.« 

using UnityEngine;
using UnityEngine.SceneManagement;

public class InvokeBehaviour : MonoBehaviour
{
    public void MoveScene()
    {
        SceneManager.MoveGameObjectToScene(gameObject, SceneManager.GetSceneByName("scene1"));
    }
}
  

De plus, à partir de Unity 2022.3.11, la méthode « SceneManager.MoveGameObjectsToScene » permet le déplacement de plusieurs gameObjects en utilisant leurs instanceIds.

Instancier un GameObject vide

Pour instancier un objet vide, il n’est pas nécessaire d’utiliser la fonction « Instantiate« .

À la place, vous pouvez créer une instance de la classe GameObject dans un script.

Vous pouvez associer directement un nom en utilisant le premier paramètre du constructeur.

Pour ajouter des composants pendant la création du gameObject :

  • Vous pouvez ajouter un deuxième paramètre contenant un tableau avec tous les types de composants que vous souhaitez ajouter.
  • Alternativement, vous pouvez ajouter les types de composants un par un, chacun dans un paramètre séparé.
private void EmptyGameObject()
{
    // GameObject vide avec defaultName
    var gameObjectWithoutName = new GameObject();

    // GameObject vide avec un nom personnalisé
    var gameObjectWithName = new GameObject("Nom du GameObject");

    // GameObject vide créé avec un tableau de types de composants
    var gameObjectWithComponent = new GameObject("Nom du GameObject", new System.Type[] { typeof(BoxCollider2D), typeof(Rigidbody2D) });

    // Alternative, passer les paramètres de type de composant un par un
    var alternativeGameObjectWithComponent = new GameObject("Nom du GameObject", typeof(BoxCollider2D), typeof(Rigidbody2D));
}
  

Il est également possible d’ajouter un GameObject vide via l’éditeur : faites un clic droit dans la hiérarchie de la scène et cliquez sur « Create Empty. »

Unity create empty

Vous pouvez également utiliser le raccourci « Ctrl+Shift+N. » Pour faire du gameObject un enfant de l’objet actuellement sélectionné, utilisez « Alt+Shift+N.« 

Instancier un cube et autres primitives

Pour instancier un cube ou toute autre primitive, nous utilisons la méthode statique GameObject.CreatePrimitive.

Comme premier argument, nous passons le PrimitiveType désiré.

Unity propose six types :

  • Sphere
  • Capsule
  • Cylindre
  • Cube
  • Plane
  • Quad
private void CreatePrimitive()
{
    GameObject cube = GameObject.CreatePrimitive(PrimitiveType.Cube);
}
  

Une note importante de la documentation (traduit) :

CreatePrimitive peut échouer à l’exécution si votre projet ne fait pas référence aux composants suivants : MeshFilter, MeshRenderer, et BoxCollider ou SphereCollider à l’exécution.

La meilleure façon d’éviter ce crash est de déclarer des propriétés privées de ces types. Le système de stripping reconnaîtra leur utilisation, les inclura dans la build, donc il ne supprimera pas ces composants.

Vous pouvez également créer un cube directement dans l’éditeur Unity : faites un clic droit dans la hiérarchie de la scène -> « 3D Object » -> « Cube.« 

Unity create cube

Instancier avec des paramètres

Si vous souhaitez instancier et appliquer des paramètres, comme avec AddComponent, il n’est pas possible de le faire directement.

Il est préférable de créer une fonction séparée, par exemple ‘SetParameters’, à appeler après la création du composant.

using UnityEngine;

public class TorsoEquip : MonoBehaviour
{
    public int Defense { get; private set; }
    public int Resilience { get; private set; }

    public void SetParameters(int defense, int resilience)
    {
        Defense = defense;
        Resilience = resilience;
    }
}

using UnityEngine;

public class InvokeBehaviour : MonoBehaviour
{
    public TorsoEquip prefabTorsoEquip;

    private void InvokeWithParameters()
    {
        var torsoEquip = Instantiate(prefabTorsoEquip);
        torsoEquip.SetParameters(20, 30);
    }
}  

Pour rendre le code plus réutilisable, il peut être plus judicieux de passer un objet de configuration au lieu de passer des paramètres un par un.

Cet objet de configuration peut être une classe, une structure ou même un ScriptableObject, selon vos besoins :

using UnityEngine;

public class TorsoEquip : MonoBehaviour
{
    public class EquipmentStat
    {
        public int Defense { get; set; }
        public int Resilience { get; set; }
    }

    public EquipmentStat EquipmentConfiguration { get; private set; }

    public void AddConfiguration(EquipmentStat equipmentConfiguration)
    {
        EquipmentConfiguration = equipmentConfiguration;

        // Maintenant, vous pouvez accéder à
        // EquipmentConfiguration.Defense
        // ou EquipmentConfiguration.Resilience
    }
}
  

Aller plus loin

Lors de l’instanciation de nombreux GameObjects, je vous recommande vivement l’utilisation d’un système de pooling.

Ce système vous permet de réutiliser des GameObjects précédemment créés au lieu d’en créer de nouveaux.

L’instanciation et la destruction de GameObjects peuvent être coûteuses.

Astuce supplémentaire : Dans un contexte d’éditeur, si vous vous retrouvez à instancier des GameObjects, n’oubliez pas d’appeler la méthode EditorUtility.SetDirty sur votre GameObject.

Sinon, la scène ne reconnaîtra pas qu’il y a de nouveaux changements, et peu importe combien de fois vous appuirez sur Ctrl+S, rien ne sera enregistré.

Conclusion

Tout au long de cette exploration, nous avons vu divers aspects de la méthode Instantiate, de son utilisation de base pour dupliquer des gameObjects jusqu’à des scénarios plus avancés tels que l’instanciation de prefabs, de scriptable objects et son comportment avec plusieurs scènes.

Nous avons également découvert des techniques telles que l’instanciation en tant qu’enfant, le réglage des positions et rotations, et la création de GameObjects vides.

Merci d’avoir lu. Bon codage !

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *