Shooters en games die deeltjessystemen gebruiken, moeten veel objecten tegelijkertijd maken, manipuleren en vervolgens verwijderen - misschien zelfs honderden per frame. Dit kan ertoe leiden dat de game achterblijft of zelfs bevriest. In deze zelfstudie bekijken we hoe objectpools kan helpen met dit probleem, door ons toe te staan objecten opnieuw te gebruiken in plaats van ze opnieuw te maken.
Notitie: Hoewel deze tutorial geschreven is met behulp van Java, zou je in bijna elke game-ontwikkelomgeving dezelfde technieken en concepten moeten kunnen gebruiken.
Een veel voorkomend verschijnsel bij shooters en particle-systemen is het snel achter elkaar maken en verwijderen van veel objecten. Van schiethamers tot magische spreuken, het maken en verwijderen van de benodigde objecten kan een dure operatie zijn. Wanneer veel van deze bewerkingen snel worden uitgevoerd, kan het spel vertragen of bevriezen.
De reden is dat een achtergrondprogramma genaamd de vuilnisman vereist systeembronnen om het gebruikte geheugen op te schonen. Door deze opruiming kan het systeem achterlopen.
Een manier om dit te voorkomen is om een objectpool te gebruiken om oude objecten te hergebruiken in plaats van ze te verwijderen.
Om te begrijpen waarom een objectgroep nodig is, moeten we eerst begrijpen hoe garbagecollection werkt.
Garbage collection is het proces van automatisch resource management. Het is verantwoordelijk voor het vrijmaken van geheugenruimte voor hergebruik wanneer het niet langer nodig is.
Bekijk het volgende voorbeeld van een eenvoudig voor
lus:
voor (int i = 1; i < 10; i++) System.out.println(i);
Wanneer een programma deze lus uitvoert, wordt een variabele gemaakt ik
door het toewijzen van voldoende geheugen om de gegevens van de variabele vast te houden (in dit geval voldoende ruimte om een geheel getal te houden). Zodra de lus is voltooid, de variabele ik
is niet langer nodig; het programma zal dit uiteindelijk detecteren en kan dan het geheugen vrijmaken voor ander gebruik.
Grotendeels vindt garbage collection automatisch en zonder kennisgeving plaats. Soms, echter, als veel geheugen in één keer moet worden vrijgegeven, kan garbage collection het programma dwingen waardevolle systeembronnen te gebruiken om het benodigde geheugen vrij te maken. Hierdoor kan het programma tijdelijk vertragen of bevriezen, omdat dit tijd kost.
Het systeem kan ook achterblijven als er in één keer veel geheugen nodig is om nieuwe objecten te maken. Dit komt omdat het toewijzen van geheugen net zo arbeidsintensief kan zijn als het vrijmaken ervan.
Vanwege dit wordt het belangrijk om garbage collection te beheren, zodat het uw programma niet hindert.
Een objectpool is een gegevensstructuur die oude objecten hergebruikt om niet voortdurend nieuwe te maken of te verwijderen. In plaats van nieuw geheugen voor een object toe te wijzen en het vervolgens te bevrijden zodra we klaar zijn, blijven we het object steeds opnieuw gebruiken door de waarden ervan te wijzigen. Op deze manier hoeft het geheugen niet te worden vrijgegeven, waardoor garbage collection wordt voorkomen.
Het poolen van resources kan een aanzienlijke prestatieverbetering bieden in situaties waar de kosten voor het initialiseren van een klasse-instantie hoog zijn, de snelheid waarmee een klasse wordt geconstateerd hoog is en het aantal exemplaren dat op een bepaald moment in gebruik is, laag is.
Zie het als een spel kaarten waarbij het kaartspel het geheugen vertegenwoordigt. Elke keer dat je een nieuwe kaart nodig hebt (dat wil zeggen, je hebt een nieuw object nodig), teken je er een uit het kaartspel en gebruik je het; als je eenmaal klaar bent met de kaart, gooi je hem in een kleine vuilnisbak.
Uiteindelijk zou je geen kaarten meer hebben en een nieuw kaartspel nodig hebben, anders zou de vuilnisbak vol raken en moeten worden verwijderd (dat wil zeggen garbage collection). In beide gevallen moet je stoppen met wat je momenteel doet om een nieuwe stapel te krijgen of de prullenbak te verwijderen.
In een objectgroep is elke kaart in het kaartspel leeg. Als u een kaart nodig heeft, schrijft u de basisinformatie die u nodig hebt en gebruikt u deze. Als je klaar bent met de kaart, wis je de informatie en plaats je deze terug in het kaartspel. Op deze manier zijn geen nieuwe kaarten nodig en hoef je nooit een kaart in de vuilnisbak te gooien. Het is de manier van recyclen van de programmeur!
Het implementeren van een objectgroep is niet zo moeilijk, maar omdat het een object vereist om in actie te komen, laat ik je ook zien hoe je het object dat de objectpool bevat, kunt implementeren. Aangezien een objectpool het beste werkt voor objecten die snel moeten worden gemaakt en verwijderd, lijkt een particle-systeem de meest ideale keuze. Het is een twee voor één special!
We beginnen eerst met het implementeren van de Deeltje
klasse. De volgende code is geschreven in Java, maar dezelfde technieken kunnen voor de meeste andere programmeertalen worden gebruikt. Ik zal reageren na elk codefragment.
public class Particle private int framesLeft; private int posX; privé intitie; private int xVelocity; privé int yVelocity; / ** * Constructor * / public Particle () framesLeft = 0; posX = 0; posY = 0; xVelocity = 0; yVelocity = 0; / ** * Initialiseer alle variabelen vóór gebruik * / public void init (int pFramesLeft, int pPosX, int pPosY, int pXVelocity, int pYVelocity) framesLeft = pFramesLeft; posX = pPosX; posY = pPosY; xVelocity = pXVelocity; yVelocity = pYVelocity; / ** * Animatie van het deeltje * / public Boolean animate () if (isAlive ()) posX + = xVelocity; posY + = yVelocity; framesLeft--; // Teken het object naar het scherm return false; return true; / ** * Bepaal of een deeltje in leven is (of in gebruik is) * / public Boolean isAlive () return framesLeft> 0;
Zoals u kunt zien, is een deeltje een heel eenvoudig object. Het bevat enkele variabelen om bij te houden waar het zich op het scherm bevindt (posx
en ruiker
), hoe snel het gaat (xVelocity
en yVelocity
) en hoeveel frames het moet worden getrokken (framesLeft
). Een deeltje is "levend" als het nog frames heeft die nog moeten worden getekend en anders "dood".
Op elk frame, de bezielen
functie wordt aangeroepen om de positie van het deeltje bij te werken en naar het scherm te tekenen. Het komt terug vals
als het deeltje nog in leven is, anders geeft het de ware betekenis van het deeltje terug ging dood
.
Notitie: De code om het deeltje te tekenen valt buiten het bestek van deze tutorial.
Vervolgens implementeren we de ObjectPool
klasse:
openbare klasse ObjectPool private int size; privélijst deeltjes; / ** * Constructor * / public ObjectPool (int pSize) size = pSize; particles = new ArrayList (); // Initialiseer de array met deeltjes voor (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));
De ObjectPool
klasse is ook een relatief eenvoudig object. Het bevat alleen een lijst met deeltjes en de grootte van de lijst. De kracht van de objectpool komt op twee manieren, krijgen
en bezielen
.
Wanneer de objectpool een nieuw object moet krijgen om te gebruiken, wordt naar het laatste item in de lijst gekeken en wordt gecontroleerd of het object momenteel in leven is of dood. Als het in leven is, is de pool vol en moet er dus een nieuw object worden gemaakt; als deze leeg is, initialiseert de pool het laatste item in de lijst, knalt het vanaf het einde en duwt het terug naar de voorkant van de lijst. Op deze manier heeft het zwembad altijd beschikbare objecten aan de achterkant en gebruikte objecten aan de voorkant.
In de bezielen
methode, als de animatiefunctie van het deeltje terugkeert waar
, het object is klaar om opnieuw te worden gebruikt. De pool verwijdert het item uit de lijst en duwt het naar achteren. Het manipuleren van de lijst op deze manier maakt het creëren en vernietigen van objecten in het zwembad constant en zeer efficiënt.
In dit voorbeeld zijn de objecten die de objectpool zal bevatten Particles, maar voor uw eigen objectpool kan het zijn wat u maar wilt. Zolang dezelfde functies bestaan in het object dat u gebruikt als in de klasse Particle, zal het hetzelfde werken.
Uitgerust met een object-pool, is het tijd om een particle-systeem te maken om een sparkler-effect te creëren.
We beginnen met het maken van de objectgroep om alle deeltjes op het scherm te houden.
ObjectPool pool = nieuwe ObjectPool (100);
Bij elk frame genereren we een nieuw deeltje in het midden van het scherm en wijzen het een willekeurige snelheid toe. Ten slotte animeren we de objectgroep.
Random randomGenerator = new Random (); int velX = randomGenerator.nextInt (5); int velY = randomGenerator.nextInt (5); pool.get (30, 200, 200, velX, velY); pool.animate ();
Als u snel objecten maakt en verwijdert, kan een game mogelijk achterlopen of vastlopen. Door een objectpool te gebruiken, kunt u zowel systeembronnen als frustratie van gebruikers besparen.