Snelheid

Pengo © 2002-2003, Joost Ronkes Agerbeek

Op dit moment beweegt alles in Pengo even snel. Dat komt doordat we de game loop maar op één punt vertraagd hebben. Het maakt het spel niet echt leuk, dus daar gaan we deze les verandering in brengen.

Individuele timing

Wat we gaan doen om het probleem op te lossen, is voor elk object een aparte vertraging inbouwen. We krijgen dus drie vertragingen: één voor de speler, één voor de blokken en één voor de vijanden. Deze vertragingen slaan we op in globale constanten.

Main.cpp
/**
 * De vertragingen van de speler, blokken en vijanden.
 */
const int PlayerDelay = 150;
const int BlockDelay  = 10;
const int EnemyDelay  = 250;

Omdat we de objecten individueel gaan vertragen, moeten we de algemene game loop vertraging weghalen. Verwijder dus alle regels die te maken hebben met het vertragen van de game loop. Oké oké, ik zal vertellen welke regels dat zijn.

/**
 * De vertraging van de game loop in milliseconden.
 */
const int GameDelay = 250;
// vraag huidige tijd op
DWORD myTime = timeGetTime();
// wacht tot vertraging om is
while (timeGetTime() < (myTime + GameDelay));
[ Naar boven | Terug naar Pengo ]

Wachten met bewegen

De truc is nu dat we per object (speler, blokken, vijanden) de tijd bijhouden en dat we per object bepalen of de vertraging voorbij is. Om de tijd bij te houden, hebben we dus drie vertragingen nodig. Deze komen boven de game loop te staan.

Main.cpp
// vraag tijd op voor speler, blokken en vijanden
DWORD myPlayerTime = timeGetTime();
DWORD myBlockTime  = timeGetTime();
DWORD myEnemyTime  = timeGetTime();

Om nu de speler te vertragen, zet we een if-statement rondom de code die de speler beweegt. De logische expressie lijkt erg veel op die van de vertraging van de game loop: als de opgeslagen tijd plus de vertraging groter is dan de huidige tijd, dan is het tijd om de speler te verplaatsen.

Main.cpp
// is het tijd om de speler te verplaatsen?
if ((myPlayerTime + PlayerDelay) < timeGetTime())
{
	// ja, begin opnieuw met de timing voor de speler
	myPlayerTime = timeGetTime();
	
	// beweegt de speler?
	if (GlobalLevel.Player.IsMoving)
	{
		// ja, verwijder speler van het scherm
		ClearPlayer(GlobalLevel.Player);

		// verplaats de speler
		MovePlayer(GlobalLevel.Player);
	}
}

Je ziet dat myPlayerTime weer wordt ingesteld op de huidige tijd. Hiermee reset je als het ware de timer van de speler.

In bovenstaand stukje code wordt timeGetTime() twee keer aangeroepen. Bovendien moeten we timeGetTime() straks ook nog aanroepen voor de blokken en de vijanden. Omdat een functieaanroep relatief traag is, passen we het zó aan, dat timeGetTime() maar één keer wordt aangeroepen.

Main.cpp
DWORD myTime = timeGetTime();

// is het tijd om de speler te verplaatsen?
if ((myPlayerTime + PlayerDelay) < myTime)
{
	// ja, begin opnieuw met de timing voor de speler
	myPlayerTime = myTime;
	
	// beweegt de speler?
	if (GlobalLevel.Player.IsMoving)
	{
		// ja, verwijder speler van het scherm
		ClearPlayer(GlobalLevel.Player);

		// verplaats de speler
		MovePlayer(GlobalLevel.Player);
	}
}

Inderdaad, je had het vorige blok code net zo goed niet kunnen kopiëren. :-P

[ Naar boven | Terug naar Pengo ]

Op herhaling

Het vertragen van de blokken en van de vijanden gaat natuurlijk net zo als het vertragen van de speler.

Main.cpp
// is het tijd om de blokken te verplaatsen?
if ((myBlockTime + BlockDelay) < myTime)
{
	// ja, begin opnieuw met de timing voor de blokken
	myBlockTime = myTime;

	// verplaats de blokken
	for (int i = 0; i < GlobalLevel.Blocks.size(); i++)
	{
		// beweegt het blok?
		if (GlobalLevel.Blocks.at(i).IsMoving)
		{
			// ja, verwijder het van het scherm
			ClearBlock(GlobalLevel.Blocks.at(i));

			// verplaats het blok
			MoveBlock(GlobalLevel.Blocks.at(i));
		}
	}
}

// is het tijd om de vijanden te verplaatsen?
if ((myEnemyTime + EnemyDelay) < myTime)
{
	// ja, begin opnieuw met de timing voor de vijanden
	myEnemyTime = myTime;

	// verplaats de vijanden
}

Omdat we nog geen code hebben geschreven om de vijanden te laten verplaatsen, ontbreekt dat gedeelte. Ik heb de vertraging er wel alvast bij gezet; dan hoeven we dat volgende keer niet meer te doen.

[ Naar boven | Terug naar Pengo ]

Vijanden verplaatsen

Nou vooruit, laten we de basis voor het verplaatsen van vijanden maar alvast leggen. Dat scheelt ons volgende keer werkt. Het verplaatsen van vijanden gaat net als het verplaatsen van blokken: we lopen alle vijanden stuk voor stuk langs. Eerst halen we ze van het scherm met ClearEnemy en daarna verplaatsen we ze met MoveEnemy.

Main.cpp
// is het tijd om de vijanden te verplaatsen?
if ((myEnemyTime + EnemyDelay) < myTime)
{
	// ja, begin opnieuw met de timing voor de vijanden
	myEnemyTime = myTime;

	// verplaats de vijanden
	for (int i = 0; i < GlobalLevel.Enemies.size(); i++)
	{
		// verwijder vijand van het scherm
		ClearEnemy(GlobalLevel.Enemies.at(i));

		// verplaats het vijand
		MoveEnemy(GlobalLevel.Enemies.at(i));
	}
}

Nu hebben we alleen nog de functies ClearEnemy en MoveEnemy nodig. Ik zet hier allebei meteen maar onder. Denk eraan om de declaraties in de juiste headerbestanden op te namen.

Graphics.cpp
/**
 * Verwijdert een vijand van het scherm.
 *
 * @param	enemy	de vijand die verwijderd moet worden
 */
void ClearEnemy(const Enemy& enemy)
{
	// verwijder vijand
	WriteText(" ", enemy.X, enemy.Y);
}
Enemy.cpp
/**
 * Verplaats een vijand één positie.
 *
 * @param	enemy	de vijand die verplaatst moet worden
 */
void MoveEnemy(Enemy& enemy)
{
}

Je ziet, de functie MoveEnemy is nog leeg en de vijanden bewegen dus nog niet. Volgende les zullen we MoveEnemy invullen. Op dit moment staat de functie er alleen maar om te zorgen dat de code compileert.

[ Naar boven | Terug naar Pengo ]

Conclusie

En dat is het alweer voor deze keer. Dat was toch niet zo heel erg veel? (Zeker niet in vergelijking met de vorige keer). We hebben de algemene vertraging van de game loop vervangen door individuele vertragingen van de speler, de blokken en de vijanden.

De volgende keer laten we de vijanden achter ons aan rennen en daarvoor moeten we ze wat intelligentie geven. Oftewel, we gaan ons bezig houden met één van de moeilijkste onderwerpen van het spelprogrammeren: artificial intelligence. Maak je borst maar nat.

Programmeren is leuk. :-)

[ Naar boven | Terug naar Pengo ]

Extra

Als je de bovenstaande code af hebt en je wilt Pengo graag uitbreiden, dan kun je de volgende features toevoegen.

[ Naar boven | Terug naar Pengo ]

Downloads

De volgende bestanden horen bij deze les.

[ Naar boven | Terug naar Pengo ]

Valid XHTML 1.0! Correct CSS! Laatst bijgewerkt: dinsdag 15 april 2014