Programmera spel i C++ för nybörjare/Sprites och spelpjäser 5

Från Wikibooks


(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