Nieuws — Lesstof — Pengo — Projecten — Console API — Links
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.
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.
/****************************************************************************** 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.
/****************************************************************************** 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; }
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.
/** * 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); }
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.
// 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.
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.
/** * 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; };
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.
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.
// 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); }
Als het level getekend wordt, moeten nu ook de vijanden getekend worden. We breiden DrawLevel dus uit met de volgende code.
// 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. :-)
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. :-)
Als je de bovenstaande code af hebt en je wilt Pengo graag uitbreiden, dan kun je de volgende features toevoegen.
Het volgende bestand hoort bij deze les.