Blokken

Pengo © 2002-2003, Joost Ronkes Agerbeek

In Pengo is het veld opgebouwd uit blokken. De speler kan deze blokken verplaatsen door er tegen te schoppen. In deze les programmeren we de blokken.

Datastructuur

Net als bij de speler beginnen we met het maken van een datastructuur voor de blokken. Om te beginnen heeft een blok in ieder geval een positie. De structure komt in het bestand Block.h te staan.

Block.h
/******************************************************************************
	Bestand:   Block.h
	Project:   Pengo

	Copyright: (c) 2003 Joost Ronkes Agerbeek

	Auteur:    Joost Ronkes Agerbeek <joost@ronkes.nl>
	Datum:     18 januari 2003
******************************************************************************/

#ifndef __BLOCK_H__
#define __BLOCK_H__

/******************************************************************************
	Structures
******************************************************************************/

/**
 * Een blok.
 */
struct Block
{
	/**
	 * De positie van het blok.
	 */
	int X, Y;
};

#endif

Het aanmaken van een blok gaat met de functie CreateBlock. Als parameters krijgt deze functie de positie van het blok mee. In Pengo gebruiken we meerdere blokken en die willen we natuurlijk niet allemaal op dezelfde plaats hebben staan. De code komt in Block.cpp.

Block.cpp
/******************************************************************************
	Bestand:   Block.cpp
	Project:   Pengo

	Copyright: (c) 2003 Joost Ronkes Agerbeek

	Auteur:    Joost Ronkes Agerbeek <joost@ronkes.nl>
	Datum:     18 januari 2003
******************************************************************************/

#include "Block.h"

/******************************************************************************
	Globale functies
******************************************************************************/

/**
 * Maakt een nieuw blok aan en initialiseert de gegevens van het blok.
 *
 * @param	x	de x-coordinaat van het te maken blok
 * @param	y	de y-coordinaat van het te maken blok
 *
 * @return	een nieuw blok
 */
Block CreateBlock(int x, int y)
{
	// maak nieuw blok
	Block myBlock;

	// stel standaardwaarden in
	myBlock.X = x;
	myBlock.Y = y;

	// geef blok terug
	return myBlock;
}

We moeten CreateBlock aan kunnen roepen vanuit andere bestanden, dus we nemen het prototype op in Block.h.

Block.h
/******************************************************************************
	Globale functies - definities
******************************************************************************/

/**
 * Maakt een nieuw blok aan en initialiseert de gegevens van het blok.
 *
 * @param	x	de x-coordinaat van het te maken blok
 * @param	y	de y-coordinaat van het te maken blok
 *
 * @return	een nieuw blok
 */
Block CreateBlock(int x, int y);
[ Naar boven | Terug naar Pengo ]

Tekenen

Voordat we een blok aanmaken, zorgen we er eerst voor dat het blok getekend wordt. Het tekenen van een blok is vrij rechttoe-rechtaan. We moeten alleen een ASCII-teken en kleuren kiezen voor het blok. De functie DrawBlock roept WriteText aan om het block te tekenen. Net als bij het tekenen van de speler, zetten we de code voor het tekenen van een blok in Graphics.cpp. Vergeet niet Block.h te includen.

Graphics.cpp
/**
 * Het ASCII-teken dat een blok voorstelt.
 */
const char BlockCharacter = 4;

/**
 * De kleuren van een blok.
 */
const Color BlockForegroundColor = Yellow;
const Color BlockBackgroundColor = Black;
/**
 * Tekent een blok naar het scherm.
 *
 * @param	block	het blok dat getekend moet worden
 */
void DrawBlock(const Block& block)
{
	// teken blok
	WriteText(BlockCharacter, block.X, block.Y, BlockForegroundColor,
		BlockBackgroundColor);
}

De parameter block wordt niet veranderd door de functie DrawBlock, dus we maken hem constant door het woord const ervoor te zetten.

Nu kunnen we een blok aanmaken en op het scherm zetten.

Main.cpp
// maak blok aan
Block myBlock = CreateBlock(30, 11);

// teken blok
DrawBlock(myBlock);
[ Naar boven | Terug naar Pengo ]

Beweging

De beweging van een blok in Pengo lijkt erg veel op de beweging van een speler. De code zal daarom niet erg veel verrassingen opleveren. Eerst maar eens de structure uitbreiden.

Block.h
/**
 * Een blok.
 */
struct Block
{
	/**
	 * De positie van het blok.
	 */
	int X, Y;
	
	/**
	 * De richting waarin het blok beweegt.
	 */
	Directions Direction;

	/**
	 * Geeft aan of het blok beweegt.
	 */
	bool IsMoving;
};

Dit levert een probleem op. De enum Directions is namelijk gedefinieerd in Player.h en de compiler geeft dus een foutmelding. We kunnen Player.h natuurlijk includen in Block.h, maar dat is niet zo netjes. Immers een blok is helemaal niet afhankelijk van een speler. We maken dus een aparte header file voor Directions. We noemen het bestand Movement.h; hierin kunnen we alle code kwijt die gedeeld wordt door objecten die kunnen bewegen.

Movement.h
/******************************************************************************
	Bestand:   Movement.h
	Project:   Pengo

	Copyright: (c) 2003 Joost Ronkes Agerbeek

	Auteur:    Joost Ronkes Agerbeek <joost@ronkes.nl>
	Datum:     19 januari 2003
******************************************************************************/

#ifndef __MOVEMENT_H__
#define __MOVEMENT_H__

/******************************************************************************
	Enums
******************************************************************************/

/**
 * De bewegingsrichtingen van een speler.
 */
enum Directions
{
	Up,
	Down,
	Left,
	Right
};

#endif

Je moet nu de enum Directions verwijderen uit Player.h en Movement.h includen in zowel Player.h als Block.h.

Bij het aanmaken van een nieuw blok, staat het blok natuurlijk stil, dus dat nemen we op in CreateBlock.

Block.cpp
myBlock.IsMoving = false;

Nu nog de functie MoveBlock die het blok ook echt verplaatst. Vergeet niet de functie declaratie op te nemen in Block.h.

Block.cpp
/**
 * Verplaatst het blok één positie.
 *
 * @param	block	het blok dat verplaatst moet worden
 */
void MoveBlock(Block& block)
{
	// beweegt het blok?
	if (block.IsMoving)
	{
		// ja, in welke richting beweegt het blok?
		switch (block.Direction)
		{
		case Up:
			{
				// kan blok nog omhoog?
				if (block.Y > 0)
				{
					// ja, verplaats blok omhoog
					block.Y--;
				}
				else
				{
					// nee, zet blok stil
					block.IsMoving = false;
				}
			} break;
		case Down:
			{
				// kan blok nog omlaag?
				if (block.Y < 24)
				{
					// ja, verplaats blok omlaag
					block.Y++;
				}
				else
				{
					// nee, zet blok stil
					block.IsMoving = false;
				}
			} break;
		case Left:
			{
				// kan blok nog naar links?
				if (block.X > 0)
				{
					// ja, verplaats blok naar links
					block.X--;
				}
				else
				{
					// nee, zet blok stil
					block.IsMoving = false;
				}
			} break;
		case Right:
			{
				// kan blok nog naar rechts?
				if (block.X < 79)
				{
					// ja, verplaats blok naar rechts
					block.X++;
				}
				else
				{
					// nee, zet blok stil
					block.IsMoving = false;
				}
			} break;
		}
	}
}

En tot slot zetten we het blok in beweging en roepen we MoveBlock aan vanuit de game loop.

Main.cpp
// maak blok aan
Block myBlock = CreateBlock(30, 11);
myBlock.IsMoving = true;
myBlock.Direction = Left;
Main.cpp
// verwijder blok van scherm
MoveCursor(myBlock.X, myBlock.Y);
cout << " ";

// verplaats het blok
MoveBlock(myBlock);

// teken block
DrawBlock(myBlock);
[ Naar boven | Terug naar Pengo ]

Schoppen

Voordat je nou denkt dat we ineens met een kaartspel bezig zijn, het gaat hier om Pengo die tegen een blok aan schopt. Ten eerste moeten we een toets toevoegen waarmee de speler Pengo kan laten schoppen. Net als alle andere toetsen zetten we deze in een constante.

Main.cpp
const int PengoKick  = VK_SPACE;

Nu we de toets bepaald hebben, moeten we ook op deze toets reageren. Dit doen we door de functie Kick aan te roepen, die we straks gaan schrijven. Eerst maar even de toets afhandelen.

Main.cpp
case PengoKick:
	{
		// laat speler schoppen
		Kick(myPlayer, myBlock);
	} break;

De functie Kick krijgt twee parameters mee: de speler die schopt en het blok dat op het veld staat. Het eerste dat de functie doet, is controleren of de speler het blok raakt. Daarbij houden we rekenen met de richting waarin de speler kijkt, want dat is ook de richting waarin hij schopt.

Player.cpp
/**
 * Laat de speler schoppen.
 *
 * @param	player	de speler die schopt
 * @param	block	het blok waar de speler tegenaan kan schoppen
 */
void Kick(const Player& player, Block& block)
{
	// in welke richting schopt de speler?
	switch (player.Direction)
	{
	case Up:
		{
			// schopt speler tegen blok aan?
			if ((player.X == block.X) & ((player.Y - 1) == block.Y))
			{
				// ja, zet blok in beweging
				block.Direction = Up;
				block.IsMoving  = true;
			}
		}
	case Down:
		{
			// schopt speler tegen blok aan?
			if ((player.X == block.X) & ((player.Y + 1) == block.Y))
			{
				// ja, zet blok in beweging
				block.Direction = Down;
				block.IsMoving  = true;
			}
		}
	case Left:
		{
			// schopt speler tegen blok aan?
			if (((player.X  - 1) == block.X) & (player.Y == block.Y))
			{
				// ja, zet blok in beweging
				block.Direction = Left;
				block.IsMoving  = true;
			}
		}
	case Right:
		{
			// schopt speler tegen blok aan?
			if (((player.X  + 1) == block.X) & (player.Y == block.Y))
			{
				// ja, zet blok in beweging
				block.Direction = Right;
				block.IsMoving  = true;
			}
		}
	}
}

Omdat Kick een parameter van het type Block meekrijgt, moet je Block.h includen. De parameter block kan in deze functie gewijzigd worden, maar de parameter player niet. We maken player dus constant door er het woord const voor te zetten. Neem de declaratie van de functie op in Player.h.

[ Naar boven | Terug naar Pengo ]

Conclusie

En dat is het voor deze keer. We hebben gezien hoe we de code voor het bewegen van de speler kunnen toepassen voor het bewegen van een blok. Bovendien hebben we ervoor gezorgd dat de speler tegen een blok aan kan schoppen.

De volgende keer houden we ons bezig met het programmeren van het level. Tot die tijd raad ik je aan om veel te experimenteren met de code die we tot nu toe hebben geschreven. Voer wat wijzigingen door en zie wat het effect is.

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 ]

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