Hoppa till innehållet

Programmera spel i C++ för nybörjare/Enkelt stridsvagnspel

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.)

När animationen flyttas ut i en funktion avstannar hela spelet när funktionen visas upp, och det är inte så kul. I det här kapitlet skall vi skapa en stridsvagn som åker runt och skjuter och när kulorna kommer 50 pixel från en kant i spelet exploderar dem med en knall. Det här är alltså inget spel i sig utan en demonstration över hur animation och rörelser kan kombineras, så att du själv kan bygga ut koden till ett eget spel.

Nu skall vi för första gången koppla ihop allt vi lärt oss.

  • Vi skall skapa en klass för fordon.
  • Vi skall skapa en stridsvagn genom att ärva från fordon.
  • Vi skall skapa en klass för explosionen.
  • Vi skall skapa en klass för skotten.
  • Stridsvagnen flyttas med mustangenten och roterar själv i den riktning vi klickar med musen.
  • När vi trycker ner space-tangenten skjuter stridsvagnen.
  • Då skapas ett skott och färdas en viss sträcka från stridsvagnen
  • När skottet nått hela sträckan försvinner skottet och en explosion syns istället.

Den grafik vi behöver är: en pansarvagn, en kula och en explosion.

Pansarvagn:

http://media.strategywiki.org/images/2/2a/MH_Tank.gif (måste göras om till png)


Kula: Eight-Ball-icon.png

http://www.iconarchive.com/show/symbolic-objects-icons-by-mozco/Eight-Ball-icon.html


Explosion:

http://cdn.pimpmyspace.org/media/pms/c/b9/9e/ez/xplosion17.png

De ljud vi behöver är: en liten knall när skottet avlossas och en större knall vid explosionen.

Liten knall : http://www.mediacollege.com/downloads/sound-effects/explosion/ (explosion 3)

Stor knall : http://www.mediacollege.com/downloads/sound-effects/explosion/ (bomb 3)


Sedan vi stulit/lånat kod från alla tidigare projekt har vi alltså något liknande detta:

((OBS i andra exempel var hastigheten en int, men det fungerade helt enkelt inte. Det fick bli 0.01 som standard och en double).


Skapa en explosionsklass

[redigera]

Det första vi måste göra är att utöka explosionsklassen. Vi måste ha olika lägen definierade och en spärr som slår till när animationen visats färdigt.

Klassen, i sin enklaste form, ser ut så här:

class Explosion
{
public:
Explosion(bool ut_playing, bool ut_has_played, double ut_starttid); //Konstruktion
~Explosion(){};//Destruktor

sf::Sprite Sprite; // en per instans
sf::Image Image; //bildfilshållaren

//var skall explosionen ske?
double target_x; 
double target_y;

//Var i sprite sheeten skall vi börja  kopiera bilder
int  topkoordinatX;
int  topkoordinatY;

double starttid; //Vid vilket ögonblick skall explosionen starta?
bool playing; //visas den upp?
bool has_played; //Har den visats färdigt?

void get_explosionsbild() //Ta ut rätt koordinater i sprite sheeten för visning av explosion
{
 //Ta fram rätt bild
 Sprite.SetSubRect(sf::IntRect(topkoordinatX,topkoordinatY, topkoordinatX + 64,topkoordinatY + 64));
 //Funktionen ger övre vänstra hörnets koordinater
 //då kan vi själva räkna ut nedre högra hörnet eftersom bilderna är 64 x 64 stora
}

};


Explosion::Explosion(bool ut_playing, bool ut_has_played, double ut_starttid) //Initiering
{
playing = ut_playing; //Avgör om animationen spelar. Ange false som standard
has_played = ut_has_played; //Ställs från funktion utanför klass, false från början
starttid = ut_starttid; //När börjar explosionen

//Ladda in rätt bild vid skapandet
if (!Image.LoadFromFile("xplosion17.png")) //Hämta bildfilen    
 {        
 std::cout << "Kan inte hitta bilden: xplosion17.png " << std::endl;  
 }
Sprite.SetImage(Image); //ge bilden till spriten 
}

Detta innebär att varje gång vi skapar en explosion med ”new” laddas bilden in på nytt i minnet. Korkat, jag vet, men jag vet ingen väg runt problemet. När explosionen börjar ställes "playing" till true och när explosionen har visats klart sätts "has_played" till true. På det viset kan animationen bara visas en enda gång. Startiden är viktig, för det är skillnaden mellan starttiden och den tid som spelet varit igång som avgör vilken bild i animationen som skall visas upp. "Target_x" och "target_y" är koordinaterna till den plats som explosionen skall stå och explodera på. Slutligen har vi funktionen: void get_explosionsbild() som vi använder för att "skära ut" rätt del av sprite sheeten beroende på hur lång tid som gått.

Listor

[redigera]
      //Lista för att spara skott i
      Pansarvagnsskott *skottlista[100];//max 100 skott på planen
      int skott_i=0; //0-100 avgör var vi är i listan
      int si =0; //räknare

      //Lista för att spara explosioner i
      Explosion *explosionslista[100];//max 100 explosioner på planen
      int exp_i=0; //0-100 avgör var vi är i listan
      int ei =0; //räknare

Vi har två listor i spelet. varje gång pansarvagnen skjuter skapas en kanonkula och en explosion samtidigt. Så länge kanonkulan syns, syns inte explosionen. När kanonkulan träffar ett mål (i det här fallet är det väggen i spelet) sätter vi kulans värde "kan_ses" till false så att den inte ritas ut och hastigheten till 0. Därefter placerar vi animationen på samma ställe och visar upp den. Hyfsta enkelt att förstå. Explosionen är inställd så att om man har fler än ett skott som exploderar nästan samtidigt stoppas ljudet och börjar om från början för varje skott. Nackdelen med att använda listor är att de fylls upp. Efter 100 skott kan stridsvagnen inte göra så mycket mer. Ett sätt att komma runt det problemet är att använda länkade listor (som man gjorde i C programmering förr) eller använda en "Vector" eller "Deque" istället (som C++ programmerare använder), men mer om det i senare kapitel.

Explosionsanimation

[redigera]
if (exp_i > 0)
{//det finns explosioner i listan
                                                                   
while (ei <= (exp_i -1) && exp_i < 100)
 { //Man har inte nått slutet på listan
 	   
 if (explosionslista[ei]->playing == true && explosionslista[ei]->has_played == false ) //man är under 26 bilder
   { 
   explosionsbildruta(spelklocka.GetElapsedTime(), *explosionslista[ei]); //Ge spritens bild rätt x och y koordinater, funktion
   explosionslista[ei]->get_explosionsbild(); //Välj rätt bild till explosionen, funktion inom klassen Explosion
   explosionslista[ei]->Sprite.SetPosition(explosionslista[ei]->target_x, explosionslista[ei]->target_y); //Placera ut den på rätt ställe
   App.Draw(explosionslista[ei]->Sprite); //Rita ut bilden på explosionen  
   }
 ei++; //Gå till nästa post på explosionslistan
 } //Man har inte nått slutet på listan
ei=0; //Nollställ ei igen så att du kan räkna upp listan en gång till
}//Slut på explosioner i listan

Det här är principen för animationer i spel. Man går igenom en lista med de sprites som har någon form av animation, har en funktion som väljer ut rätt bild baserat endera på tid eller på spelets framerate och som tilldelas spriten innan den visas upp.

  • Finns det explosioner i listan? I sådana fall skall de troligtvis ritas upp.
  • Kontrollera om varje explosion kan ses och inte har spelats upp färdigt. Om båda är true tas en bildruta fram till spriten. Jag använder funktionen: explosionsbildruta.
  • Funktionen skickar sitt värde till en annan funktion inuti klassen Explosion som i sin tur ritar upp sin egen sprites bild. Man hade kunnat ha en funktion som först tog fram x- och sedan y-värdet för att därefter rita upp rätt bild inne i klassens sprite, men det hade varit minst lika krångligt.
  • Explosionen placeras ut på den punkt kulan försvann.
  • Kö-räknaren nollställs så att den kan göra om hela proceduren vid nästa omgång, 1/60 sekund senare.

Slutligen har jag suttit och definierat varje ruta för sig vid animationen. Det går att göra så, men det är bevis för att man är en total nybörjare. Det går i alla fall hyfsat enkelt att se. Koordinaterna är två stycken, X och Y för övre vänstra hörnet. Detta går bra när samtliga bilder i den sprite sheet vi använder har exakt samma storlek. Om storleken skulle variera skulle vi vara tvungna att ange koordinaterna för neder högerhörn också.

void explosionsbildruta(double speltid, Explosion &Klassinstans) //Tar fram rätt bild i ett sprite sheet
{
  //De koordinater vi är ute efter
 int utx = 0;
 int uty = 0;

double tidsomgaott = speltid-Klassinstans.starttid; //Hur lång tid har det gått sedan explosionen startade
                                                   //Starttiden finns lagrad inuti klassen
double visningstid = 0.05; //0.05 sekund mellan varje bild
  • Speltid - starttid visar hur lång tid som gått av animationen. visningstiden avgör animationshastigheten.
  • utx och uty är koordinaterna i sprite sheeten

Koden för hur funktionen beräknar x- och y- koordinaterna är lång och ganska självförklarande, men på slutet kontrollerar vi om man är inne på ruta 26, dvs. om animationens samtliga 25 bilder har visats. Om så är fallet sätts klassens "has_played" till true och den explosionsanimationen kan inte spelas upp igen.

Inuti klassen finns:

void get_explosionsbild() //Ta ut rätt koordinater i sprite sheeten för visning av explosion
{
 //Ta fram rätt bild
 Sprite.SetSubRect(sf::IntRect(topkoordinatX,topkoordinatY, topkoordinatX + 64,topkoordinatY + 64));
 //Funktionen ger övre vänstra hörnets koordinater
 //då kan vi själva räkna ut nedre högra hörnet eftersom bilderna är 64 x 64 stora
}

Det är den funktionen som matas med utx och uty så att rätt bild kan visas upp.

Komplett kod

[redigera]
#include <iostream>
#include <SFML\System.hpp>
#include <SFML\Graphics.hpp>
#include <SFML\Window.hpp>
#include <SFML\Audio.hpp>

#define M_PI 3.14159265358979323846 /* pi som statisk konstant*/
#define SFML_STATIC //Se till så att det inte behövs extra DLL-filer
using namespace std;

//Class för skottet
class Pansarvagnsskott
{
public :
Pansarvagnsskott(double  ut_hastighet, double ut_vinkel, bool ut_kan_ses); //Konstruktion
~Pansarvagnsskott(){};
double target_x;
double target_y;
double hastighet;
double vinkel;
bool kan_ses;
sf::Sprite Sprite; // en per instans
sf::Image Image; //bildfilshållaren
};

Pansarvagnsskott::Pansarvagnsskott(double  ut_hastighet, double ut_vinkel, bool ut_kan_ses) 
{
if (!Image.LoadFromFile("Eight-Ball-icon.png")) //Hämta bildfilen    
 {        
 std::cout << "Kan inte hitta bilden: Eight-Ball-icon.png " << std::endl;  
 }
hastighet=ut_hastighet;
vinkel=ut_vinkel;
kan_ses = ut_kan_ses;
Sprite.SetRotation(vinkel);//Ge den rätt vinkel redan från start
Sprite.SetImage(Image); //ge bilden till spriten
}

class Explosion
{
public:
Explosion(bool ut_playing, bool ut_has_played, double ut_starttid); //Konstruktion
~Explosion(){};//Destruktor

sf::Sprite Sprite; // en per instans
sf::Image Image; //bildfilshållaren

//var skall explosionen ske?
double target_x; 
double target_y;

//Var i sprite sheeten skall vi börja  kopiera bilder
int  topkoordinatX;
int  topkoordinatY;

double starttid; //Vid vilket ögonblick skall explosionen starta?
bool playing; //visas den upp?
bool has_played; //Har den visats färdigt?

void get_explosionsbild() //Ta ut rätt koordinater i sprite sheeten för visning av explosion
{
 //Ta fram rätt bild
 Sprite.SetSubRect(sf::IntRect(topkoordinatX,topkoordinatY, topkoordinatX + 64,topkoordinatY + 64));
 //Funktionen ger övre vänstra hörnets koordinater
 //då kan vi själva räkna ut nedre högra hörnet eftersom bilderna är 64 x 64 stora
}

};

Explosion::Explosion(bool ut_playing, bool ut_has_played, double ut_starttid) //Initiering
{
playing = ut_playing; //Avgör om animationen spelar. Ange false som standard
has_played = ut_has_played; //Ställs från funktion utanför klass, false från början
starttid = ut_starttid; //När börjar explosionen

//Ladda in rätt bild vid skapandet
if (!Image.LoadFromFile("xplosion17.png")) //Hämta bildfilen    
 {        
 std::cout << "Kan inte hitta bilden: xplosion17.png " << std::endl;  
 }
Sprite.SetImage(Image); //ge bilden till spriten 
}


//En moderklass för alla fordon i spelet
class fordon 
{
public:
//Konstruktordeklaration, definition utanför klassdeklarationen
fordon(double  hastighet, double spelare_x, double spelare_y, int bensin, int pansar);//startvärden

//Destruktion
~fordon(){};

double  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
int bensin; //Hur långt kan den köra
int pansar; //Hur mycket tål den
sf::Sprite sprite; //Används i grafiskt läge
};


//Konstruktionsdeklaration, fordon--------------------------------------------------
fordon::fordon (double  ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar) 
{
   hastighet=ut_hastighet;
   spelare_x=ut_spelare_x;
   spelare_y=ut_spelare_y;
   bensin = ut_bensin;
   pansar = ut_pansar;
//Också vill vi se att det verkligen skapas ett fordon när underklasserna skapas
std::cout << "Ett fordon rullar ut från fabriken!" << std::endl; 
}


//Skapa en klass som ärver fordon
class stridsvagn : public fordon
{
public:
int ammunition; // Avgör hur många skott som finns i stridsvagnen.
stridsvagn(double  ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar):
fordon(ut_hastighet, ut_spelare_x, ut_spelare_y, ut_bensin, ut_pansar)
{
ammunition=100;
}

//Destruktion
~stridsvagn(){};

//Funktioner
void skott()
{
std::cout << "Skott kommer!" << std::endl;
ammunition = ammunition-1;
std::cout << "Skott kvar=" <<ammunition << std::endl;
}

};


void explosionsbildruta(double speltid, Explosion &Klassinstans);
//Funktion som tar fram x och y koordinat på sprite sheet för explosionsbilder
//och som stänger av animationen om fler än 25 bilder visas

bool check_hit(double x, double y);
//Funktion som kollar om kulan träffat ett mål

double  mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare); 
//Funktion som roterar pansarvagnen mot en speciell koordinat

//******************************************************PROGRAMSTART********************************************************

int main (int argc, char *argv)
{ //main startar
       //För att kunna styra animationer i spelet måste vi kunna hålla koll på tiden. 
      //Det finns många sätt att göra det på och det här är det sämsta
      //men det fungerar och är lätt att förstå.
      sf::Clock spelklocka; //Skapa en spelklocka
      spelklocka.Reset(); //Ställ den på 0

      //Övriga variabler
      double  explosionsstarttid = 0.0f;
      //bool visaexplosion = false; //skall den visas?

      //Lista för att spara skott i
      Pansarvagnsskott *skottlista[100];//max 100 skott på planen
      int skott_i=0; //0-100 avgör var vi är i listan
      int si =0; //räknare

	   //Lista för att spara explosioner i
      Explosion *explosionslista[100];//max 100 explosioner på planen
      int exp_i=0; //0-100 avgör var vi är i listan
      int ei =0; //räknare

      //Olika värden för att styra stridsvagnen
      float newx = 0.0f; //ditt stridsvagnen skall gå
      float newy = 0.0f; //dit stridsvagnen skall gå
      float speed = 2.0f; //stridsvagnens hastighet.
      double vinkel =0.0f; //Stridsvagnens vinkel


      //Skapa först en stridsvagn
     //stridsvagn(int ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar)
     stridsvagn stridsvagn1(0.01f, 100.0,100.0,100,100);
     std::cout << "Pansarvagnen har " << stridsvagn1.ammunition << " skott" << endl ;

            //Skapa bild
       sf::Image bild; //skapa en tom bildhållare som heter bild
       if (!bild.LoadFromFile("MH_Tank.png")) return EXIT_FAILURE; 
       //fyll den tomma bildhållaren med bilden MH_tank.png
       stridsvagn1.sprite.SetImage(bild);

     //nu har vi en stridsvagn, Placera ut stridsvagnen på spelplanen
       stridsvagn1.sprite.SetPosition(stridsvagn1.spelare_x,stridsvagn1.spelare_y);

       //Det är jobbigt att inte riktigt veta var mitten är på stridsvagnen
       //Ändra positionen där stridsvagnen är från övre vänstra hörnet till mitt på bilden.
       stridsvagn1.sprite.SetCenter(stridsvagn1.sprite.GetSize().x / 2 , stridsvagn1.sprite.GetSize().y / 2);

    //Ladda in de två ljuden
       //Ljud för att avlossa kanonen
     sf::SoundBuffer kanonljud; //skapa en ljudbuffer/hållare
     if (!kanonljud.LoadFromFile("explosion-03.wav")) //ladda in en fil i hållaren
     {
     std::cout << "Kan inte hitta ljudfilen: explosion-03.wav " << endl; 
     }
     sf::Sound kanoneffekt; //skapa ett ljud i spelet för kanonen som vi döper till kanoneffekt
     kanoneffekt.SetBuffer(kanonljud); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.

      //Ljud när granaten exploderar
     sf::SoundBuffer explosionsljud; //skapa en ljudbuffer/hållare
     if (!explosionsljud.LoadFromFile("bomb-03.wav")) //ladda in en fil i hållaren
     {
     std::cout << "Kan inte hitta ljudfilen: bomb-03.wav " << endl; 
     }
     sf::Sound ljudeffekt; //skapa ett ljud i spelet som vi döper till ljudeffekt
      ljudeffekt.SetBuffer(explosionsljud); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.

	/**************  Programstart ***********************************************************/

   sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av pansarvagn"); 
  // Skapa fönstret vi skall testa explosionerna i

  while (App.IsOpened())
     { //while 1 startar, spelet körs
		
       sf::Event Event; //kolla om mus/tangentbord används
       while (App.GetEvent(Event))
         { //while 2 börjar, kontrollerar händelser
			
          if (Event.Type == sf::Event::MouseButtonPressed) // En musknapp har tryckts ner
         {//musknapp, vilken som helst
          if (Event.MouseButton.Button == sf::Mouse::Left)
            { //vänster musknapp
            //Vrid stridsvagnen i rätt vinkel
           mot_koordinat(stridsvagn1.sprite, App, App.GetInput().GetMouseX(), App.GetInput().GetMouseY(), 90);
           stridsvagn1.spelare_x = App.GetInput().GetMouseX(); //Ge stridsvagnen en X-koordinat att gå mot
           stridsvagn1.spelare_y = App.GetInput().GetMouseY(); //Ge stridsvagnen en Y-koordinat att gå mot
           } //vänster musknapp
          }//slut mus, vilken knapp som helst



          if (Event.Type == sf::Event::Closed) //kryssat på [x] symbolen? stäng programmet
          App.Close();

          if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
          App.Close();//avsluta programmet om man klickar på ESC


		   /************************* Kanonskott *******************************************************************************************/
		   // Här kommer koden för att avlossa kanonen!
		   // Ett explosionsljud spelas upp
		   // Mängden ammunition i stridsvagnen räknas ner ett steg
		   // En ny kanonkula skapas och läggs in i en array
		   // Kanonkulans mittpunkt sätts mitt på kulan istället för i övre högra hörnet
		   // Kanonkulan har sin mittpunkt på samma punkt som pansarvagnen har sin
		   // För varje kula skapar vi en potentiell explosion med samma indexnummer
		   // Explosionens mittpunkt placeras på samma ställe som kulans mittpunkt
		   // Räknaren skott_i flyttar ett steg upp och väntar på nästa kula
		   // Slutligen synkroniseras skottlistan med explosionslistan
		   /********************************************************************************************************************/

          if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Space))
          {//Spacetangent nertryckt
          kanoneffekt.Play(); //spela upp kanonljudet
          stridsvagn1.skott();    //Skjut med stridsvagnens funktion               
          skottlista[skott_i] =  new Pansarvagnsskott(1.0f,stridsvagn1.sprite.GetRotation(), true); 
		                         //Skapa ett nytt skott med:
                                //hastighet 1.0f och vinkel exakt samma som stridsvagnen och tru e= den syns.
          skottlista[skott_i]->Sprite.SetCenter(skottlista[skott_i]->Sprite.GetSize().x / 2 , skottlista[skott_i]->Sprite.GetSize().y / 2); //Sätt koordinaten mitt på 
          skottlista[skott_i]->Sprite.SetPosition(stridsvagn1.sprite.GetPosition().x,stridsvagn1.sprite.GetPosition().y); //Placera ut den på samma punkt som pansarvagnen

		   //Samtidigt som vi skapar ett skott måste vi skapa en explosion för skottet. Alla skott har en möjlighet att explodera.
		   explosionslista[skott_i] =  new Explosion(false, false, spelklocka.GetElapsedTime());
		   //Den skall inte visas ännu, den har inte spelats klart men starttiden är skapelseögonblicket

		   //Detta fungerar inte, mittpunkten måste kodas med siffror av någon anledning
		   //explosionslista[skott_i]->Sprite.SetCenter(explosionslista[skott_i]->Sprite.GetSize().x / 2 , explosionslista[skott_i]->Sprite.GetSize().y / 2);
		   explosionslista[skott_i]->Sprite.SetCenter(32,32); //Hela bilden är 64 x 64
          skott_i++; //Räkna upp till nästa plats i kön för nästa skott 
		   exp_i = skott_i; //Synkronisera de två listorna (skott och explosioner) så att de alltid pekar på samma postnummer i 
		                    //båda listorna. Det behövs sedan för att rita ut korrekt animation på rätt plats
           }//Spacetangent nertryckt

        } //while 2 slutar

              //Slutligen visar vi upp ändringarna om och om igen många gånger i sekunden
              App.Clear(sf::Color(0, 100, 0)); //rensa allt i fönstret och ersätt med grönfärg

			   /**************************** Flytta på skotten på spelplanen ****************************************/
                if (skott_i > 0)
               {//det finns skott i listan
                                                                   
				 while (si <= (skott_i -1) && skott_i < 100)
                { //Man har inte nått slutet på listan
                  //Flytta skotten
                  vinkel = skottlista[si]->vinkel; // 0 = rakt upp, startposition. Motsols 0-360 grader.
                  newx= sin(vinkel*M_PI/180.0) * skottlista[si]->hastighet; //Flytt i x-led
                  newy= cos(vinkel*M_PI/180.0) * skottlista[si]->hastighet; //Flytt i Y-led
                  skottlista[si]->Sprite.Move(newx*-1, newy*-1); //gånger * -1 eftersom rakt upp är 0 och inte 90 grader 

				   if (check_hit(skottlista[si]->Sprite.GetPosition().x, skottlista[si]->Sprite.GetPosition().y) == true)
					   //Man befinner sig 50 pixlar från kanten med kulan, dags för en explosion
				   {//träff
					   //******************************************************************************/
					   // När en explosion initieras sker det under flera steg
					   // Kom ihåg att indexet/platsen i listan är samma för kulan som för explosionen
					   //
					   // För att inte orsaka en explosion som låter hela tiden kontrollerar vi
					   // om kulan är i rörelse, om så är fallet låter explosionen. Den är tyst när hastighet=0
					   // och kulan inte är i rörelse längre
					   //
					   // Gör så att animationen av explosionen kan ses
					   // Ge samma koordinater till explosionen som kulan har
					   // Vi måste alltså trolla bort kanonkulan. Vi gör helt enkelt så att den inte kan ses
					   // Sätt sedan kulans hastighet till 0, så att den inte åker omkring med explosionen
					   // Spela upp explosionsljudet
					   // 
					   // Slutligen flyttar vi på kulan om den kan ses
					   // 
					   // Gå fram i kön till nästa kula och gör samma sak på den.
					   // När du gått igenom hela kön ställer vi si på 0 så att vi kan gå igenom hela listan igen nästa varv
					   //***************************************************************************/

					   //Enklast möjligt; samma explosionsljud används hela tiden
					   if (skottlista[si]->hastighet > 0)
					   {//kulan är i rörelse
						explosionslista[si]->playing = true; //börja spela upp animationen
					   	explosionslista[si]->target_x = skottlista[si]->Sprite.GetPosition().x; //x och y
						explosionslista[si]->target_y = skottlista[si]->Sprite.GetPosition().y; 

					   	if(ljudeffekt.GetStatus() != sf::Sound::Status::Playing)
						  {
						   ljudeffekt.Play(); //spela upp ljudet om den inte spelas redan
						  // while(ljudeffekt.GetStatus() == sf::Sound::Status::Playing) {}
						  }	
						else
						  {
							ljudeffekt.Stop(); //stoppa och påbörja ljudet igen
							ljudeffekt.Play();
							//while(ljudeffekt.GetStatus() == sf::Sound::Status::Playing) {}
						  }
					   }//kulan är i rörelse

					   skottlista[si]->kan_ses=false; //Gör så att kulan försvinner
					   skottlista[si]->hastighet = 0; //ge kulan hastighet 0

                  }//träff 
				   //Rita ut kanonkulebilden om den inte har träffat
				   if (skottlista[si]->kan_ses==true)
                  App.Draw(skottlista[si]->Sprite); 

				   si++; //Gå framåt i kön till nästa kula
				   } //Man har inte nått slutet på listan
              si=0; //Nollställ si igen så att du kan räkna upp listan en gång till             
             }//Slut på skott i listan

			  
                //Var skall pansarvagnen gå?
                vinkel = stridsvagn1.sprite.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader.
                newx= sin(vinkel*M_PI/180.0) * stridsvagn1.hastighet;
                newy= cos(vinkel*M_PI/180.0) * stridsvagn1.hastighet; 
                stridsvagn1.sprite.Move(newx*-1, newy*-1); //Multiplikation * -1 eftersom rakt upp är 0 och inte 90 grader 
                App.Draw(stridsvagn1.sprite); //Rita upp stridsvagnen
                 
//********** Visa upp animationerna*******************************************************************************************//

// Dags att rita upp animationerna av explosionerna
// Vi gör det sist av allt så att explosionerna ligger ovanpå
// både kanonkulor och stridsvagnar.
//
// Precis som vi går igenom listan av kulor som skall flyttas
// går vi igenom listan av explosioner för att se om värdet "playing" är true/sant
// och om den inte spelats upp klart. Om båda är sant visas bildrutan upp
//
// Funktionen "explosionsbildruta" används för att välja ut rätt x/y koordinater
// för rätt bild i sprite sheeten. 

		if (exp_i > 0)
               {//det finns explosioner i listan
                                                                   
	            while (ei <= (exp_i -1) && exp_i < 100)
               { //Man har inte nått slutet på listan
	   
		          if (explosionslista[ei]->playing == true && explosionslista[ei]->has_played == false ) //man är under 26 bilder
		           { 
		            explosionsbildruta(spelklocka.GetElapsedTime(), *explosionslista[ei]); //Ge spritens bild rätt x och y koordinater, funktion
                   explosionslista[ei]->get_explosionsbild(); //Välj rätt bild till explosionen, funktion inom klassen Explosion
	                explosionslista[ei]->Sprite.SetPosition(explosionslista[ei]->target_x, explosionslista[ei]->target_y); //Placera ut den på rätt ställe
	                App.Draw(explosionslista[ei]->Sprite); //Rita ut bilden på explosionen  
		           }
                ei++; //Gå till nästa post på explosionslistan
               } //Man har inte nått slutet på listan
              ei=0; //Nollställ ei igen så att du kan räkna upp listan en gång till
             }//Slut på explosioner i listan

//**********Slut på att visa upp animationerna*****************************************************

        App.Display(); //visa upp ändringarna för användaren
     } //while 1 slutar
  return 0;
}  //main slutar**********************************************************************************

//Funktioner som används i spelet

void explosionsbildruta(double speltid, Explosion &Klassinstans) //Tar fram rätt bild i ett sprite sheet
{
  //De koordinater vi är ute efter
 int utx = 0;
int uty = 0;

double tidsomgaott = speltid-Klassinstans.starttid; //Hur lång tid har det gått sedan explosionen startade
                                                   //Starttiden finns lagrad inuti klassen
double visningstid = 0.05; //0.05 sekund mellan varje bild
//25 bilder = värden mellan 0.25-1.25  vid 0.05

//Räkna ut Y koordinaten
if (tidsomgaott <= (visningstid * 5)) //rad 1
  uty = 0;
else if(tidsomgaott > visningstid * 5  && tidsomgaott <= visningstid *10)//rad 2
  uty = 64;
else if(tidsomgaott > visningstid * 10  && tidsomgaott <= visningstid *15)//rad 3
  uty = 128;
else if(tidsomgaott > visningstid * 15  && tidsomgaott <= visningstid *20)//rad 4
  uty = 192;
else if(tidsomgaott > visningstid * 20  && tidsomgaott <= visningstid *25)//rad 5
  uty = 256;

//Räkna ut x koordinaten

//Rad 1
if (tidsomgaott >= 0.0 && tidsomgaott < visningstid)
 utx = 0; //ruta 1
else if (tidsomgaott > visningstid && tidsomgaott < (visningstid * 2))
utx= 64;
else if (tidsomgaott > (visningstid * 2) && tidsomgaott < (visningstid * 3))
utx= 128;
else if (tidsomgaott > (visningstid * 3) && tidsomgaott < (visningstid * 4))
utx= 192;
else if (tidsomgaott > (visningstid * 4) && tidsomgaott < (visningstid * 5))
utx= 256;

//Rad 2
else if (tidsomgaott > visningstid * 5 && tidsomgaott < visningstid *6)
utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 6 && tidsomgaott < (visningstid * 7))
utx= 64;
else if (tidsomgaott > (visningstid * 7) && tidsomgaott < (visningstid * 8))
utx= 128;
else if (tidsomgaott > (visningstid * 8) && tidsomgaott < (visningstid * 9))
utx= 192;
else if (tidsomgaott > (visningstid * 9) && tidsomgaott < (visningstid * 10))
utx= 256;

//Rad 3
else if (tidsomgaott > visningstid * 10  && tidsomgaott < visningstid *11)
 utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 11 && tidsomgaott < (visningstid * 12))
utx= 64;
else if (tidsomgaott > (visningstid * 12) && tidsomgaott < (visningstid * 13))
utx= 128;
else if (tidsomgaott > (visningstid * 13) && tidsomgaott < (visningstid * 14))
utx= 192;
else if (tidsomgaott > (visningstid * 14) && tidsomgaott < (visningstid * 15))
utx= 256;

//Rad4
else if (tidsomgaott > visningstid * 15  && tidsomgaott < visningstid * 16)
 utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 16 && tidsomgaott < (visningstid * 17))
utx= 64;
else if (tidsomgaott > (visningstid * 17) && tidsomgaott < (visningstid * 18))
utx= 128;
else if (tidsomgaott > (visningstid * 18) && tidsomgaott < (visningstid * 19))
utx= 192;
else if (tidsomgaott > (visningstid * 19) && tidsomgaott < (visningstid * 20))
utx= 256;

//Rad5
else if (tidsomgaott > visningstid * 20  && tidsomgaott < visningstid *21)
 utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 21 && tidsomgaott < visningstid * 22)
utx= 64;
else if (tidsomgaott > visningstid * 22 && tidsomgaott < visningstid * 23)
utx= 128;
else if (tidsomgaott > visningstid * 23 && tidsomgaott < visningstid * 24)
utx= 192;
else if (tidsomgaott > visningstid * 24 && tidsomgaott <= visningstid * 25)
utx= 256;

else if (tidsomgaott > (visningstid * 25))
  Klassinstans.has_played = true; //Visa bara 25 bilder, sluta spela upp animationen

Klassinstans.topkoordinatX = utx; //skriv in koordinaterna direkt i klassen
Klassinstans.topkoordinatY = uty; //låt klassen beräkna bilden
}//funktionsslut

 
//Kolla om kulan har träffat en vägg***********************************************************
bool check_hit(double x, double y) //se om kulan träffat
{
bool kulan_har_traeffat = false;
if (x < 50 || x > 750)
	 kulan_har_traeffat = true;
if (y < 50 || y > 550)
	 kulan_har_traeffat = true;

return kulan_har_traeffat;
}
 
 
//Rotera stridsvagn mot en speciell koordinat *****************************************************************************************

double mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare)
{
double spritensvinkel; //Den vinkel vår sprite stannar på
double PI = 3.14159265358979323846; //Utifall att pi inte är definierad

if (position_X <= sprite.GetPosition().x) //Kollar om inte x eller y är likadana 
sprite.SetRotation  (((-1* 360 / PI * (atan2(static_cast<double> (sprite.GetPosition().y - position_Y) , static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); 
 else 
sprite.SetRotation  (((-1* 360 / PI *(atan2(static_cast<double> (sprite.GetPosition().y - position_Y) ,  static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); 

window.Draw(sprite); //Rita ut ändringen

spritensvinkel = sprite.GetRotation(); //ta fram vinkeln
// cout << spritensvinkel << endl; kontrollera vinkeln i konsollfönstret
return spritensvinkel; //Skicka tillbaka vinkeln 
}

//***************************************PROGRAMSLUT*****************************************************************//

Slutsats

[redigera]

Vad kan vi säga om det här? I enklare spel fungerar listor med ett fast antal poster bra. Får man bara litet känsla för hur animationer visas går det enkelt att använda listor. Har du ett spel där alla pjäser är kända när du börjar: 52 kort i en kortlek, 32 schackpjäser osv går det hur bra som helst och hålla ett spel på den här nivån. Problemet är att det inte går att radera de klasskopior som ligger i listorna, en minnesläcka bildas. För att komma runt det finns två sätt. Det ena är att använda länkade listor, ett rätt gammaldags och plåttrigt system. Det andra är att lägga in kopiorna i en "Vector" eller ett "Deque", olika moderna listtyper i C++ som kan växa obehindrat utan minnesläckor. Jag kommer bara att använda mig av dem i kommande kapitel.

Skall du ha mer än ett explosionsljud som låter samtidigt kan du endera skapa en bunt "Sound" som delar samma ljudfil. Kontrollera om ljudet i ett valt "Sound" inte spelas och i sådana fall spela upp det, eller skapa ett nytt "Sound" varje gång en granat exploderar. SFML klarar 512 stycken olika "Sound" kopior innan det kraschar. Även ljud kan läggas i "Vector" och "Deque", vilket underlättar speltillverkning en hel del.


Vector

[redigera]

Det finns ett tillägg till C++ som heter STL. I det paketet finns (bland annat) tre riktigt bra klasser som används just som kontainrar. Man har dem för att själv slippa hålla ordning på minneshanteringen. De som främst används är:

  • Vector = spelare läggs vanligen in i slutet av listan.
  • Deque = spelare läggs vanligen in i början eller slutet av listan
  • List = används vanligen som mellankontainer om man snabbt t.ex. vill sortera en vector.

För att visa fördelarna med en vector har jag skrivit om hela koden är ovanför så att den använder vectorer (med obegränsat minne) istället för arrays (som tar slut vid 100 skott enligt koden).

Det finns litet olika jämförelser mellan array och vector vi kan göra: När vi skapar en lista för skotten skriver vi:

Pansarvagnsskott *skottlista[100];

Men om vi skapar en vector skriver vi:

std::vector<Pansarvagnsskott> vSkottlista;

När vi lägger in nya poster i listan skriver vi:

skottlista[skott_i] =  new Pansarvagnsskott(1.0f,stridsvagn1.sprite.GetRotation(), true); //Lägger en kula i listan

på det gamla sättet men med en vector skriver vi:

vSkottlista.push_back( Pansarvagnsskott(1.0f,stridsvagn1.sprite.GetRotation(), true) ); //Lägger en kula i vector

Man använder inga -> tecken för att komma åt värdena i en vector. För att ställa om en klassinstans mittpunkt skriver man t.ex.:

vSkottlista.at(skott_i).Sprite.SetCenter(8,8); //Kanonkula. Sätt koordinaten mitt på, bilden är 16x16 stor

Jämför med hur man gör i en vanlig lista:

skottlista[skott_i]->Sprite.SetCenter(8,8);

När spelet är slut är det enkelt att radera hela vectorn med allt sitt innehåll med kommandot clear.

vSkottlista.clear();

Radera enstaka element i en vector

[redigera]

Man kan inte gå igenom en vector på samma sätt som man gör med en lista, kontrollera post efter post och radera de som motsvarar ett indexnummer. Det man får göra i en vector är att man noterar vilket indexnummer posten man står på har. Därefter ställer man sig på den första posten och sedan raderar man den post man sparat indexnumret till, med utgångspunkt från första posten. Krångligt?

Anta att vi vill radera post nummer fyra i en vector. Koden är då:

ei = 4;
vSkottlista.erase(vSkottlista.begin() + ei); //Radera motsvarande kanonkula

Slutligen: när jag modifierade koden från listor till vectorer blev all grafik vita rutor, bilderna saknades. För att komma runt det problemet laddas bilderna in i själva programmet istället för inuti klasserna.

Include vector

[redigera]

När man lägger till en vector kan det gå bra att inte lägga in någon "include" sats. Det finns andra headerfiler som kan inkludera den. I alla fall i visual c++ ingår STL containrarna som standard. Men, för att vara riktigt säker, bör du lägga till:

#include <vector>

Använder du ett deque får du lägga till den includesatsen också/istället.

#include <deque>

Komplett kod baserad på vector istället för array

[redigera]
#include <iostream>
#include <vector> //Lägg till när du använder en vector
#include <SFML\System.hpp>
#include <SFML\Graphics.hpp>
#include <SFML\Window.hpp>
#include <SFML\Audio.hpp>

#define M_PI 3.14159265358979323846 /* pi som statisk konstant*/
#define SFML_STATIC //Se till så att det inte behövs extra DLL-filer
using namespace std;

//Class för skottet
class Pansarvagnsskott
{
public :
Pansarvagnsskott(double  ut_hastighet, double ut_vinkel, bool ut_kan_ses); //Konstruktion
~Pansarvagnsskott(){};
double target_x;
double target_y;
double hastighet;
double vinkel;
bool kan_ses;
sf::Sprite Sprite; // en per instans
sf::Image Image; //bildfilshållaren
};

Pansarvagnsskott::Pansarvagnsskott(double  ut_hastighet, double ut_vinkel, bool ut_kan_ses) 
{
hastighet=ut_hastighet;
vinkel=ut_vinkel;
kan_ses = ut_kan_ses;
Sprite.SetRotation(vinkel);//Ge den rätt vinkel redan från start
Sprite.SetImage(Image); //ge bilden till spriten
//Obs, ingen image här - det ger vita rutaor i en vector
}

class Explosion
{
public:
Explosion(bool ut_playing, bool ut_has_played, double ut_starttid); //Konstruktion
~Explosion(){};//Destruktor

sf::Sprite Sprite; // en per instans
sf::Image Image; //bildfilshållaren

//var skall explosionen ske?
double target_x; 
double target_y;

//Var i sprite sheeten skall vi börja  kopiera bilder
int  topkoordinatX;
int  topkoordinatY;

double starttid; //Vid vilket ögonblick skall explosionen starta?
bool playing; //visas den upp?
bool has_played; //Har den visats färdigt?

void get_explosionsbild() //Ta ut rätt koordinater i sprite sheeten för visning av explosion
{
 //Ta fram rätt bild
 Sprite.SetSubRect(sf::IntRect(topkoordinatX,topkoordinatY, topkoordinatX + 64,topkoordinatY + 64));
 //Funktionen ger övre vänstra hörnets koordinater
 //då kan vi själva räkna ut nedre högra hörnet eftersom bilderna är 64 x 64 stora
}

};

Explosion::Explosion(bool ut_playing, bool ut_has_played, double ut_starttid) //Initiering
{
playing = ut_playing; //Avgör om animationen spelar. Ange false som standard
has_played = ut_has_played; //Ställs från funktion utanför klass, false från början
starttid = ut_starttid; //När börjar explosionen
Sprite.SetImage(Image); //ge bilden till spriten 
//Obs, ingen image här - det ger vita rutaor i en vector
}


//En moderklass för alla fordon i spelet
class fordon 
{
public:
//Konstruktordeklaration, definition utanför klassdeklarationen
fordon(double  hastighet, double spelare_x, double spelare_y, int bensin, int pansar);//startvärden

//Destruktion
~fordon(){};

double  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
int bensin; //Hur långt kan den köra
int pansar; //Hur mycket tål den
sf::Sprite sprite; //Används i grafiskt läge
};


//Konstruktionsdeklaration, fordon--------------------------------------------------
fordon::fordon (double  ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar) 
{
   hastighet=ut_hastighet;
   spelare_x=ut_spelare_x;
   spelare_y=ut_spelare_y;
   bensin = ut_bensin;
   pansar = ut_pansar;
//Också vill vi se att det verkligen skapas ett fordon när underklasserna skapas
std::cout << "Ett fordon rullar ut från fabriken!" << std::endl; 
}


//Skapa en klass som ärver fordon
class stridsvagn : public fordon
{
public:
int ammunition; // Avgör hur många skott som finns i stridsvagnen.
stridsvagn(double  ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar):
fordon(ut_hastighet, ut_spelare_x, ut_spelare_y, ut_bensin, ut_pansar)
{
ammunition=100;
}

//Destruktion
~stridsvagn(){};

//Funktioner
void skott()
{
std::cout << "Skott kommer!" << std::endl;
ammunition = ammunition-1;
std::cout << "Skott kvar=" <<ammunition << std::endl;
}

};


void explosionsbildruta(double speltid, Explosion &Klassinstans);
//Funktion som tar fram x och y koordinat på sprite sheet för explosionsbilder
//och som stänger av animationen om fler än 25 bilder visas

bool check_hit(double x, double y);
//Funktion som kollar om kulan träffat ett mål

double  mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare); 
//Funktion som roterar pansarvagnen mot en speciell koordinat

//******************************************************PROGRAMSTART********************************************************

int main (int argc, char *argv)
{ //main startar
       //För att kunna styra animationer i spelet måste vi kunna hålla koll på tiden. 
      //Det finns många sätt att göra det på och det här är det sämsta
      //men det fungerar och är lätt att förstå.
      sf::Clock spelklocka; //Skapa en spelklocka
      spelklocka.Reset(); //Ställ den på 0

      //Övriga variabler
      double  explosionsstarttid = 0.0f;
      //bool visaexplosion = false; //skall den visas?

 
     //Variabler för att hålla ordning i listorna
     int skott_i=0; // avgör var vi är i kanonkulelistan
      int si = 0; //räknare
	  int exp_i=0; //0-100 avgör var vi är i explosionslistan
      int ei = 0; //räknare

	   //Vector för pansarvagnsskott
	   std::vector<Pansarvagnsskott> vSkottlista;
	   //Vector för explosioner
	   std::vector<Explosion> vExplosionslista; 

	   //Bilderna för skotten, dels en granat och dels en explosion
	   //Ligger laddningen inom klassen skapas vita rutor istället

	sf::Image KanonkulaImage; //kanonkula
		if (!KanonkulaImage.LoadFromFile("Eight-Ball-icon.png")) //Programmet försöker att ladda in en bildfil i det tomma bildobjektet     
		std::cout << "Kan inte hitta bilden: Eight-Ball-icon.png " << std::endl;  

	sf::Image ExplosionImage; //explosion
		if (!ExplosionImage.LoadFromFile("xplosion17.png")) //Programmet försöker att ladda in en bildfil i det tomma bildobjektet      
		std::cout << "Kan inte hitta bilden: xplosion17.png " << std::endl; 

      //Olika värden för att styra stridsvagnen
      float newx = 0.0f; //ditt stridsvagnen skall gå
      float newy = 0.0f; //dit stridsvagnen skall gå
      float speed = 2.0f; //stridsvagnens hastighet.
      double vinkel =0.0f; //Stridsvagnens vinkel


      //Skapa först en stridsvagn
     //stridsvagn(int ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar)
     stridsvagn stridsvagn1(0.01f, 100.0,100.0,100,100);

            //Skapa bild
       sf::Image bild; //skapa en tom bildhållare som heter bild
       if (!bild.LoadFromFile("MH_Tank.png")) return EXIT_FAILURE; 
       //fyll den tomma bildhållaren med bilden MH_tank.png
       stridsvagn1.sprite.SetImage(bild);

     //nu har vi en stridsvagn, Placera ut stridsvagnen på spelplanen
       stridsvagn1.sprite.SetPosition(stridsvagn1.spelare_x,stridsvagn1.spelare_y);

       //Det är jobbigt att inte riktigt veta var mitten är på stridsvagnen
       //Ändra positionen där stridsvagnen är från övre vänstra hörnet till mitt på bilden.
       stridsvagn1.sprite.SetCenter(stridsvagn1.sprite.GetSize().x / 2 , stridsvagn1.sprite.GetSize().y / 2);

    //Ladda in de två ljuden
       //Ljud för att avlossa kanonen
     sf::SoundBuffer kanonljud; //skapa en ljudbuffer/hållare
     if (!kanonljud.LoadFromFile("explosion-03.wav")) //ladda in en fil i hållaren
     {
     std::cout << "Kan inte hitta ljudfilen: explosion-03.wav " << endl; 
     }
     sf::Sound kanoneffekt; //skapa ett ljud i spelet för kanonen som vi döper till kanoneffekt
     kanoneffekt.SetBuffer(kanonljud); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.

      //Ljud när granaten exploderar
     sf::SoundBuffer explosionsljud; //skapa en ljudbuffer/hållare
     if (!explosionsljud.LoadFromFile("bomb-03.wav")) //ladda in en fil i hållaren
     {
     std::cout << "Kan inte hitta ljudfilen: bomb-03.wav " << endl; 
     }
     sf::Sound ljudeffekt; //skapa ett ljud i spelet som vi döper till ljudeffekt
     ljudeffekt.SetBuffer(explosionsljud); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.

	/**************  Programstart ***********************************************************/

   sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av pansarvagn"); 
  // Skapa fönstret vi skall testa explosionerna i

  while (App.IsOpened())
     { //while 1 startar, spelet körs
		
       sf::Event Event; //kolla om mus/tangentbord används
       while (App.GetEvent(Event))
         { //while 2 börjar, kontrollerar händelser
			
          if (Event.Type == sf::Event::MouseButtonPressed) // En musknapp har tryckts ner
         {//musknapp, vilken som helst
          if (Event.MouseButton.Button == sf::Mouse::Left)
            { //vänster musknapp
            //Vrid stridsvagnen i rätt vinkel
           mot_koordinat(stridsvagn1.sprite, App, App.GetInput().GetMouseX(), App.GetInput().GetMouseY(), 90);
           stridsvagn1.spelare_x = App.GetInput().GetMouseX(); //Ge stridsvagnen en X-koordinat att gå mot
           stridsvagn1.spelare_y = App.GetInput().GetMouseY(); //Ge stridsvagnen en Y-koordinat att gå mot
           } //vänster musknapp
          }//slut mus, vilken knapp som helst



          if (Event.Type == sf::Event::Closed) //kryssat på [x] symbolen? stäng programmet
          App.Close();

          if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
		   { //Avsluta programmet
			   //Radera vectorer för att ge tillbaka minne
				vExplosionslista.clear();
				vSkottlista.clear();
				App.Close();//avsluta programmet om man klickar på ESC
		   } //avsluta programmet


		   /************************* Kanonskott *******************************************************************************************/
		   // Här kommer koden för att avlossa kanonen!
		   // Ett explosionsljud spelas upp
		   // Mängden ammunition i stridsvagnen räknas ner ett steg
		   // En ny kanonkula skapas och läggs in i en array
		   // Kanonkulans mittpunkt sätts mitt på kulan istället för i övre högra hörnet
		   // Kanonkulan har sin mittpunkt på samma punkt som pansarvagnen har sin
		   // För varje kula skapar vi en potentiell explosion med samma indexnummer
		   // Explosionens mittpunkt placeras på samma ställe som kulans mittpunkt
		   // Räknaren skott_i flyttar ett steg upp och väntar på nästa kula
		   // Slutligen synkroniseras skottlistan med explosionslistan
		   /********************************************************************************************************************/

           if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Space))
          {//Spacetangent nertryckt
          kanoneffekt.Play(); //spela upp kanonljudet
          stridsvagn1.skott();    //Skjut med stridsvagnens funktion       

		   //Nytt- vi använder vektorer
		   vSkottlista.push_back( Pansarvagnsskott(1.0f,stridsvagn1.sprite.GetRotation(), true) ); //Lägger en kula i vector
		   		                  //Skapa ett nytt skott med:
                                //hastighet 1.0f och vinkel exakt samma som stridsvagnen och true= den syns.
		   //vSkottlista.at(skott_i).Sprite.SetCenter(vSkottlista.at(skott_i).Sprite.GetSize().x / 2 , vSkottlista.at(skott_i).Sprite.GetSize().y / 2); //Sätt koordinaten mitt på 
		   vSkottlista.at(skott_i).Sprite.SetCenter(8,8); //Sätt koordinaten mitt på, bilden är 16x16 stor
		   //Kan även skrivas vSkottlista[skott_i].Sprite.SetCenter(vSkottlista.at(skott_i).Sprite.GetSize().x / 2 , vSkottlista.at(skott_i).Sprite.GetSize().y / 2); //Sätt koordinaten mitt på 
		   
		   vSkottlista.at(skott_i).Sprite.SetPosition(stridsvagn1.sprite.GetPosition().x,stridsvagn1.sprite.GetPosition().y); //Placera ut den på samma punkt som pansarvagnen
		   vExplosionslista.push_back( Explosion(false, false, spelklocka.GetElapsedTime()) );//lägger en explosion i vector
		   vExplosionslista.at(skott_i).Sprite.SetCenter(32,32); //Hela bilden är 64 x 64

		   //Slutligen, ge bilderna utifrån
		   vExplosionslista.at(skott_i).Sprite.SetImage(ExplosionImage); //ge bilden till spriten 
		   vSkottlista.at(skott_i).Sprite.SetImage(KanonkulaImage); //ge bilden till spriten


		   //Både nytt och gammalt
          skott_i++; //Räkna upp till nästa plats i kön för nästa skott 
		   exp_i = skott_i; //Synkronisera de två listorna (skott och explosioner) så att de alltid pekar på samma postnummer i 
		                    //båda listorna. Det behövs sedan för att rita ut korrekt animation på rätt plats
           }//Spacetangent nertryckt

        } //while 2 slutar

              //Slutligen visar vi upp ändringarna om och om igen många gånger i sekunden
              App.Clear(sf::Color(0, 100, 0)); //rensa allt i fönstret och ersätt med grönfärg

			   /**************************** Flytta på skotten på spelplanen ****************************************/

				if ( vSkottlista.empty() == false )
					{ // om vectorn inte är tom

				      for (si=0; si < vSkottlista.size(); si++) //for istället för while eftersom vi inte vet antalet poster längre
                      { //Gå igenom hela listan
                     vinkel = vSkottlista.at(si).vinkel; // 0 = rakt upp, startposition. Motsols 0-360 grader.
                     newx= sin(vinkel*M_PI/180.0) * vSkottlista.at(si).hastighet; //Flytt i x-led
                     newy= cos(vinkel*M_PI/180.0) * vSkottlista.at(si).hastighet; //Flytt i Y-led
                     vSkottlista.at(si).Sprite.Move(newx*-1, newy*-1); //gånger * -1 eftersom rakt upp är 0 och inte 90 grader 
	
					  //Kolla träff
					  if (check_hit(vSkottlista.at(si).Sprite.GetPosition().x, vSkottlista.at(si).Sprite.GetPosition().y) == true)
					  {//träff
						  //Enklast möjligt; samma explosionsljud används hela tiden
					   if (vSkottlista.at(si).hastighet > 0)
					   {//kulan är i rörelse
						vExplosionslista.at(si).playing = true; //börja spela upp animationen.
					   	vExplosionslista.at(si).target_x = vSkottlista.at(si).Sprite.GetPosition().x; //x och y
						vExplosionslista.at(si).target_y = vSkottlista.at(si).Sprite.GetPosition().y; 
						if(ljudeffekt.GetStatus() != sf::Sound::Status::Playing)
						  {
						   ljudeffekt.Play(); //spela upp ljudet om den inte spelas redan
						  // while(ljudeffekt.GetStatus() == sf::Sound::Status::Playing) {}
						  }	
						else
						  {
							ljudeffekt.Stop(); //stoppa och påbörja ljudet igen
							ljudeffekt.Play();
							//while(ljudeffekt.GetStatus() == sf::Sound::Status::Playing) {}
						  }
					   }//kulan är i rörelse

					   	vSkottlista.at(si).kan_ses=false; //Gör så att kulan försvinner
					   vSkottlista.at(si).hastighet = 0; //ge kulan hastighet 0
					  }//träff
				if (vSkottlista.at(si).kan_ses==true)
                   App.Draw(vSkottlista.at(si).Sprite); 
                        }//gå igenom hela listan

			  }//om vectorn inte är tom

			  
                //Var skall pansarvagnen gå?
                vinkel = stridsvagn1.sprite.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader.
                newx= sin(vinkel*M_PI/180.0) * stridsvagn1.hastighet;
                newy= cos(vinkel*M_PI/180.0) * stridsvagn1.hastighet; 
                stridsvagn1.sprite.Move(newx*-1, newy*-1); //Multiplikation * -1 eftersom rakt upp är 0 och inte 90 grader 
                App.Draw(stridsvagn1.sprite); //Rita upp stridsvagnen
                 
//********** Visa upp animationerna*******************************************************************************************//

				 // Dags att rita upp animationerna av explosionerna
				 // Vi gör det sist av allt så att explosionerna ligger ovanpå
				 // både kanonkulor och stridsvagnar.
				 //
				 // Precis som vi går igenom listan av kulor som skall flyttas
				 // går vi igenom listan av explosioner för att se om värdet "playing" är true/sant
				 // och om den inte spelats upp klart. Om båda är sant visas bildrutan upp
				 //
				 // Funktionen "explosionsbildruta" används för att välja ut rätt x/y koordinater
				 // för rätt bild i sprite sheeten. 


				 //__________________________VECTORSÄTTET___________________________-----

			 if ( vExplosionslista.empty() == false )
			  { // om vectorn inte är tom
				  		for (ei=0; ei < vExplosionslista.size(); ei++) //for istället för while eftersom vi inte vet antalet poster längre
                      { //Gå igenom hela listan
					if (vExplosionslista.at(ei).playing == true && vExplosionslista.at(ei).has_played == false ) //man är under 26 bilder
		           { 
		            explosionsbildruta(spelklocka.GetElapsedTime(), vExplosionslista.at(ei)); //Ge spritens bild rätt x och y koordinater, funktion
                   vExplosionslista.at(ei).get_explosionsbild(); //Välj rätt bild till explosionen, funktion inom klassen Explosion
	                vExplosionslista.at(ei).Sprite.SetPosition(vExplosionslista.at(ei).target_x, vExplosionslista.at(ei).target_y); //Placera ut den på rätt ställe
	                App.Draw(vExplosionslista.at(ei).Sprite); //Rita ut bilden på explosionen  
		           }
						} //Gå igenom hela listan
			 }//Om vectorn inte är tom
				

//**********Slut på att visa upp animationerna*****************************************************

        App.Display(); //visa upp ändringarna för användaren


//Radera slutligen alla kulor och explosioner som inte används
if ( vExplosionslista.empty() == false )
	{ // om vectorn inte är tom

	for (ei=0; ei < vExplosionslista.size(); ei++) //for istället för while eftersom vi inte vet antalet poster längre
		{ //Gå igenom hela listan

		if (vExplosionslista.at(ei).playing == false && vExplosionslista.at(ei).has_played == true) //man är över 26 bilder
			{ //animationen är klar
			vExplosionslista.erase(vExplosionslista.begin() + ei); //Raderar ut den färdiga explosionen
			vSkottlista.erase(vSkottlista.begin() + ei); //Radera motsvarane kanonkula
			skott_i = skott_i - 1; //Återställ antalet poster
			exp_i = skott_i;   //I bägge variablerna
			} //animationen klar

		} //Gå igenom hela listan

} // om vectorn inte är tom


     } //while 1 slutar
  return 0;
} //main slutar**********************************************************************************

//Funktioner som används i spelet

void explosionsbildruta(double speltid, Explosion &Klassinstans)
{
//De koordinater vi är ute efter
int utx = 0;
int uty = 0;

double tidsomgaott = speltid-Klassinstans.starttid; //Hur lång tid har det gått sedan explosionen startade
                                                   //Starttiden finns lagrad inuti klassen
double visningstid = 0.05; //0.05 sekund mellan varje bild
//25 bilder = värden mellan 0.25-1.25  vid 0.05

//Räkna ut Y koordinaten
if (tidsomgaott <= (visningstid * 5)) //rad 1
  uty = 0;
else if(tidsomgaott > visningstid * 5  && tidsomgaott <= visningstid *10)//rad 2
  uty = 64;
else if(tidsomgaott > visningstid * 10  && tidsomgaott <= visningstid *15)//rad 3
  uty = 128;
else if(tidsomgaott > visningstid * 15  && tidsomgaott <= visningstid *20)//rad 4
  uty = 192;
else if(tidsomgaott > visningstid * 20  && tidsomgaott <= visningstid *25)//rad 5
  uty = 256;
//Räkna ut x koordinaten
 
//Rad 1
if (tidsomgaott >= 0.0 && tidsomgaott < visningstid)
 utx = 0; //ruta 1
else if (tidsomgaott > visningstid && tidsomgaott < (visningstid * 2))
utx= 64;
else if (tidsomgaott > (visningstid * 2) && tidsomgaott < (visningstid * 3))
utx= 128;
else if (tidsomgaott > (visningstid * 3) && tidsomgaott < (visningstid * 4))
utx= 192;
else if (tidsomgaott > (visningstid * 4) && tidsomgaott < (visningstid * 5))
utx= 256;

//Rad 2
else if (tidsomgaott > visningstid * 5 && tidsomgaott < visningstid *6)
utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 6 && tidsomgaott < (visningstid * 7))
utx= 64;
else if (tidsomgaott > (visningstid * 7) && tidsomgaott < (visningstid * 8))
utx= 128;
else if (tidsomgaott > (visningstid * 8) && tidsomgaott < (visningstid * 9))
utx= 192;
else if (tidsomgaott > (visningstid * 9) && tidsomgaott < (visningstid * 10))
utx= 256;

//Rad 3
else if (tidsomgaott > visningstid * 10  && tidsomgaott < visningstid *11)
 utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 11 && tidsomgaott < (visningstid * 12))
utx= 64;
else if (tidsomgaott > (visningstid * 12) && tidsomgaott < (visningstid * 13))
utx= 128;
else if (tidsomgaott > (visningstid * 13) && tidsomgaott < (visningstid * 14))
utx= 192;
else if (tidsomgaott > (visningstid * 14) && tidsomgaott < (visningstid * 15))
utx= 256;

//Rad4
else if (tidsomgaott > visningstid * 15  && tidsomgaott < visningstid * 16)
 utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 16 && tidsomgaott < (visningstid * 17))
utx= 64;
else if (tidsomgaott > (visningstid * 17) && tidsomgaott < (visningstid * 18))
utx= 128;
else if (tidsomgaott > (visningstid * 18) && tidsomgaott < (visningstid * 19))
utx= 192;
else if (tidsomgaott > (visningstid * 19) && tidsomgaott < (visningstid * 20))
utx= 256;

//Rad5
else if (tidsomgaott > visningstid * 20  && tidsomgaott < visningstid *21)
 utx= 0; //ruta 1
else if (tidsomgaott > visningstid * 21 && tidsomgaott < visningstid * 22)
utx= 64;
else if (tidsomgaott > visningstid * 22 && tidsomgaott < visningstid * 23)
utx= 128;
else if (tidsomgaott > visningstid * 23 && tidsomgaott < visningstid * 24)
utx= 192;
else if (tidsomgaott > visningstid * 24 && tidsomgaott <= visningstid * 25)
utx= 256;

else if (tidsomgaott > (visningstid * 25))
  Klassinstans.has_played = true; //Visa bara 25 bilder, sluta spela upp animationen

Klassinstans.topkoordinatX = utx; //skriv in koordinaterna direkt i klassen
Klassinstans.topkoordinatY = uty; //låt klassen beräkna bilden
} //funktionsslut

//Kolla om kulan har träffat en vägg***********************************************************
bool check_hit(double x, double y) //se om kulan träffat
{
bool kulan_har_traeffat = false;
if (x < 50 || x > 750)
	 kulan_har_traeffat = true;
if (y < 50 || y > 550)
	 kulan_har_traeffat = true;

return kulan_har_traeffat;
}


//Rotera stridsvagn mot en speciell koordinat *****************************************************************************************
double mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare)
{
double spritensvinkel; //Den vinkel vår sprite stannar på
double PI = 3.14159265358979323846; //Utifall att pi inte är definierad

if (position_X <= sprite.GetPosition().x) //Kollar om inte x eller y är likadana 
sprite.SetRotation  (((-1* 360 / PI * (atan2(static_cast<double> (sprite.GetPosition().y - position_Y) , static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); 
  else 
sprite.SetRotation  (((-1* 360 / PI *(atan2(static_cast<double> (sprite.GetPosition().y - position_Y) ,  static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); 

window.Draw(sprite); //Rita ut ändringen

spritensvinkel = sprite.GetRotation(); //ta fram vinkeln
return spritensvinkel; //Skicka tillbaka vinkeln  
}

//***************************************PROGRAMSLUT*****************************************************************//