Advertisement
  1. Game Development
  2. Programming

Vytvorenie života: Hre Conway života

Scroll to top
Read Time: 12 min

() translation by (you can also view the original English article)

Niekedy aj jednoduchú sadu základných pravidiel vám môžu poskytnúť veľmi zaujímavé výsledky. V tomto tutoriále budeme budovať jadra motora Conway Game of Life od Zeme.

Poznámka: Hoci tento tutoriál je napísaný pomocou C# a XNA, by mali byť schopní používať rovnaké techník a pojmov v takmer každom prostredí 2D vývoj hier.


Úvod

Conway Game of Life je celulárnej automat, ktorý bol navrhnutý v roku 1970 anglický matematik pomenovaný, No, John Conway.

Vzhľadom na dvojrozmernej mriežke buniek, niektoré "na" alebo "živý" iní "off" alebo "mŕtvy" a súbor pravidiel, ktoré riadi, ako prídu nažive, alebo zomrieť, môžeme mať zaujímavé "životná forma" rozložte priamo pred nami. Tak jednoducho kreslenie niekoľko vzorov na našich mriežku, a potom spustenie simulácie, môžeme sledovať základné formy života vyvíjajú, šíriť vymřít a nakoniec stabilizovať. Stiahnuť Záverečná zdrojové súbory, alebo Pozrite sa na ukážku nižšie:

Teraz, táto "hra života" nie je striktne "hra" - to je viac strojom, hlavne pretože neexistuje žiadny hráč a žiadny gól, to jednoducho vyvíja podľa jeho počiatočné podmienky. Napriek tomu je veľa zábavy hrať s, a existuje mnoho princípov herné dizajn, ktorý možno použiť na jeho vytvorenie. Takže bez ďalších okolkov, poďme začať!

Pre tento tutorial, som išiel dopredu a postavili všetko v XNA, pretože to je to, čo som najviac vyhovuje. (Tam je sprievodca Začíname s XNA tu, ak máte záujem.) Avšak, ste mali sledovať spolu s akúkoľvek 2D hra vývojové prostredie, ktoré poznáte.


Vytvorenie bunky

Najzákladnejší prvok v Conway Game of Life sú bunky, ktoré sú "formy života", ktoré tvoria základ celého simulácie. Každá bunka môže byť v jednom z dvoch štátov: "živé" alebo "mŕtve". Kvôli konzistentnosti, budeme sa držať tieto dva názvy pre bunky štáty na zvyšok tohto tutoriálu.

Bunky sa nepohybujú, jednoducho ovplyvniť ich susedia na základe ich aktuálneho stavu.

Teraz, pokiaľ ide o programovanie ich funkčnosť, sú tri správanie, musíme im dať:

  1. Potrebujú na sledovanie ich polohy, medze a štát, tak môže byť klikli a vyvodiť správne.
  2. Budú musieť prepnúť medzi živý a mŕtvy, keď klikol, ktorý umožňuje užívateľovi, aby skutočne zaujímavé veci sa stávajú.
  3. Museli byť koncipovaný ako biele alebo čierne Ak ste mŕtvy alebo živý, resp..

Všetky vyššie uvedené môže byť splňované vytvorenie triedy bunku, ktorá bude obsahovať kód nižšie:

1
2
class Cell
3
{
4
  public Point Position { get; private set; }
5
	public Rectangle Bounds { get; private set; }
6
7
	public bool IsAlive { get; set; }
8
9
	public Cell(Point position)
10
	{
11
		Position = position;
12
		Bounds = new Rectangle(Position.X * Game1.CellSize, Position.Y * Game1.CellSize, Game1.CellSize, Game1.CellSize);
13
14
		IsAlive = false;
15
	}
16
17
	public void Update(MouseState mouseState)
18
	{
19
		if (Bounds.Contains(new Point(mouseState.X, mouseState.Y)))
20
		{
21
			// Make cells come alive with left-click, or kill them with right-click.

22
			if (mouseState.LeftButton == ButtonState.Pressed)
23
				IsAlive = true;
24
			else if (mouseState.RightButton == ButtonState.Pressed)
25
				IsAlive = false;
26
		}
27
	}
28
29
	public void Draw(SpriteBatch spriteBatch)
30
	{
31
		if (IsAlive)
32
			spriteBatch.Draw(Game1.Pixel, Bounds, Color.Black);
33
34
		// Don't draw anything if it's dead, since the default background color is white.

35
	}
36
}

Mriežky a jeho pravidlá

Teraz, že každá bunka bude správať správne, musíme vytvoriť sieť, ktorá bude držať ich všetky, a vykonávať logické, že každý z nich povie, či to mala ožije, zostať nažive, zomrie alebo pobytu mŕtvych (žiadne zombie!).

Pravidlá sú pomerne jednoduché:

  1. Akékoľvek živé bunky s menej ako dve živé susedov umiera, akoby spôsobené nedostatočným populácie.
  2. Všetky živé bunky s dvoch alebo troch živých susedov žije na ďalšiu generáciu.
  3. Akékoľvek živé bunky s viac ako troch živých susedov zomrie, ako keby o preľudnenia.
  4. Žiadne mŕtve bunky s presne troch živých susedov sa stáva živé bunky, akoby reprodukcie.

Tu je rýchly vizuálny sprievodca týchto pravidiel na obrázku nižšie. Každá bunka zdôraznená modrá šípka bude ovplyvnený jeho príslušné očíslované pravidlo vyššie. Inými slovami, buniek 1 zomrie, bunky 2 zostane nažive, bunka 3 zomrie a bunky 4 ožije.

Tak, ako hry simulácia spustí aktualizáciu konštantný časových intervaloch, mriežky skontroluje každý z týchto pravidiel pre všetky bunky v mriežke. Ktoré možno dosiahnuť tým, že nasledujúci kód do novej triedy zavolám mriežky:

1
2
class Grid
3
{
4
	public Point Size { get; private set; }
5
6
	private Cell[,] cells;
7
8
	public Grid()
9
	{
10
		Size = new Point(Game1.CellsX, Game1.CellsY);
11
12
		cells = new Cell[Size.X, Size.Y];
13
14
		for (int i = 0; i < Size.X; i++)
15
			for (int j = 0; j < Size.Y; j++)
16
				cells[i, j] = new Cell(new Point(i, j));
17
	}
18
19
	public void Update(GameTime gameTime)
20
	{
21
		(...)
22
23
		// Loop through every cell on the grid.

24
		for (int i = 0; i < Size.X; i++)
25
		{
26
			for (int j = 0; j < Size.Y; j++)
27
			{
28
				// Check the cell's current state, and count its living neighbors.

29
				bool living = cells[i, j].IsAlive;
30
				int count = GetLivingNeighbors(i, j);
31
				bool result = false;
32
33
				// Apply the rules and set the next state.

34
				if (living && count < 2)
35
 					result = false;
36
 				if (living && (count == 2 || count == 3))
37
 					result = true;
38
 				if (living && count > 3)
39
					result = false;
40
				if (!living && count == 3)
41
					result = true;
42
43
				cells[i, j].IsAlive = result;
44
			}
45
		}
46
	}
47
48
	(...)
49
50
}

Jediné, čo sme chýba tu je kúzlo GetLivingNeighbors metóda, ktorá jednoducho spočíta, koľko aktuálnu bunku susedov sú v súčasnosti nažive. Takže, pridajte túto metódu do našej triedy mriežky:

1
2
public int GetLivingNeighbors(int x, int y)
3
{
4
	int count = 0;
5
6
	// Check cell on the right.

7
	if (x != Size.X - 1)
8
		if (cells[x + 1, y].IsAlive)
9
			count++;
10
11
	// Check cell on the bottom right.

12
	if (x != Size.X - 1 && y != Size.Y - 1)
13
		if (cells[x + 1, y + 1].IsAlive)
14
			count++;
15
16
	// Check cell on the bottom.

17
	if (y != Size.Y - 1)
18
		if (cells[x, y + 1].IsAlive)
19
			count++;
20
21
	// Check cell on the bottom left.

22
	if (x != 0 && y != Size.Y - 1)
23
		if (cells[x - 1, y + 1].IsAlive)
24
			count++;
25
26
	// Check cell on the left.

27
	if (x != 0)
28
		if (cells[x - 1, y].IsAlive)
29
			count++;
30
31
	// Check cell on the top left.

32
	if (x != 0 && y != 0)
33
		if (cells[x - 1, y - 1].IsAlive)
34
			count++;
35
36
	// Check cell on the top.

37
	if (y != 0)
38
		if (cells[x, y - 1].IsAlive)
39
			count++;
40
41
	// Check cell on the top right.

42
	if (x != Size.X - 1 && y != 0)
43
		if (cells[x + 1, y - 1].IsAlive)
44
			count++;
45
46
	return count;
47
}

Všimnite si, že vo vyššie uvedenom kódu, prvý ak vyhlásenie každého páru je jednoducho kontrola, že nie sme na okraji mriežky. Keby sme nemali túto kontrolu, narazíme by na niekoľko výnimiek z prekročenia hranice poľa. Tiež, pretože to povedie k počítať, nikdy sa zvýši, keď budeme kontrolovať cez okraje, to znamená, hra "predpokladá" hrany sú mŕtvi, tak to je ekvivalentná k mať trvalé okrajom biele, mŕtve bunky okolo našej hry windows.


Aktualizuje mriežku v diskrétnych oneskorovacích

Tak ďaleko, všetko logiky, ktorý sme implementovali je zvuk, ale to nebude správať správne Ak nie sme opatrní, aby sa ubezpečil, našich simulácia beží v diskrétnych oneskorovacích. To je len ozdobný spôsob, ako hovorí, že všetky naše bunky bude aktualizovaná v presne rovnakom čase, kvôli konzistentnosti. Ak sme nemali realizovať to, by sme dostať čudné správanie, pretože poradie v ktorom boli kontrolované bunky by vec, tak prísne pravidlá sme len stanovili by sa rozpadne a mini-chaos vyžiada.

Napríklad naše slučky vyššie skontroluje všetky bunky zľava doprava, takže ak bunku vľavo skontrolovali sme ožil, by sa malo zmeniť počet buniek v stredu sme si teraz kontrolu a môže spôsobiť, že ožije. Ale keby sme kontrolu sprava doľava namiesto bunku vpravo by mohol byť mŕtvy a bunku vľavo neprišiel nažive ešte, takže naša stredná bunka by zostať mŕtvy. To je zlé, pretože je v rozpore! By sme mali byť schopní kontrolovať bunky v náhodnom poradí chceme (ako špirála!) a ďalším krokom by mala byť vždy identické.

Našťastie, to je naozaj docela ľahko implementovať do kódu. Všetko čo potrebujeme je mať druhý mriežky buniek v pamäti pre budúci stav nášho systému. Zakaždým, keď zistíme, budúci stav bunky, uložíme ho do našej druhej mriežky pre budúci stav celého systému. Potom, keď sme našli ďalší stav každej bunke, aplikujeme ich všetky v rovnakom čase. Tak môžeme pridať 2D rad cestami nextCellStates ako súkromné premennej, a potom pridať túto metódu mriežka trieda:

1
2
public void SetNextState()
3
{
4
	for (int i = 0; i < Size.X; i++)
5
		for (int j = 0; j < Size.Y; j++)
6
			cells[i, j].IsAlive = nextCellStates[i, j];
7
}

Nakoniec nezabudnite opraviť váš spôsob aktualizácie vyššie, tak ho priradí výsledok ďalšieho stavu skôr ako aktuálny, potom hovor SetNextState na samom konci metódu Update, vpravo po slučky.


Kresliaca mriežka

Teraz, že sme dokončili častí zložitejšie mriežka logika, musíme mať možnosť čerpať na obrazovku. Mriežka bude čerpať každú bunku volaním metódy ich čerpať jeden naraz, tak, že všetky živé bunky budú čierne, a mŕtve bude biela.

Skutočné mriežky nepotrebuje k tomu nič, ale je to oveľa jasnejšie z pohľadu užívateľa, ak pridáme nejaké čiary mriežky. To umožňuje používateľovi ľahšie vidieť hranice buniek a komunikuje pocit z rozsahu, takže poďme vytvoriť nakresliť metóda takto:

1
2
public void Draw(SpriteBatch spriteBatch)
3
{
4
	foreach (Cell cell in cells)
5
		cell.Draw(spriteBatch);
6
7
	// Draw vertical gridlines.

8
	for (int i = 0; i < Size.X; i++)
9
		spriteBatch.Draw(Game1.Pixel, new Rectangle(i * Game1.CellSize - 1, 0, 1, Size.Y * Game1.CellSize), Color.DarkGray);
10
11
	// Draw horizontal gridlines.

12
	for (int j = 0; j < Size.Y; j++)
13
		spriteBatch.Draw(Game1.Pixel, new Rectangle(0, j * Game1.CellSize - 1, Size.X * Game1.CellSize, 1), Color.DarkGray);
14
}

Poznámka, že v horeuvedenom kóde sme pričom jediný pixel a strečing vytvoriť veľmi dlhé a tenké línie. Váš konkrétny herný engine môže poskytnúť jednoduchú metódu DrawLine, kde môžete zadať dva body a mať čiarou medzi nimi, čo by bolo ešte jednoduchšie, ako je uvedené vyššie.


Pridanie vysokej úrovni Hra Logic

V tomto bode, máme všetky základné kúsky potrebujeme aby sa hra beží, potrebujeme len, aby to všetko dohromady. Takže pre začiatok, vaša hra hlavnej triedy (ten, ktorý začína všetko), musíme pridať pár konštanty ako rozmery mriežky a framerate (ako rýchlo bude update) a všetky ostatné veci potrebujeme ako jediný pixel obrazu , veľkosť obrazovky, a tak ďalej.

Musíme tiež inicializovať mnohé z týchto vecí, ako je vytváranie distribučnej siete, nastavenie veľkosti okna hry a uistite sa, že myš je viditeľné, takže môžeme kliknúť na bunky. Ale všetky tieto veci sú špecifické pre motor a veľmi zaujímavé, takže budeme prejdite priamo cez to a dostať sa na dobré veci. (Samozrejme, ak ste po v XNA, môžete si Stiahnuť zdrojový kód pre získanie všetkých podrobností.)

Teraz, keď máme všetko nastavené a pripravené ísť, by sme mali byť schopní stačí spustiť hru! Ale nie tak rýchlo, pretože sa vyskytol problém: nemôžeme skutočne nič robiť lebo v hre je vždy spustený. To je v podstate nemožné vyvodiť konkrétne tvary, pretože oni vám rozpadávať, pri kreslení, takže naozaj musíme byť schopní pozastaviť hru. To by bolo pekné, ak by sme mohli jasne mriežky, ak sa to stane neporiadok, pretože naše výtvory budú často rastú mimo kontroly a zanechávajú bordel.

Takže, poďme sa pridať nejaký kód vždy, keď stlačíte medzerník pozastaviť hru a vyčistiť obrazovku, ak stlačíte kláves backspace:

1
2
protected override void Update(GameTime gameTime)
3
{
4
	keyboardState = Keyboard.GetState();
5
6
	if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
7
		this.Exit();
8
9
	// Toggle pause when spacebar is pressed.

10
	if (keyboardState.IsKeyDown(Keys.Space) && lastKeyboardState.IsKeyUp(Keys.Space))
11
		Paused = !Paused;
12
13
	// Clear the screen if backspace is pressed.

14
	if (keyboardState.IsKeyDown(Keys.Back) && lastKeyboardState.IsKeyUp(Keys.Back))
15
		grid.Clear();
16
17
	base.Update(gameTime);
18
19
	grid.Update(gameTime);
20
21
	lastKeyboardState = keyboardState;
22
}

To by tiež pomohlo, keby sme jasne najavo, že hra bola pozastavená, tak ako napíšeme naše metódy kresliť, poďme pridať nejaký kód na pozadí ísť červené a napíšte "Pozastavené" na pozadí:

1
2
protected override void Draw(GameTime gameTime)
3
{
4
	if (Paused)
5
		GraphicsDevice.Clear(Color.Red);
6
	else
7
		GraphicsDevice.Clear(Color.White);
8
9
	spriteBatch.Begin();
10
	if (Paused)
11
	{
12
		string paused = "Paused";
13
		spriteBatch.DrawString(Font, paused, ScreenSize / 2, Color.Gray, 0f, Font.MeasureString(paused) / 2, 1f, SpriteEffects.None, 0f);
14
	}
15
	grid.Draw(spriteBatch);
16
	spriteBatch.End();
17
18
	base.Draw(gameTime);
19
}

To je všetko! Všetko by malo byť teraz pracuje, takže si môžete dať to vírenie, čerpať niektoré formy života a čo sa stane! Ísť a preskúmať zaujímavé vzory môžete urobiť s odkazom na stránky Wikipédie znova. Môžete tiež hrať s framerate, veľkosť bunky a mriežky rozmery vyladiť podľa vašich predstáv.


Pridanie vylepšenia

V tomto bode, hra je plne funkčný a nie je žiadna hanba v volanie je deň. Ale jedna nepríjemnosti, možno ste si všimli, je kliknutia myši nezaregistrujete vždy v prípade, keď sa pokúšate aktualizovať bunky, takže keď si kliknite a ťahajte myšou cez mriežku nechám bodkovanú čiaru skôr než tuhé. To sa deje, pretože rýchlosť akou bunky aktualizácia je tiež rýchlosť myši je kontrolované, a to je príliš pomalý. Takže musíme jednoducho oddeliť rýchlosť akou aktualizuje hru a rýchlosť, akou sa číta vstup.

Začať určením aktualizácia úroková sadzba a framerate samostatne v hlavnej triede:

1
2
public const int UPS = 20; // Updates per second

3
public const int FPS = 60;

Teraz pri inicializácii hru, pomocou framerate (FPS) definovať, ako rýchlo to prečítam myši vstupné a kresliť, ktoré by mali byť pekné hladké 60 FPS prinajmenšom:

1
2
IsFixedTimeStep = true;
3
TargetElapsedTime = TimeSpan.FromSeconds(1.0 / FPS);

Potom pridať časovač mriežka triedy, taká, že to bude len aktualizovať, keď to potrebuje, nezávisle od rýchlosti snímok:

1
2
public void Update(GameTime gameTime)
3
{
4
	(...)
5
6
	updateTimer += gameTime.ElapsedGameTime;
7
8
	if (updateTimer.TotalMilliseconds > 1000f / Game1.UPS)
9
	{
10
		updateTimer = TimeSpan.Zero;
11
12
		(...) // Update the cells and apply the rules.

13
14
	}
15
16
}

Teraz, mali by ste byť schopní spustiť hru na čokoľvek rýchlosť, ktorú chcete, dokonca veľmi pomaly 5 aktualizácií za sekundu môžete pozorne sledovať vaše simulácia rozvinúť, kým ešte je schopný kresliť pekné hladké čiary na pevné framerate.


Záver

Teraz máte hladký a funkčná hra života na rukách, ale v prípade, že chcete preskúmať ďalšie, tam je vždy ďalšie Vychytávky, ktoré môžete pridať k nemu. Napríklad mriežky momentálne predpokladá, že po jej okrajoch, všetko je mŕtvy. Môžete upraviť ho tak, aby mriežka obteká okolo, takže Klzák poletia navždy! Neexistuje žiadny nedostatok variácie na tejto populárnej hre, takže nechať svoju fantáziu voľne behať.

Vďaka za čítanie, dúfam, že ste sa naučil niekoľko užitočných vecí dnes!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Game Development tutorials. Never miss out on learning about the next big thing.
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.