Unlimited WordPress themes, graphics, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Game Development
  2. Programming
Gamedevelopment

Het leven creëren: “Conway’s Game of Life”

by
Difficulty:IntermediateLength:LongLanguages:

Dutch (Nederlands) translation by Adjatay Bashroh Aldad (you can also view the original English article)

Soms kan zelfs een eenvoudige set basisregels u zeer interessante resultaten opleveren. In deze tutorial bouwen we de kernmotor van Conway's Game of Life vanaf de basis op.

Opmerking: hoewel deze zelfstudie is geschreven met behulp van C# en XNA, zou u in bijna elke 2D-ontwikkelingsomgeving voor games dezelfde technieken en concepten moeten kunnen gebruiken.


Introductie

Conway's Game of Life is een cellulaire automaat die in de jaren zeventig werd bedacht door een Britse wiskundige met de naam, nou ja, John Conway.

Gegeven een tweedimensionaal raster van cellen, met enkele "aan" of "in leven" en anderen "uit" of "dood", en een reeks regels die bepalen hoe ze tot leven komen of sterven, kunnen we een interessante 'levensvorm hebben' 'ontvouw recht voor ons. Dus door simpelweg een paar patronen op ons rooster te tekenen en vervolgens de simulatie te starten, kunnen we basale levensvormen zien evolueren, verspreiden, afsterven en uiteindelijk stabiliseren. Download de uiteindelijke bronbestanden of bekijk de onderstaande demo:

Nu, deze "Game of Life" is niet strikt een "game" - het is meer een machine, vooral omdat er geen speler en geen doel is, maar gewoon evolueert op basis van de initiële condities. Desalniettemin is het heel leuk om mee te spelen en er zijn veel principes van gameontwerp die kunnen worden toegepast op de creatie ervan. Dus, zonder verder oponthoud, laten we beginnen!

Voor deze zelfstudie ging ik door en bouwde alles in XNA, want daar ben ik het meest comfortabel mee. (Er is een handleiding om hier met XNA aan de slag te gaan, als je geïnteresseerd bent.) Je zou echter wel moeten kunnen meegaan met elke 2D-game-ontwikkelomgeving die je kent.


De Cellen Maken

Het meest elementaire element in Conway's Game of Life zijn de cellen, de "levensvormen" die de basis vormen van de hele simulatie. Elke cel kan zich in een van de twee toestanden bevinden: "levend" of "dood". Voor de consistentie houden we vast aan die twee namen voor de celstatussen voor de rest van deze zelfstudie.

Cellen bewegen niet, ze beïnvloeden eenvoudigweg hun buren op basis van hun huidige toestand.

Wat betreft het programmeren van hun functionaliteit, zijn er de drie gedragingen die we ze moeten geven:

  1. Ze moeten hun positie, grenzen en status bijhouden, zodat ze op de juiste manier kunnen worden aangeklikt en getekend.
  2. Ze moeten schakelen tussen levend en dood wanneer erop wordt geklikt, waardoor de gebruiker daadwerkelijk interessante dingen kan doen.
  3. Ze moeten als wit of zwart worden getekend als ze respectievelijk dood of levend zijn.

Al het bovenstaande kan worden bereikt door een celklasse te maken die de onderstaande code bevat:


Het Raster en zijn Regels

Nu dat elke cel zich correct gaat gedragen, moeten we een rooster maken dat ze allemaal vasthoudt, en de logica implementeren die iedereen vertelt of het levend moet worden, in leven zal blijven, zal sterven of dood zal blijven (geen zombies!).

De regels zijn redelijk eenvoudig:

  1. Elke levende cel met minder dan twee live buren sterft, alsof veroorzaakt door onderpopulatie.
  2. Elke levende cel met twee of drie levende buren leeft voort op de volgende generatie.
  3. Elke levende cel met meer dan drie live buren sterft als door overbevolking.
  4. Elke dode cel met precies drie levende buren wordt een levende cel, als door reproductie.

Hier is een snelle visuele gids voor deze regels in de afbeelding hieronder. Elke cel gemarkeerd door een blauwe pijl zal worden beïnvloed door de desbetreffende genummerde regel hierboven. Met andere woorden, cel 1 zal sterven, cel 2 zal in leven blijven, cel 3 zal sterven en cel 4 zal tot leven komen.

Omdat de spelsimulatie dus een update uitvoert met constante tijdsintervallen, controleert het raster elk van deze regels voor alle cellen in het raster. Dat kan worden bereikt door de volgende code in een nieuwe klasse te plaatsen die ik Grid zal noemen:

Het enige wat we hier missen is de magische GetLivingNeighbors methode, die eenvoudig telt hoeveel van de buren van de huidige cel op dit moment in leven zijn. Dus laten we deze methode toevoegen aan onze Grid klasse:

Merk op dat in de bovenstaande code de eerste if instructie van elk paar gewoon controleert of we niet aan de rand van het raster staan. Als we deze controle niet hadden, zouden we verschillende uitzonderingen tegenkomen die de grenzen van de array overschrijden. Ook, omdat dit ertoe leidt dat het aantal nooit wordt verhoogd als we langs de randen kijken, dat betekent dat het spel 'veronderstelt' dat de randen dood zijn, dus het komt overeen met een permanente rand van witte, dode cellen rond onze spelvensters.


Het Raster Bijwerken in Discrete Tijd Stappen

Tot nu toe alle logica die we hebben geïmplementeerd is goed, maar het zal niet goed gedragen als we niet voorzichtig zijn om ervoor te zorgen dat onze simulatie loopt in discrete tijdstappen. Dit is gewoon een mooie manier om te zeggen dat al onze cellen op precies hetzelfde moment, omwille van de consistentie zal worden bijgewerkt. Als wij dit niet uitvoeren, zouden we vreemde gedrag krijgen, omdat de volgorde waarin de cellen werden gecontroleerd zou van belang, zodat de strenge regels die we net gesteld uit elkaar vallen zou en mini chaos zou voortvloeien.

Bijvoorbeeld, onze bovenstaande lus controleert alle cellen van links naar rechts, dus als de cel aan de linkerkant die we net gecontroleerd kwam tot leven, dit zou het aantal voor de cel in het midden te wijzigen we bent nu de controle en misschien maken het tot leven komen. Maar als men in plaats daarvan controleren van rechts naar links, de cel rechts misschien wel dood en de cel aan de linkerkant is niet tot leven komen nog, zodat onze middelste cel dood zou blijven. Dit is slecht omdat het inconsistent is! We moeten in staat zijn om de cellen in willekeurige volgorde te controleren (zoals een spiraal!) En de volgende stap moet altijd identiek zijn.

Gelukkig is dit echt vrij eenvoudig te implementeren in code. We hebben alleen een tweede raster met cellen nodig voor de volgende status van ons systeem. Telkens wanneer we de volgende staat van een cel bepalen, slaan we deze op in ons tweede raster voor de volgende status van het hele systeem. Wanneer we vervolgens de volgende status van elke cel hebben gevonden, passen we ze allemaal tegelijkertijd toe. Dus we kunnen een 2D-array van booleans nextCellStates als een privévariabele toevoegen en vervolgens deze methode aan de Grid klasse toevoegen:

Vergeet ten slotte niet om je Update methode hierboven te repareren, zodat het resultaat wordt toegewezen aan de volgende staat in plaats van de huidige, en vervolgens SetNextState aan het einde van de Update methode aanroept, direct nadat de lussen zijn voltooid.


Tekening van Het Raster

Nu we de lastiger delen van de logica van het raster hebben voltooid, moeten we het op het scherm kunnen tekenen. Het raster zal elke cel tekenen door hun tekenmethoden één voor één te noemen, zodat alle levende cellen zwart worden en de doden wit.

Het werkelijke raster hoeft niets te tekenen, maar het is veel duidelijker vanuit het perspectief van de gebruiker als we wat rasterlijnen toevoegen. Dit stelt de gebruiker in staat gemakkelijker celgrenzen te zien en communiceert ook een gevoel voor schaal, dus laten we een Draw methode als volgt maken:

Merk op dat we in de bovenstaande code een enkele pixel nemen en deze uitrekken om een ​​zeer lange en dunne lijn te creëren. Uw specifieke game-engine kan een eenvoudige DrawLine methode bieden, waarbij u twee punten kunt specificeren en een lijn tussen deze punten kunt laten weergeven, waardoor het nog eenvoudiger wordt dan het bovenstaande.


Het Toevoegen van High-level Game Logic

Op dit punt hebben we alle basisstukken die we nodig hebben om het spel te laten draaien, we moeten het allemaal bij elkaar brengen. Dus om te beginnen, in de hoofdklasse van je spel (degene die alles start), moeten we een paar constanten toevoegen, zoals de afmetingen van het raster en de framerate (hoe snel het zal worden bijgewerkt), en alle andere dingen die we nodig hebben de afbeelding met één pixel, de schermgrootte, enzovoort.

We moeten ook veel van deze dingen initialiseren, zoals het maken van het raster, het instellen van de venstergrootte voor het spel en ervoor zorgen dat de muis zichtbaar is, zodat we op cellen kunnen klikken. Maar al deze dingen zijn motorspecifiek en niet erg interessant, dus we gaan er meteen overheen en komen bij de goede dingen. (Natuurlijk, als je in XNA meegaat, kun je de broncode downloaden om alle details te krijgen.)

Nu dat we alles ingesteld hebben en klaar om te gaan, moeten we zitten kundig voor uitsluitend stormloop van het spel! Maar niet zo snel, want er een probleem is: we echt niets kan doen omdat het spel altijd draait. Het is eigenlijk onmogelijk om specifieke vormen te tekenen, omdat ze uit elkaar vallen terwijl je ze tekent, dus we moeten echt in staat zijn om het spel te pauzeren. Het zou ook leuk zijn als we het raster zouden kunnen opruimen als het een puinhoop wordt, omdat onze creaties vaak uit de hand lopen en een puinhoop achterlaten.

Dus laten we wat code toevoegen om het spel te pauzeren wanneer de spatiebalk wordt ingedrukt en het scherm leegmaken als er op de spatiebalk wordt gedrukt:

Het zou ook helpen als we heel duidelijk maakten dat de game was onderbroken, dus als we onze Draw methode schrijven, voegen we wat code toe om de achtergrond rood te maken en schrijven we "Paused" op de achtergrond:

Dat is het! Alles zou nu moeten werken, dus je kunt het een werveling geven, een paar levensvormen tekenen en zien wat er gebeurt! Ga en ontdek interessante patronen die je kunt maken door opnieuw naar de Wikipedia pagina te gaan. Je kunt ook spelen met de framerate, celgrootte en rasterafmetingen om het naar wens aan te passen.


Verbeteringen Toevoegen

Op dit punt, de game is volledig functioneel en er is geen schaamte om het een dag te noemen. Maar een ergernis die je misschien hebt gemerkt is dat je muisklikken niet altijd registreren wanneer je een cel probeert bij te werken, dus wanneer je klikt en je muis over het raster sleept, laat het een stippellijn achter in plaats van een solide een. Dit gebeurt omdat de snelheid waarmee de cellen worden bijgewerkt ook de snelheid is waarmee de muis wordt gecontroleerd en het is veel te langzaam. We moeten dus eenvoudigweg de snelheid waarmee de game wordt bijgewerkt en de snelheid waarmee de invoer wordt gelezen, ontkoppelen.

Begin met het definiëren van de updatefrequentie en de framerate afzonderlijk in de hoofdklasse:

Nu, bij het initialiseren van het spel, gebruik de framerate (FPS) om te definiëren hoe snel het de muisinvoer en -tekening zal lezen, wat op zijn minst een mooie soepele 60 FPS zou moeten zijn:

Voeg vervolgens een timer toe aan uw Grid klasse, zodanig dat deze alleen wordt bijgewerkt wanneer dat nodig is, ongeacht de framerate:

Nu zou je in staat moeten zijn om het spel uit te voeren op elke gewenste snelheid, zelfs een zeer trage 5 updates per seconde, zodat je zorgvuldig je simulatie kunt bekijken, terwijl je nog steeds mooie vloeiende lijnen kunt tekenen met een solide framerate.


Conclusie

Je hebt nu een soepel en functioneel Game of Life in handen, maar voor het geval je het verder wilt verkennen, kun je er altijd nog een paar extra's aan toevoegen. Het raster veronderstelt bijvoorbeeld dat buiten de randen alles dood is. Je zou het zo kunnen aanpassen dat het raster rondwikkelt, zodat een zweefvliegtuig voor altijd zou vliegen! Er is geen gebrek aan variaties op deze populaire game, dus laat je fantasie de vrije loop.

Bedankt voor het lezen, ik hoop dat je vandaag een aantal nuttige dingen hebt geleerd!

Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.