Programmera spel i C++ för nybörjare/Sprites och spelpjäser 5
(Genomgången förutsätter att du har en fungerande installation av Microsoft Visual C++ 2010 Express och SFML 1.6 på din dator.)
Nya kor
[redigera]Om man vill kunna skapa obegränsat med sprites måste man ha någon form av funktion där man kan gå igenom dem. Vanligtvis görs det med länkade listor eller vektorer, men enklast är att spara korna i en vanlig array. Orsaken till behovet av att lägga nya spelare i en lista kanske inte är helt självklar, men häng med till slutet så förstår du varför du faktiskt måste göra det. Sättet att skriva en lista på är:
class *array/listnamn[antal];
Vi vill ha en array av kopior av klassen spelare. I vårt fall vill vi ha 100 kor och koden blir alltså:
spelare *kolista[100];//max 100 kor på planen
För att kunna räkna fram och tillbaka i listan måste vi även ha två integers/heltal definierade:
int ko_i=0; //0-100 avgör var vi är i listan int i = 0; kontrollsiffra mot ko_i för att den inte skall rita upp fler kor än vad som står i kön. Då kraschar programmet.
Anta att vi skall ha en ny ko på spelplanen varje gång vi klickar på spacetangenten:
if (Event.Key.Code == sf::Key::Space) // space tangenten skapa kor { kolista[ko_i] = new spelare(50, 'c', 100.f, 400.f); kolista[ko_i]->ljud(kolista[ko_i]->ras); kolista[ko_i]->sprite.SetImage(kobild); kolista[ko_i]->sprite.SetPosition(kolista[ko_i]->spelare_x+100,kolista[ko_i]->spelare_y+100); ko_i++; }
I vanliga fall är vi vana med att spelarna har ett lättförståeligt namn. Det problem som uppstår här är ju att vi skapar nya spelare hela tiden, men istället för namn får de en ”kölapp” i listan. En plats i ledet. När vi vill att något skall hända utgår vi från den kölappen istället för figurens nanmn. Andra alternativet vore att ge varje spelare en tag, en märkning, och sedan använda märkningen för att utföra olika funktioner, men det är oändligt mycket krångligare.
Skapa en ko
[redigera]- När programmet startar är ko_i satt till 0.
- När spacetangenten trycks ner skapas därför en ko i kolista[0] med könummer 0.
- Den får fart 50, ras cow, placeras 100 in och 400 ner som standard.
- Den muuar (så man kan kontrollera att den fötts).
- Den får Bilden av en ko.
- Den placeras ut på spelplanen +100 i x och y led från den första positionen. Med slumptal hade blivit ändå intressantare.
- Därefter flyttas pekaren i kön till plats 1 i väntan på nästa ko.
Utritning
[redigera]När det sedan är dags att rita upp korna på spelplanen kommer funktionen vi tidigare gjorde till god hjälp
if (ko_i >0) {//det finns kor i kön while (i <= (ko_i -1) && ko_i < 100) //Inte fler än 100 { App.Draw(kolista[i]->sprite); //Rita ut kon med det könumret //skicka ut den att jaga pojken kojakt(pojke.sprite.GetPosition().x, pojke.sprite.GetPosition().y, kolista[i]->sprite.GetPosition().x, kolista[i]->sprite.GetPosition().y, &kolista[i]->sprite, ElapsedTime, kolista[i]->hastighet); i++; //räkna upp i till nästa steg i kön } }//Slut på kor i listan i=0; //återställ i till första platsen i kön, i får aldrig ha ett högre värde än ko_i
Radering
[redigera]När så spelet skall avslutas måste vi återställa minnet genom att radera kolistan. Lägg till följande rad under ESC-knapptryckningens funktion:
if (Event.Key.Code == sf::Key::Escape) // ESC tangenten = stäng programmet { while (i <= ko_i) //Så länge det finns kor i kön... {//Det finns kor delete kolista[ko_i]; //Återställ minnet. //destruktorn ~spelare kallas i++; //Gå till nästa ko i kön }// slut det finns kor App.Close(); //Stäng programmet }
Då kommer allt minne vi "tagit" för att skapa korna att återställas till systemet så att andra program mommer åt det.
Obs, enligt handböckerna borde det gå att skriva:
delete [] kolista;
istället för att räkna igenom hela listan och radera varje ko för sig. Av någon anledning ger det kommandot ett otrevligt felmeddelande medan genomräkningen fungerar bra.
Avslutning
[redigera]Nu har du sett hur man kan skapa spelpjäser som olika sorters klasser och hur man tillverkar obegränsat med spelpjäser utan att minnet tar slut. De här enkla funktionerna kan på många olika sätt byggas ut till riktigt avancerade spel. Objektorienterad programmering kan verka krångligt, men rätt använd kan den bli din absolut bästa vän.
Slutkoden för projektet
[redigera]#include <iostream> #include <SFML\System.hpp> #include <SFML\Graphics.hpp> #include <SFML\Window.hpp> using namespace std; //Klassdefinition---------------------------------------------------------------------------------- class spelare { public: //Konstruktordeklaration, definition utanför klassdeklarationen spelare (int hastighet, char ras, double spelare_x, double spelare_y);//startvärden //Destruktion ~spelare(){}; int hastighet; //Hur snabb är den double spelare_x; // var är den i sidled i programmet double spelare_y; //var är den i höjdled i programmet char ras; sf::Sprite sprite; //Funktionerna void spelare::ljud(char vem); }; //Konstruktionsdeklaration------------------------------------------------------------------------- spelare::spelare (int ut_hastighet, char ut_ras, double ut_spelare_x, double ut_spelare_y) { hastighet=ut_hastighet; ras=ut_ras; spelare_x=ut_spelare_x; spelare_y=ut_spelare_y; std::cout << "En spelare har fötts!" << endl; } //funktioner--------------------------------------------------------------------------------------- void spelare::ljud(char vem) { if (vem =='b') std::cout << "HJÄÄÄÄLP" << endl; if (vem == 'c') std::cout << "MUUUUUUUU!" << endl; } //--------------------------------------------------------------------------------------------------- //Slut på klassdeklaration //-------------------------------------------------------------------------------------------------- void kojakt(double spelare1_x, double spelare1_y, double spelare2_x, double spelare2_y, sf::Sprite *kospelare, float ElapsedTime, int kofart ); int main() { //Början av programkörningen sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test - klasser"); float ElapsedTime = 0.0f; //Skapar en konstant för att hålla hastigheten likvärdig på olika datorer //Bildhållare skapas sf::Image kobild; sf::Image pojkbild; spelare *kolista[100];//max 100 kor på planen int ko_i=0; //0-100 avgör var vi är i listan int i =0; //räknare kobild.LoadFromFile("cow.png"); pojkbild.LoadFromFile("boy.png"); kobild.CreateMaskFromColor(sf::Color(192,248,0)); //Skapa en ko spelare ko (50, 'c', 100.f, 400.f); ko.ljud(ko.ras); //Skriver ut MUUUUUUU! I det svarta konsollfönstret. ko.sprite.SetImage(kobild); ko.sprite.SetPosition(ko.spelare_x,ko.spelare_y); //Skapa en pojke spelare pojke(100, 'b', 100.f, 100.f); pojke.ljud('b'); //Skriver ut HJÄÄÄÄÄÄLP! I det svarta konsollfönstret. pojke.sprite.SetImage(pojkbild); pojke.sprite.SetPosition(pojke.spelare_x,pojke.spelare_y); //Skapa en ny ko spelare *ko0 = new spelare(50, 'c', 100.f, 400.f); ko0->ljud(ko0->ras); ko0->sprite.SetImage(kobild); ko0->sprite.SetPosition(ko0->spelare_x+100,ko0->spelare_y+100); while(App.IsOpened()) { sf::Event Event; ElapsedTime=App.GetFrameTime(); //Skapar en konstant för att hålla hastigheten likvärdig på olika datorer while (App.GetEvent(Event)) // Ta hand om händelser { //while 2 if (Event.Type == sf::Event::Closed) //kryssat på [x] symbolen? stäng programmet App.Close(); if (Event.Type == sf::Event::KeyPressed) // En tangent har tryckts ner { //if 1 if (Event.Key.Code == sf::Key::Escape) // ESC tangenten = stäng programmet { while (i <= ko_i) //Så länge det finns kor i kön... {//Det finns kor delete kolista[ko_i]; //Återställ minnet. //destruktorn ~spelare kallas i++; //Gå till nästa ko i kön }// slut det finns kor App.Close(); //stäng programmet } if (Event.Key.Code == sf::Key::Space) // space tangenten skapa ko { kolista[ko_i] = new spelare(50, 'c', 100.f, 400.f); kolista[ko_i]->ljud(kolista[ko_i]->ras); kolista[ko_i]->sprite.SetImage(kobild); kolista[ko_i]->sprite.SetPosition(kolista[ko_i]->spelare_x+100,kolista[ko_i]->spelare_y+100); ko_i++; } } //slut if 1 } //slut, while 2 //Flytta pojken if (App.GetInput().IsKeyDown(sf::Key::Left)) pojke.sprite.Move(-pojke.hastighet * ElapsedTime, 0); if (App.GetInput().IsKeyDown(sf::Key::Right)) pojke.sprite.Move( pojke.hastighet * ElapsedTime, 0); if (App.GetInput().IsKeyDown(sf::Key::Up)) pojke.sprite.Move(0, -pojke.hastighet * ElapsedTime); if (App.GetInput().IsKeyDown(sf::Key::Down)) pojke.sprite.Move(0, pojke.hastighet * ElapsedTime); //Flytta korna--------------------------------------------------- kojakt(pojke.sprite.GetPosition().x, pojke.sprite.GetPosition().y, ko.sprite.GetPosition().x, ko.sprite.GetPosition().y, &ko.sprite, ElapsedTime, ko.hastighet); kojakt(pojke.sprite.GetPosition().x, pojke.sprite.GetPosition().y, ko0->sprite.GetPosition().x, ko0->sprite.GetPosition().y, &ko0->sprite, ElapsedTime, ko0->hastighet); // Rensa skärmen App.Clear(sf::Color(0, 255, 0)); //rensa allt i fönstret och ersätt med grönfärg //updatera animation App.Draw(pojke.sprite); App.Draw(ko.sprite); App.Draw(ko0->sprite); //Uppdatera animationen för alla kor som skapats med "new" if (ko_i >0) {//det finns kor i listan while (i <= (ko_i -1) && ko_i < 100) { App.Draw(kolista[i]->sprite); kojakt(pojke.sprite.GetPosition().x, pojke.sprite.GetPosition().y, kolista[i]->sprite.GetPosition().x, kolista[i]->sprite.GetPosition().y, &kolista[i]->sprite, ElapsedTime, kolista[i]->hastighet); i++; } }//Slut på kor i listan i=0; //Nollställ räknaren //Visa upp alla förändringar för spelaren App.Display(); } return 0; } //slut på programkörningen void kojakt(double spelare1_x, double spelare1_y, double spelare2_x, double spelare2_y, sf::Sprite *kospelare, float ElapsedTime, int kofart ) {//spelare 1 är pojken spelare 2 en ko if (spelare2_x <= spelare1_x) //pojken har sprungit förbi kon kospelare->Move(kofart * ElapsedTime, 0); if (spelare2_x > spelare1_x) //pojken har inte sprungit förbi kon kospelare->Move( -kofart * ElapsedTime, 0); if (spelare2_y > spelare1_y ) //kon är nedanför pojken kospelare->Move(0, -kofart * ElapsedTime); if (spelare2_y <= spelare1_y ) //kon är ovanför pojken kospelare->Move(0, kofart * ElapsedTime); } //Slut på funktionen