ObjectPool de Unity
Fait partie de la section Architecture de jeu dans Unity du Journey Unity de Coddy. Leçon 48 sur 64.
Unity fournit un pool, ObjectPool<T> dans UnityEngine.Pool. Tu lui indiques quoi faire à chaque étape à l’aide de quatre fonctions lambda, et il conserve les objets disponibles ainsi que les compteurs. Cette cheminée produit une bouffée de fumée toutes les 0.2 secondes, et chaque bouffée est remise dans le pool au bout d’une seconde :
using UnityEngine;
using UnityEngine.Pool;
public class Chimney : MonoBehaviour
{
public Puff puffPrefab;
private ObjectPool<Puff> pool;
private float timer;
void Awake()
{
pool = new ObjectPool<Puff>(
createFunc: () => Instantiate(puffPrefab),
actionOnGet: puff => puff.gameObject.SetActive(true),
actionOnRelease: puff => puff.gameObject.SetActive(false),
actionOnDestroy: puff => Destroy(puff.gameObject),
collectionCheck: true,
defaultCapacity: 10,
maxSize: 20);
}
void Update()
{
timer += Time.deltaTime;
if (timer >= 0.2f)
{
timer = 0f;
Puff puff = pool.Get();
puff.transform.position = transform.position + Vector3.up;
puff.Launch(pool);
}
}
}// Puff.cs
private IObjectPool<Puff> pool;
private float age;
public void Launch(IObjectPool<Puff> owner)
{
pool = owner;
age = 0f;
}
void Update()
{
transform.Translate(0f, Time.deltaTime, 0f);
age += Time.deltaTime;
if (age >= 1f) pool.Release(this);
}createFunc s’exécute lorsque Get trouve le pool vide ; actionOnGet s’exécute à chaque Get et actionOnRelease à chaque Release. Lorsqu’une bouffée est remise dans le pool alors que maxSize objets disponibles s’y trouvent déjà, le pool ne la conserve pas : actionOnDestroy la détruit. defaultCapacity correspond uniquement à la capacité initiale de la liste du pool ; aucun objet n’est donc créé à l’avance. CountAll compte les bouffées créées par le pool et qu’il possède encore, CountActive celles qui se trouvent dans le jeu et CountInactive celles qui sont disponibles. Get a activé la bouffée avant que la cheminée ne la place, ce qui convient ici, car Puff n’a pas de méthode OnEnable qui lit sa position. La bouffée conserve le pool sous la forme d’un IObjectPool<Puff>, l’interface implémentée par tous les pools Unity, et Launch réinitialise son âge à chaque utilisation.
Lorsque collectionCheck est activé, remettre dans le pool une bouffée qui s’y trouve déjà déclenche une InvalidOperationException : Trying to release an object that has already been released to the pool. La vérification ne s’exécute que dans l’éditeur, car elle parcourt les objets disponibles à chaque Release ; dans une version compilée, la bouffée se trouverait deux fois dans le pool et deux appels à Get renverraient le même objet. Une cause fréquente est un objet qui continue de s’exécuter après sa remise dans le pool et qui s’y remet une deuxième fois. C’est pourquoi actionOnRelease le désactive.
Défi
MoyenFais éclater les feux d’artifice à partir du pool de Unity.
- Dans
AwakedeLauncher, crée unObjectPool<Spark>: crée avecInstantiate(sparkPrefab), active les étincelles lorsqu’elles sont prises, désactive-les lorsqu’elles sont relâchées, détruis l’objet lorsqu’il est libéré, et définismaxSize: 5. - Dans
Update, prends chaque étincelle avecpool.Get()au lieu deInstantiate. - Dans
Return, appellepool.Release(spark)au lieu deDestroy.
Appuie sur « Jouer » et lance quelques salves. Puis appuie sur « Vérifier ».
Essayez vous-même
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.Pool;
public class Launcher : MonoBehaviour
{
public Spark sparkPrefab;
public int sparks = 8;
void Awake()
{
// Créer le pool : créer, récupérer, libérer et détruire, avec au plus 5 étincelles en réserve
}
void Update()
{
if (Keyboard.current.spaceKey.wasPressedThisFrame)
{
for (int i = 0; i < sparks; i++)
{
Spark spark = Instantiate(sparkPrefab);
spark.transform.position = transform.position + Vector3.up;
spark.Launch(Direction(i), this);
}
}
}
// Une étincelle arrivée en fin de vie
public void Return(Spark spark)
{
Destroy(spark.gameObject);
}
// Vers l’extérieur et vers le haut, réparties uniformément autour du lanceur
Vector3 Direction(int i)
{
return Quaternion.Euler(0f, i * 360f / sparks, 0f) * new Vector3(0f, 1f, 3f);
}
}
Toutes les leçons de Architecture de jeu dans Unity
1Des composants, pas des hiérarchies
De petits composantsUn script de base pour les ennemisComposants abstraitsDes composants qui fonctionnent ensembleRécapitulatif : tourelle modulaireRécapitulatif : canon double4Données sérialisées
Classes sérialisablesListes dans l’InspecteurÉnumérations dans l’InspecteurRécapitulatif : table de butinRécapitulatif : tirer le butin2Les interfaces dans Unity
IDamageableInterfaces et TryGetComponentGetComponentsRécapitulatif : touche d’interactionLe plus proche à portée5ScriptableObjects
Ressources de donnéesStatistiques des armesParamètres partagésSauver le ventLes vagues comme donnéesRécapitulatif : base de données des objetsRécapitulatif : acheter dans la boutique3Événements
Les événements C# dans UnityS’abonner dans OnEnableChamps UnityEventLes événements pilotent l’interfaceRécapitulatif : fenêtre de succèsRécapitulatif : minuteur de la fenêtre6Sauvegarder avec JSON
JsonUtilitySauvegarder la partieCharger et valeurs par défautRécapitulatif : emplacements de sauvegardeChoisir et supprimer un emplacement9Réutilisation des objets
Pourquoi mettre les objets en poolUn pool avec une fileObjectPool de UnityRécapitulatif : pool de projectilesEntraînez-vous par vous-même : Playground Unity