I pool di oggetti ti aiutano a ridurre il ritardo nei giochi intensivi per le risorse

Shooter e giochi che usano i sistemi di particelle devono creare, manipolare e quindi rimuovere molti oggetti contemporaneamente - forse anche centinaia per frame. Questo può portare al ritardo del gioco o persino al congelamento. In questo tutorial, vedremo come pool di oggetti può aiutare con questo problema, permettendoci di riutilizzare gli oggetti invece di ricrearli da zero.

Nota: Sebbene questo tutorial sia scritto usando Java, dovresti essere in grado di utilizzare le stesse tecniche e concetti in quasi tutti gli ambienti di sviluppo di giochi.


introduzione

Un evento comune negli sparatutto e nei sistemi di particelle è quello di creare ed eliminare molti oggetti in rapida successione. Dalle pistole a lanciare incantesimi magici, creare e cancellare gli oggetti necessari può essere un'operazione costosa. Quando molte di queste operazioni vengono eseguite rapidamente, può causare il ritardo o il blocco del gioco.

Il motivo è che un programma in background chiamato il netturbino richiede risorse di sistema per ripulire la memoria utilizzata. È questa pulizia che può causare il ritardo del sistema.

Un modo per evitare ciò è utilizzare un pool di oggetti per riutilizzare vecchi oggetti invece di eliminarli.


Che cos'è la raccolta dei rifiuti?

Per capire perché è necessario un pool di oggetti, dobbiamo prima capire come funziona la garbage collection.

Garbage Collection è il processo di gestione automatica delle risorse. È responsabile della liberazione dello spazio di memoria per il riutilizzo quando non è più necessario.

Dai un'occhiata al seguente esempio di un semplice per ciclo continuo:

 per (int i = 1; i < 10; i++)  System.out.println(i); 

Quando un programma esegue questo ciclo, creerà una variabile io assegnando sufficiente memoria per contenere i dati della variabile (in questo caso, spazio sufficiente per contenere un intero). Al termine del ciclo, la variabile io non è più necessario; il programma alla fine lo rileverà e potrà quindi liberare la memoria per altri usi.

Per la maggior parte, la raccolta dei rifiuti avviene automaticamente e senza preavviso. A volte, tuttavia, se è necessario liberare molta memoria contemporaneamente, la garbage collection può forzare il programma a utilizzare preziose risorse di sistema per liberare la memoria necessaria. Ciò può causare il ritardo o il blocco del programma temporaneamente, poiché ciò richiede tempo.

Il sistema può anche essere in ritardo quando è necessaria molta memoria contemporaneamente per creare nuovi oggetti. Questo perché assegnare memoria può essere altrettanto dispendioso in termini di risorse quanto liberarlo.

Per questo motivo diventa importante gestire la garbage collection in modo che non interferisca con il programma.


Cos'è un pool di oggetti?

Un pool di oggetti è una struttura di dati che riutilizza oggetti vecchi in modo da non creare o eliminare continuamente nuovi. Invece di assegnare nuova memoria per un oggetto e poi liberarlo una volta che abbiamo finito, continuiamo a riutilizzare l'oggetto più e più volte alterandone i valori. In questo modo, la memoria non deve essere liberata, evitando così la garbage collection.

Il pooling di risorse può offrire un significativo aumento delle prestazioni in situazioni in cui il costo di inizializzazione di un'istanza di classe è elevato, il tasso di istanziazione di una classe è elevato e il numero di istanze in uso in qualsiasi momento è basso.

Pensa ad esso come un mazzo di carte in cui il mazzo rappresenta la memoria. Ogni volta che hai bisogno di una nuova carta (cioè hai bisogno di un nuovo oggetto), ne estrai una dal mazzo e la usi; una volta che hai finito con la carta, la butti in un piccolo bidone della spazzatura.

Alla fine, si esauriranno le carte e sarà necessario un nuovo mazzo, altrimenti il ​​bidone della spazzatura si riempirebbe e sarà necessario estrarlo (ad esempio la garbage collection). In entrambi i casi, devi interrompere ciò che stai facendo per ottenere un nuovo mazzo o eliminare la spazzatura.

In un pool di oggetti, ogni carta nel mazzo è vuota. Quando hai bisogno di una carta, scrivi le informazioni di base su di essa di cui hai bisogno e usalo. Una volta che hai finito con la carta, cancelli le informazioni e rimettile nel mazzo. In questo modo, non sono necessarie nuove carte e non devi mai gettare una carta nel bidone della spazzatura. È il modo di riciclare del programmatore!


Implementazione di un pool di oggetti

L'implementazione di un pool di oggetti non è troppo difficile, ma poiché richiede un oggetto su cui agire, ti mostrerò anche come implementare l'oggetto che il pool di oggetti manterrà. Poiché un pool di oggetti funziona meglio su oggetti che devono essere creati ed eliminati rapidamente, un sistema di particelle sembra la scelta ideale. È un due per uno speciale!

Per prima cosa inizieremo implementando il particella classe. Il seguente codice è scritto in Java, ma le stesse tecniche possono essere utilizzate per la maggior parte degli altri linguaggi di programmazione. Commenterò dopo ogni snippet di codice.

 public class Particle private int framesLeft; int int privato; int privato; int privato xVelocity; privato int yVelocity; / ** * Costruttore * / public Particle () framesLeft = 0; posX = 0; posY = 0; xVelocity = 0; yVelocity = 0;  / ** * Inizializza tutte le variabili prima dell'uso * / public void init (int pFramesLeft, int pPosX, int pPosY, int pXVelocity, int pYVelocity) framesLeft = pFramesLeft; posX = pPosX; posY = pPosY; xVelocity = pXVelocity; yVelocity = pYVelocity;  / ** * Anima la particella * / public booleano animate () if (isAlive ()) posX + = xVelocity; posY + = yVelocity; framesLeft--; // Disegna l'oggetto sullo schermo return false;  return true;  / ** * Determina se una particella è viva (o in uso) * / public booleano isAlive () return framesLeft> 0; 

Come puoi vedere, una particella è un oggetto molto semplice. Contiene alcune variabili per tenere traccia di dove si trova sullo schermo (posX e posY), quanto velocemente sta andando (xVelocity e yVelocity) e quanti fotogrammi deve essere disegnato (framesLeft). Una particella è "viva" se ha ancora fotogrammi da disegnare e "morta" altrimenti.

Ad ogni fotogramma, il animare la funzione viene richiamata per aggiornare la posizione della particella e disegnarla sullo schermo. Ritorna falso se la particella è ancora viva, altrimenti ritorna vera indicando la particella morto.

Nota: Il codice per disegnare la particella va oltre lo scopo di questo tutorial.

Successivamente, implementeremo il ObjectPool classe:

 ObjectPool di classe pubblica dimensione int privata; particelle di elenchi private; / ** * Costruttore * / public ObjectPool (int pSize) size = pSize; particles = new ArrayList (); // Inizializza la matrice con particelle per (int i = 0; i < size; i++)  particles.add(new Particle());   /** * Get the first available particle and assign it the new values */ public void get(int pFramesLeft, int pPosX, int pPosY, int pXVelocity, int pYVelocity)  if (!particles.get(size-1).isAlive())  particles.get(size-1).init(pFramesLeft, pPosX, pPosY, pXVelocity, pYVelocity); particles.add(0, particles.remove(size-1));   /** * Animate the object pool. Any dead particles will be placed at the front of the list to be reused */ public void animate()  for (int i = 0; i < size; i++)  if (particles.get(i).animate())  particles.add(size-1, particles.remove(i));    

Il ObjectPool la classe è anche un oggetto relativamente semplice. Tiene solo un elenco di particelle e la dimensione della lista. La potenza del pool di oggetti ha i suoi due metodi, ottenere e animare.

Quando il pool di oggetti ha bisogno di ottenere un nuovo oggetto per l'uso, guarda l'ultimo elemento nell'elenco e controlla se è attualmente vivo o morto. Se è vivo, il pool è pieno e quindi è necessario creare un nuovo oggetto; se è morto, il pool inizializza l'ultimo elemento nell'elenco, lo apre alla fine e lo riporta in cima all'elenco. In questo modo, il pool ha sempre oggetti disponibili sul retro e oggetti usati nella parte anteriore.

Nel animare metodo, se restituisce la funzione animata della particella vero, l'oggetto è pronto per essere riutilizzato. Il pool rimuove l'elemento dall'elenco e lo spinge sul retro. Manipolare l'elenco in questo modo rende costante e molto efficiente la creazione e la distruzione di oggetti nel pool.

In questo esempio, gli oggetti che il pool di oggetti manterrà sono Particles, ma per il proprio pool di oggetti può essere qualsiasi cosa tu voglia. Finché esistono le stesse funzioni nell'oggetto che userai come nella classe Particle, funzionerà allo stesso modo.


Mettere tutto insieme

Dotato di un pool di oggetti, è ora di creare un sistema di particelle per creare un effetto sparkler.

Iniziamo creando il pool di oggetti per contenere tutte le particelle sullo schermo.

 ObjectPool pool = new ObjectPool (100);

Ad ogni fotogramma, genereremo una nuova particella al centro dello schermo e assegneremo una velocità casuale. Infine, animeremo il pool di oggetti.

 Random randomGenerator = new Random (); int velX = randomGenerator.nextInt (5); int velY = randomGenerator.nextInt (5); pool.get (30, 200, 200, velX, velY); pool.animate ();

Conclusione

La creazione e l'eliminazione rapida di oggetti può potenzialmente causare il rallentamento o il blocco di un gioco. Utilizzando un pool di oggetti, è possibile salvare sia le risorse di sistema che la frustrazione dell'utente.