Vijanden

Pengo © 2002-2003, Joost Ronkes Agerbeek

De les van deze keer zit vol gevaar, want we gaan de vijanden programmeren. Eigenlijk valt het allemaal wel mee, want de meeste code lijkt erg op wat we gedaan hebben met de speler en de blokken. We moeten de vijanden kunnen opslaan in level.dat, maar ook dat zal wel lukken. Let's get coding.

Datastructuur

We slaan de gegevens van een vijand op in - jawel - een struct. Een vijand heeft een positie en hij kan bewegen. Dat hebben we eerder gezien. Uiteraard komt de code in twee nieuwe bestanden: Enemy.h en Enemy.cpp.

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

	Copyright: (c) 2003 Joost Ronkes Agerbeek

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

#ifndef __ENEMY_H__
#define __ENEMY_H__

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

/**
 * Een vijand.
 */
struct Enemy
{
	/**
	 * De positie van de vijand.
	 */
	int X, Y;

	/**
	 * Geeft aan of de vijand beweegt.
	 */
	bool IsMoving;
};

#endif

Het enige opvallende aan deze structure is dat Direction ontbreekt. Het is namelijk niet van belang in welke richting de vijand beweegt: de speler heeft er geen controle over en de vijand kan niet tegen blokken schoppen. Niet overtuigd? Geloof me toch maar; het wordt duidelijk als we de vijand gaan verplaatsen.

We hebben een functie nodig die een vijand kan maken. Ook dit is voornamelijk knip- en plakwerk. Denk aan de functiedefinitie in Enemy.h.

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

	Copyright: (c) 2003 Joost Ronkes Agerbeek

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

#include "Enemy.h"

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

/**
 * Maakt een nieuwe vijand aan en initialiseert de gegevens van de vijand.
 *
 * @param	x	de x-coordinaat van de te maken vijand
 * @param	y	de y-coordinaat van de te maken vijand
 *
 * @return	een nieuwe vijand
 */
Enemy CreateEnemy(int x, int y)
{
	// maak nieuwe vijand
	Enemy myEnemy;

	// stel standaardwaarden in
	myEnemy.X = x;
	myEnemy.Y = y;
	myEnemy.IsMoving = false;

	// geef blok terug
	return myEnemy;
}
[ Naar boven | Terug naar Pengo ]

Tekenen

Om de vijanden te kunnen tekenen, hebben we een paar constanten nodig. Hierin slaan we het ASCII-teken dat een vijand voorstelt op en de kleuren. Dit alles komt in Graphics.cpp te staan.

Graphics.cpp
/**
 * Het ASCII-teken dat een vijand voorstelt.
 */
const char EnemyCharacter = 15;

/**
 * De kleuren van een vijand.
 */
const Color EnemyForegroundColor = Red;
const Color EnemyBackgroundColor = Black;

Nu nog de functie om een vijand te tekenen. Ook nu weer: vergeet niet om de functiedefinitie toe te voegen aan Graphics.h.

/**
 * Tekent een vijand naar het scherm.
 *
 * @param	enemy	de vijand die getekend moet worden
 */
void DrawEnemy(const Enemy& enemy)
{
	// teken vijand
	WriteText(EnemyCharacter, enemy.X, enemy.Y, EnemyForegroundColor,
		EnemyBackgroundColor);
}
[ Naar boven | Terug naar Pengo ]

Even testen

Door de volgende code in main te zetten, kun je testen of tot nu toe alles goed is gegaan. De code maakt een vijand aan en tekent hem naar het scherm. Zet de code vlak boven de game loop.

Main.cpp
// maak vijand aan
Enemy myEnemy = CreateEnemy(10, 10);

// teken vijand
DrawEnemy(myEnemy);

Als dit allemaal goed gaat, kun je de code meteen weer weghalen. We slaan de vijanden namelijk op bij het level.

[ Naar boven | Terug naar Pengo ]

Vijanden en levels

Een level bevat vijanden, dus we moeten de struct Level zo aan passen, dat we er vijanden in kunnen plaatsen. Hiervoor voegen we een vector toe.

Level.h
/**
 * Een level.
 */
struct Level
{
	// de grootte van het level
	int Width, Height;

	// de blokken in het level
	vector<Block> Blocks;

	// de vijanden in het level
	vector<Enemy> Enemies;

	// de speler
	Player Player;
};
[ Naar boven | Terug naar Pengo ]

Bestand aanpassen

We moeten een manier verzinnen om de startpositie van de vijanden op te slaan in het bestand level.dat. Dat kan op verschillende manier, maar ik heb gekoezen voor het volgende.

Na de levelinformatie slaan we op hoeveel vijanden er in het level voorkomen. Daarna volgen de coördinaten waar de vijanden beginnen. Met twee vijanden ziet level.dat er als volgt uit.

Level.dat
40 20

1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

2

10 10
30 15

Uiteraard moeten we de functie LoadLevel aangepast worden om ervoor te zorgen dat de vijanden ook geladen worden. Denk eraan dat eerst de blokken geladen moeten worden en dat je daarachter deze code moet toegevoegen.

Level.cpp
// lees aantal vijanden
int myEnemyNumber;

myFile >> myEnemyNumber;

// lees vijanden in
for (int i = 0; i < myEnemyNumber; i++)
{
	// lees startpositie in
	int myX, myY;

	myFile >> myX;
	myFile >> myY;

	// maak nieuwe vijand aan
	Enemy myEnemy = CreateEnemy(myX, myY);

	// voeg vijand toe aan level
	myLevel.Enemies.push_back(myEnemy);
}
[ Naar boven | Terug naar Pengo ]

Nogmaals tekenen

Als het level getekend wordt, moeten nu ook de vijanden getekend worden. We breiden DrawLevel dus uit met de volgende code.

Graphics.cpp
// doorloop alle vijanden in het level
for (int j = 0; j < level.Enemies.size(); j++)
{
	// teken vijand
	DrawEnemy(level.Enemies.at(j));
}

Compileren, starten en dat is het. De vijanden staat nog stil en dat maakt het spel lekker makkelijk, dus ik laat het voorlopig zo. :-)

[ 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 en de blokken kunnen aanpassne voor het bewegen van de vijanden blok. Bovendien hebben we ervoor gezorgd dat de vijanden opgeslagen zijn in level.dat.

Alle elementen van het spel zijn inmiddels aanwezig. In de volgende lessen zullen allerlei onderwerpen aanpakken die ik tot nu toe heb laten liggen. De eerstvolgende keer zien we hoe we botsingen kunnen detecteren en hoe we op botsingen moeten reageren.

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

Het volgende bestand hoort bij deze les.

[ Naar boven | Terug naar Pengo ]

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