Vrijwel elke game kan worden beschouwd als een hoofdfunctie die alle gamelogica bevat en die wordt uitgevoerd wanneer de gebruiker iets doet of na een bepaalde tijd. Deze cyclus van het telkens opnieuw uitvoeren van dezelfde kernfunctie wordt de game loop, en is cruciaal om te begrijpen voor elke soort game-ontwikkeling.
Je hebt waarschijnlijk het bordspel Chutes and Ladders gespeeld (of Snakes and Ladders zoals we het in het VK noemen).
(Photo credit incurable_hippie op Flickr)
Elke speler gooit de dobbelsteen (of draait de spinner) en beweegt het aantal aangegeven vakjes vooruit. Het vierkant waarop ze landen, kan ze naar achteren laten glijden of meerdere velden naar voren laten klimmen. De speler wint het spel wanneer ze bij het laatste vierkant komen.
Dus, in de bovenstaande afbeelding, maakt het landen op Plein 6 je vier vierkanten omhoog naar Plein 10; als je op Plein 19 landt, schuif je 15 vierkanten terug naar Vierkant 4; en de landing op Square 64 betekent dat je het spel wint.
Stel nu dat je een speler speelt, om te oefenen. Je doet hetzelfde als steeds opnieuw, totdat je Square 64 bereikt. Hoe zou je dit in code voorstellen??
U begint waarschijnlijk met het maken van een array om de waarden van de vierkanten op te slaan. De meeste elementen bevatten nul, maar een paar bevatten een positief getal (een ladder aanduidend) of een negatief getal:
Notitie: Dit is pseudocode, niet AS3
var specialSquares: Array = []; specialSquares [0] = 0; // het vierkant waarop de spelers beginnen, vóór 1 specialSquares [1] = 0 ;? specialSquares [6] = +4 ;? specialSquares [19] = -15;
? enzovoorts. Dan zou je een functie hebben om de speler te verplaatsen op basis van het nummer op de dobbelsteen:
functie movePlayer (vierkanten: Number) newSquare = currentSquare + vierkanten; newSquare + = specialSquares [newSquare]; currentSquare = newSquare;
Je zou dit dan in een grotere functie kunnen plaatsen die een hele draai vertegenwoordigt:
functie takeATurn () diceNumber = random (1, 6); // willekeurig getal tussen 1 en 6 movePlayer (diceNumber); if (currentSquare == 64) // speler heeft gewonnen! spel is over
Deze functie, takeATurn ()
, kapselt de kerngame-logica in voor Single-player Chutes en Ladders. Maar hoe krijgen we deze functie eigenlijk? Er zijn verschillende opties:
We kunnen een knop op het scherm plaatsen en laten bellen takeATurn ()
elke keer dat er op wordt geklikt. Als je ooit Facebook Scrabble of Words With Friends hebt gespeeld, heb je deze optie in actie gezien.
We zouden kunnen maken takeATurn ()
voer elke zestig seconden uit.
Eigenlijk is deze optie een beetje ingewikkelder dan het lijkt. In de praktijk zien we nooit games zonder sommige speler invoer; zonder interactie, kan het niet echt als een spel worden beschouwd. Maar toch, sommige spellen hebben een element van "kalendertijd" betrokken. Overweeg FarmVille: jij, de speler, plant je gewassen, en dan ontwikkelen ze zich om de paar minuten (of uren) een beetje verder, van zaden tot scheuten tot fruit. En in sommige beschavingswijzen krijg je een bepaalde hoeveelheid tijd (bijvoorbeeld vijf minuten) om al je zetten voor één "beurt" te maken; Zodra die vijf minuten voorbij zijn, wordt de logische spellogica geactiveerd.
Sommige games gebruiken een combinatie van deze twee opties: Mafia Wars heeft bijvoorbeeld een hulpmiddel dat 'energie' wordt genoemd en dat elke vijf minuten een eenheid vult; je neemt acties in het spel door deze bron te gebruiken, dus de kernlogica-functie wordt nog steeds geactiveerd door een actie van een gebruiker, hij is alleen beperkt door de tijd.
Dit is een patroon dat in de meeste spellen voorkomt: een stuk code met de kernspellogica wordt herhaaldelijk geactiveerd. We noemen dit het game loop.
Er is een term voor de actie of tijdsperiode die ook de kernlogica-code triggert: Kruis aan (zoals het geluid dat een klok maakt).
Dus in Civilization is de vink om de vijf minuten. In Words With Friends speelt u uw beurt oorzaken een teek. Met andere woorden, de spellus loopt één keer per tik.
Super Mario Bros lijkt niet in een van deze categorieën te passen. Mario reageert op de invoer van de speler? en toch zijn er van alles aan de gang zonder dat je iets hoeft te doen (Goomba's lopen rond, de timer telt af, voorwerpen vallen). Zijn er twee spellussen?
Nee. Er is er maar één en die wordt alleen door de tijd getriggerd, maar met een vinkje van slechts een fractie van een seconde.
In Civilization heb je een periode van vijf minuten om alles in te voeren wat je wilt doen in de huidige beurt, voordat de game "tikt" en voert de game opnieuw uit op basis van al je invoer. Dus als je in beurt 23 zegt dat je wilt dat je krijgers een hert aanvallen, dan in beurt 24 krijgt iedereen hert voor het avondeten.
Het is hetzelfde met Mario. Als je tijdens één keer op de Jump-knop drukt, begint Mario in de volgende iteratie van de game-lus te springen.
Merk op dat jij niet doen moet de tijd nemen waarop uw Jump-druk plaatsvindt, net zoals de logica van de kerngame wordt geactiveerd - al uw acties tijdens een tick-periode worden geregistreerd en gebruikt tijdens de volgende iteratie van de gamelus.
Alle spellogica wordt verwerkt in de spellus. Maar er is meer bij een game dan bij de logica, graphics zijn daar het belangrijkste voorbeeld van.
Het tekenen van afbeeldingen op het scherm is hard werken voor de computer. Laten we aannemen dat je een actiegame hebt met een vinkje van 1/60 seconde; dat moet het spel het gevoel geven dat het vloeiend reageert op de bedieningselementen van de speler. Maar wat gebeurt er als de computer van de speler te traag is om alle code voor de spellogica uit te voeren (reageren op invoer, zwaartekracht simuleren, AI-routines uitvoeren) en teken alles binnen een / 60ste van een seconde op het scherm?
In dit scenario kunnen we twee afzonderlijke loops gebruiken: een gamelus en een teken lus. We zouden dan de trekkingslus op een veel lagere frequentie kunnen uitvoeren dan de spellus; laten we zeggen dat we de schermhelft zo vaak vernieuwen, dat wil zeggen elke 1 / 30ste van een seconde.
De hoeveelheid verwerkingskracht die het spel nodig heeft, kan van niveau tot niveau variëren. Overweeg een shoot-'em-up: de eerste paar levels hebben heel weinig schepen op het scherm om de speler voorzichtig te kalmeren, terwijl de laatste levels tientallen vijandelijke schepen en honderden kogels kunnen hebben die allemaal rond dezelfde scène vliegen op een keer. De spellus moet uitzoeken hoe al die objecten moeten bewegen en de lus moet elke lus renderen, dus terwijl het mogelijk was om zowel de spellus als de lus elke 1 / 60ste van een seconde te laten draaien op de begin van het spel, tegen het einde moet iets geven.
Het is over het algemeen eenvoudiger om de tekenlus te vertragen dan de spellus, als je moet kiezen. Het aanpassen van de vinkijl van de game lus betekent dat alles in je spel wordt aangepast op basis van tijd; als Mario met een snelheid van 20 pixels / seconde loopt en je ontwerpt het spel met een vinklengte van 1/60 seconde, dan moet je hem 1/3 van een pixel per vink verplaatsen. Als je de gamelus aanpast om een vinklengte van 1/30 seconde te hebben, dan moet je dit aanpassen tot 2/3 rds van een pixel per vinkje - en zelfs dat is een simpele verandering in vergelijking met het houden van natuurkundige berekeningen en AI-routines consequent.
Om deze reden zijn spellen vaak gericht op het consistent houden van de vinklussen en het vertragen van de trekkingslus als er meer kracht nodig is. Als je ooit de FPS-teller hebt ingeschakeld (een afkorting van Frames Per Second, het aantal keren dat de trekkingslus per seconde wordt uitgevoerd) in een first person shooter, heb je deze gezien afhankelijk van hoeveel op het scherm wordt weergegeven ; de verversingsfrequentie van de tekenlus wordt automatisch aangepast. De game ziet er misschien juddery uit - zoals een live streaming video op een trage internetverbinding - maar tenzij het wordt uitgevoerd op een computer met veel minder stroom dan de game-ontwikkelaars voor ogen hadden, blijven alle objecten in de gamewereld doorgaan bewegen en communiceren met de juiste snelheden.
Voor een goed artikel waarin wordt uitgelegd hoe Flash hiermee omgaat, bekijk Sean Christmann's post op de 'Elastic Racetrack'.