Address
304 North Cardinal St.
Dorchester Center, MA 02124
Work Hours
Monday to Friday: 7AM - 7PM
Weekend: 10AM - 5PM
We use cookies to ensure that we provide you with the best possible experience on our site.
Votre source incontournable sur Unity et le développement web
Votre source incontournable sur Unity et le développement web
Dans la récente version de Unity 2022.3.11, la classe GameObject accueille une nouvelle fonction statique, « InstantiateGameObjects. » Cette fonction facilite l’instanciation d’un nombre défini de GameObjects en utilisant un ID d’instance.
Au moment de la rédaction, cette version a moins d’un mois, et nous allons explorer ensemble si elle surpasse la méthode standard « Instantiate« .
En référence à la documentation :
Contrairement à la fonction « Instantiate« , au lieu de passer l’objet à instancier en paramètre, nous devons passer un ID d’instance (qui doit être un ID d’instance de gameObject). Un autre changement est que cette fonction ne renvoie pas de valeur, donc nous devons passer 2 NativeArray<int> en tant que paramètres :
Nous allons effectuer un test approximatif pour nous donner une indication de sa vitesse. Pour la nouvelle méthode, nous utiliserons le code suivant :
using Unity.Collections;
using UnityEngine;
public GameObject reference;
private NativeArray<int> instanceIds;
private NativeArray<int> transformIds;
private void NewInstantiate()
{
int count = 20000;
instanceIds = new NativeArray<int>(count, Allocator.Persistent);
transformIds = new NativeArray<int>(count, Allocator.Persistent);
GameObject.InstantiateGameObjects(reference.GetInstanceID(), count, instanceIds, transformIds);
}
private void OnDestroy()
{
instanceIds.Dispose();
transformIds.Dispose();
}
Et comparons-le avec la méthode classique « Instantiate » :
using UnityEngine;
using System.Collections.Generic;
public GameObject reference;
private void OldInstantiate()
{
int count = 20000;
private List<Object> _objects = new List<Object>(count);
for (int i = 0; i < count; i++)
{
_objects.Add(GameObject.Instantiate(reference));
}
}
Le gameObject de référence est un Cube avec un MonoBehaviour « Tracker » attaché.
J’ai exécuté ces méthodes 100 fois; voici les résultats :
En moyenne, la méthode « GameObject.InstantiateGameObjects » prend 1781 ms pour s’exécuter, tandis que la méthode classique Instantiate prend 1870 ms. Nous pouvons voir que cette nouvelle approche est légèrement plus rapide.
En effet, cette fonction nous offre un léger coup de pouce en termes de performances. Le seul inconvénient est qu’elle ne renvoie que des IDs d’instance. Que pouvons-nous en faire ?
Pour récupérer un objet à partir d’un ID d’instance, nous pouvons utiliser la méthode « Resources.InstanceIDToObjectList« . De plus, depuis Unity 2022.3.11, cette méthode est devenue ThreadSafe.
int count = 20000;
instanceIds = new NativeArray<int>(count, Allocator.Persistent);
transformIds = new NativeArray<int>(count, Allocator.Persistent);
GameObject.InstantiateGameObjects(reference.GetInstanceID(), count, instanceIds, transformIds);
var objects = new List<Object>(count);
Resources.InstanceIDToObjectList(instanceIds, objects);
Si nous voulons ensuite récupérer notre MonoBehaviour, nous pouvons le caster puis utiliser « GetComponent« :
var trackers = new List<Tracker>(count);
for (int i = 0; i < count; i++)
{
trackers.Add(((GameObject)objects[i]).GetComponent<Tracker>());
}
Cette nouvelle version a également ajouté la fonction « GameObject.SetGameObjectsActive« , nous permettant d’activer ou de désactiver des GameObjects en passant un NativeArray en tant que paramètre :
GameObject.InstantiateGameObjects(reference.GetInstanceID(), count, instanceIds, transformIds);
// activer tous les GameObjects dans instanceIds
GameObject.SetGameObjectsActive(instanceIds, true);
J’ai comparé cette méthode avec la méthode classique « SetActive« , activant 20000 GameObjects :
// objects est une List<GameObject> avec 20000 GameObjects.
for (int i = 0; i < count; i++)
{
objects[i].SetActive(true);
}
Après avoir exécuté ces méthodes 100 fois, j’obtiens un temps d’exécution moyen de 116 ms pour « GameObject.SetGameObjectsActive » et 119 ms pour la méthode classique.
Cette nouvelle version a également introduit SceneManager.MoveGameObjectsToScene, permettant le déplacement de GameObjects vers une autre scène en utilisant un NativeArray<int> :
using UnityEngine.SceneManagement;
using Unity.Collections;
// instanceIds est un NativeArray<int> avec des IDs d'instance
var scene = SceneManager.GetSceneAt(0);
SceneManager.MoveGameObjectsToScene(instanceIds, scene);
TransformAccessArray est une structure utilisée avec un job IJobParallelForTransform. Elle permet d’effectuer la même opération sur tous les transforms (position, rotation et échelle) sur plusieurs threads, offrant de meilleures performances. TransformAccessArray a également été amélioré ; maintenant, nous pouvons passer l’ID d’instance de Transform via sa méthode « Add » :
using UnityEngine.Jobs;
using Unity.Burst;
using UnityEngine.SceneManagement;
using Unity.Collections;
using UnityEngine;
// burst compile pour de meilleures performances
[BurstCompile]
public struct MovementJob : IJobParallelForTransform
{
public float deltaTime;
public void Execute(int index, TransformAccess transformAccess)
{
transformAccess.position = transformAccess.position + Vector3.up * deltaTime;
}
}
public NativeArray<int> instanceIds;
public NativeArray<int> transformIds;
private TransformAccessArray _transformAccess;
private bool _isReady = false;
private void InstantiateAll()
{
int count = 20000;
// initialiser notre TransformAccessArray
_transformAccess = new TransformAccessArray(count);
// Préparer l'instanciation avec InstantiateGameObjects
instanceIds = new NativeArray<int>(count, Allocator.Persistent);
transformIds = new NativeArray<int>(count, Allocator.Persistent);
var scene = SceneManager.GetActiveScene();
GameObject.InstantiateGameObjects(reference.GetInstanceID(), count, instanceIds, transformIds, scene);
// Ajouter l'ID d'instance à TransformAccessArray
for (int i = 0; i < count; i++)
{
_transformAccess.Add(transformIds[i]);
}
_isReady = true;
}
private void Update()
{
// attendre que tout soit instancié
if (!_isReady)
{
return;
}
// créer le job
var job = new MovementJob()
{
deltaTime = Time.deltaTime,
};
job.Schedule(_transformAccess);
}
private void OnDestroy()
{
// n'oubliez pas de disposer
_transformAccess.Dispose();
instanceIds.Dispose();
transformIds.Dispose();
}
Ces nouvelles méthodes offrent de nouvelles possibilités d’utilisation des IDs d’instance. Il est très intéressant de pouvoir instancier une multitude de GameObjects directement et d’effectuer des actions dans un Job sans référence directe. Ces méthodes viennent d’arriver ; nous verrons comment elles évoluent avec le temps.
Merci de m’avoir lu.