Hoppa till innehållet

Programmera spel i C++ för nybörjare/Exempel på collision detect i spel

Från Wikibooks


Denna genomgång förutsätter att du har en fungerande stridsvagnskod från exemplet "Programmera spel i C++ för nybörjare/Enkelt stridsvagnspel" Jag använder koden för Vectorer och inte för Array, eftersom det ger bäst minneshantering och oändligt antal kulor både för stridsvagnen och för kanonen.

Det är väl ok att kunna skjuta med sin spelare, men hur vet man att man får en träff på en motspelare? Genom att bygga ut koden och lägga till en kanon kan vi få ett spel där en spelare kan köra omkring med en stridsvagn och en annan spelare har en stillastående kanon och försöker att träffa stridsvagnen. Genom detta kan vi se hur de tre vanligaste varianterna av collision detect fungerar i praktiken. Du kan läsa mer om teorierna bakom collision detect här: Programmera spel i C++ för nybörjare/Collision detect

Grafik

[redigera]

Ny grafik är bilden av en kanon du kan ladda hem här:

http://strategywiki.org/wiki/Assault/Getting_Started

Jag valde bilden: AT_Type_1_Cannon.gif från listan i botten av sidan. Bilden måste roteras 180 grader så att kanonpipan pekar uppåt och därefter sparas om i png-format. Originalets gif-format fungerar inte i SFML. Glöm inte att ange att den svarta färgen i bilden skall vara genomskinlig.

kanonen

[redigera]

Vi vill ha ut kanonen på spelplanen så att vi kan börja skjuta på den och kontrollera de olika sätten man kan se om man fått en träff på. Egentligen skulle vi så klart ha den här kanonen som en ny klass som ärver från fordon, men eftersom jag bara vill visa hur det går till när två enheter skjuter på varandra låter jag kanonen vara en ny pansarvagn fast med hastighet 0 och bensin 0. Den placeras ut i nedre högra hörnet på koordinaten x=700, och y=500 eftersom spelplanen är 800 x 600 stor. Den får mindre mängd pansar också.

//stridsvagn(int ut_hastighet, double ut_spelare_x, double ut_spelare_y, int ut_bensin, int ut_pansar)
stridsvagn kanon1(0.0f, 700.0,500.0,0,50);

Vi laddar in grafiken för kanonen i spelet:

//Skapa bild för kanonen
sf::Image kanonbild; //skapa en tom bildhållare som heter bild
if (!kanonbild.LoadFromFile("AT_Type_1_Cannon.png")) return EXIT_FAILURE; 
//fyll den tomma bildhållaren med bilden på kanonen
kanon1.sprite.SetImage(kanonbild);

Vi placerar ut kanonen på spelplanen:

//Placera också ut kanonen på spelplanen
kanon1.sprite.SetPosition(kanon1.spelare_x,kanon1.spelare_y);

Slutligen vill vi kunna rotera den runt sin egen axel istället för runt övre vänstra hörnet:

kanon1.sprite.SetCenter(kanon1.sprite.GetSize().x / 2 , kanon1.sprite.GetSize().y / 2);

Vid rutinen för att rita upp grafik på skärmen lägger jag till:

App.Draw(kanon1.sprite); //Rita upp kanonen

Det är vad som behövs för att få fram kanonen på spelplanen så att vi kan börja övningsskjuta på den.

Rotation, sikta med kanonen

[redigera]

Naturligtvis vill vi kunna sikta med kanonen också, men det finns bara en mus till datorn. Därför kopplar vi rotationskoden för kanonen till piltangenterna istället:

//Rotera kanonen med höger/vänster piltangent
if (App.GetInput().IsKeyDown(sf::Key::Right)) kanon1.sprite.Rotate(- 10); //medsols
if (App.GetInput().IsKeyDown(sf::Key::Left)) kanon1.sprite.Rotate(+ 10); //motsols

Man skulle naturligtvis lika gärna kunna skriva:

if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Right))
  kanon1.sprite.Rotate(- 10);//medsols
if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Left))
  kanon1.sprite.Rotate(+ 10); //motsols

Resultatet blir exakt likadant, men det blir mer kod och den här koden måste ligga inom

while (App.GetEvent(Event))
{...}

Hur du gör är mer litet av en vana (eller ovana).

Första varianten: Cirkeltest

[redigera]

Principen bakom cirkeltest är väldigt enkel och till spel där spelarna är runda eller kvadratiska duger den här typen av kontroll om man har träffat.

  • Ta reda på avståndet mellan mittpunkterna mellan de två spelpjäserna
  • Vilken radie har den första spelpjäsens bild?
  • Vilken radie har den andra spelpjäsens bild?
  • Om radien för den första + radien för den andra är större än avståndet mellan dem, har ingen träff skett.
  • Om radien för den första + radien för den andra är lika med- eller mindre än avståndet mellan dem, har en träff skett.

Pythagoras sats

[redigera]

Kommer du ihåg pythagoras sats från grundskolan? I en rätvinklig triangel (en vinkel i triangeln är 90 grader) är den längsta sidans (hypotenusans) längd samma som kvadraten ur basens längd ^2 + höjdens längd ^2 (^ tecknet = "upphöjt till" vid datorprogrammering). I en spelvärld kommer alltid en vinkel att vara 90 grader mellan två spelare med två undantag. Om båda spelpjäsernas x koordinat är likadan, eller båda spelpjäsernas y koordinat är likadan. Då står nämligen bägge spelpjäserna på samma linje och en triangel kan inte bildas.

Pythagoras sats.
Pythagoras sats

Anta att vi vill få fram avståndet mellan mittpunkterna i stridsvagnen och kanonen vid spelstarten.

  • Stridsvagnens X= 100 och Y= 100
  • Kanonens X= 700 och Y= 500.
  • Skillnaden i Y: 500 - 100 = 400.
  • Skillnad i X: 700 - 100 = 600.
  • Avståndet mellan punkterna ^2 = 600^2 + 400 ^2.

600 ^2 (samma som 600 * 600)= 360 000 400 ^2 (samma som 400 * 400)= 160 000

  • Kvadratroten (tecknet ser ut som ett v med en extra horisontell linje på miniräknaren) ur 360 000 + 160 000 = 721.11

Då vet vi avståndet mellan mittpunkterna. Då måste vi ta reda på radien av figurerna. Bägge är 64 x 64 så det är lätt att tänka sig att radien är halva den summan = 32, men så enkelt är det inte om spelpjäserna är fyrkantige. Då täcks inte hörnen in och en kula som ser ut som om den träffade ett hörn skulle bara skjutas förbi.

  • Radien måste beräknas på avståndet mellan övre vänstra hörnet och nedre högra hörnet delat med två (annars får du diametern) om spelpjäsen är fyrkantig. Om det är en rund pjäs (t.ex. en biljardboll) kan man låta diametern vara samma som avståndet mellan två sidor.
  • Diametern är då kvadratroten ur 64^2 + 64^2 ungefär = 90.4
  • Radien är halva den summan = 45.2
  • Slår man ihop radien för de två spelpjäserna ser man att så länge som avståndet mellan deras mittpunkter är större än 90.4 kommer de inte att krocka.

Negativa tal

[redigera]

Vad händer om talet blir negativt? Spelare ett befinner sig till höger om spelare två och x blir 100 - 700 = -600? Inga problem. Två negativa tal som multipliceras med varandra blir alltid ett positivt tal. -600 * -600 är samma sak som -600^2 och resultatet blir alltså detsamma som 600 * 600 eller 600^2.

x1 = x2 eller y1 = y2

[redigera]

Om spelare 1 x koordinat är samma som spelare 2 x koordinat innebär det att de befinner sig lika långt in från kanten men på olika höjd. De befinner sig i vertikal linje med varandra. Då blir avståndet mellan deras mittpunkter lika med spelare 1 y koordinat - spelare 2 y koordinat.

Om spelare 1 y koordinat är samma som spelare 2 y koordinat innebär det att de befinner sig lika långt in från överkanten men på olika avstånd från vänsterkanten. De befinner sig i horisontell linje med varandra. Då blir avståndet mellan deras mittpunkter lika med spelare 1 x koordinat - spelare 2 x koordinat.

Oftast är det bäst att kontrollera om de ligger i linje med varandra innan man beräknar pythagoras sats eftersom det är en mycket enklare beräkning för datorn att göra. Då går spelet en gnutta snabbare.

Kvadratiska spelpjäser

[redigera]

Om du bara har kvadratiska spelpjäser är det lättare att räkna ut diametern. Det finns nämligen en konstant för det: 1.4142135623730950488016887242097

  • Om varje sida är en pixel blir diametern (hypotenusan) 1.4142135623730950488016887242097
  • Om varje sida har 64 bitar blir diametern (hypotenusan) 1.4142135623730950488016887242097 * 64 = 90.4 och radien halva den summan = 45.2

Du gör det alltså enkelt för dig om så många spelpjäser som möjligt är kvadratiska och du definierar konstanten i början. Då spar du en fruktansvärd mängd flyttalsberäkningar för datorn och prestandan på spelet ökas.

Träff?

[redigera]

De tidigare koden vi använde visade en explosion om kulan var 50 pixels eller närmare kanten. Då bildades en explosion. Funktionen var väldigt enkel och såg ut så här:

//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;
}

Nu får vi lov att ändra funktionen totalt. Vi måste ha med x och y koordinaterna för två olika sprites. Endera skickar vi in fyra olika double-värden, eller så skickar vi in två olika adresser till spritesen. För att göra det tydligt kallar vi den här funktionen Cirkular (så att vi får korrekt namn på de tre olika sätten som finns).

//Kolla om kulan har träffat en annan spelare***********************************************************
bool Cirkular_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite) //se om kulan träffat
{
bool kulan_har_traeffat = false; //returvärde
double avstaond = 0.0; //Avståndet mellan två sprites mittpunkter
double traeffavstaond = 0.0; //sammanlagda summan av två sprites radie
double tempx; //sträckan i x-led
double tempy; //sträckan i y-led

//Ta fram koordinaterna för de två spritesen. 
//kulax och kulay är kanonkulans koordinater
//maolx och maoly är måltavlans koordinater
double kulax = kulsprite.GetPosition().x;
double kulay = kulsprite.GetPosition().y;
double maolx = maolsprite.GetPosition().x;
double maoly = maolsprite.GetPosition().y;

//Ta fram kulansradie, 16 x 16 pixels stor
double kula_radie = (1.4142135623730950488016887242097 * 16) / 2;
//Ta fram målets radie, 64 x 64 pixels stor
double maol_radie = (1.4142135623730950488016887242097 * 32) / 2;
//Så när träffar dem? vilket avstånd minst från varandra?
traeffavstaond = kula_radie + maol_radie;

if (kulax == maolx  && kulay != maoly ) //de står på samma linje i x-led
	{
	avstaond = kulay - maoly;
	if (avstaond <= traeffavstaond) //inom träffavstånd
	kulan_har_traeffat = true;
	}
 else if (kulay == maoly && kulax != maolx ) //de står på samma linje i y-led
       {
	avstaond = kulax - maolx;
	if (avstaond <= traeffavstaond) //inom träffavstånd
	kulan_har_traeffat = true;
       }
else if(kulax != maolx && kulay != maoly) //De står på olika platser både x och y
	{ //Räkna med pythagoras
	tempx = kulax-maolx;
	tempy = kulay-maoly;
	// Räkna ut hypotenusan/avståndet genom att ta 
	// kvadratroten ur ena sträckan ^2 + andra sträckan ^2
	avstaond = sqrt ( tempx * tempx + tempy * tempy);
	if (avstaond <= traeffavstaond) //inom träffavstånd
	kulan_har_traeffat = true;
	} //Räkna med pythagoras
else if (kulax == maolx && kulay == maoly) //De står på exakt samma plats
	kulan_har_traeffat = true;

return kulan_har_traeffat;
}

Lägg in funktionsdeklarationen alldeles före main börjar (glöm inte semikolon i slutet):

 bool Cirkular_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite); //se om kulan träffat

Slutligen kommenterar vi bort den tidigare testen för en träff och istället skriver vi:

//Kolla träff om kulan är 50 pixlar från kanten
// if (check_hit(vSkottlista.at(si).Sprite.GetPosition().x, vSkottlista.at(si).Sprite.GetPosition().y) == true)
//kolla träff för cirkular hit mellan kulan och kanonen
if ( Cirkular_check_hit(vSkottlista.at(si).Sprite, kanon1.sprite)== true)

Det är allt. När du nu kör omkring och skuter med stridsvagnen ser du att du kan skjuta iväg kulorna utanför kanten, men så fort kulan tar i kanten på kanonen får du en explosion just där.

Andra varianten: Bounding box

[redigera]

Alla sprites och spelpjäser är inte runda eller kvadratiska. Många är avlånga. Exempelvis en torped som skickas från en ubåt i ett undervattensspel. Att utgå ifrån diametern på något som t.ex. är 10 pixlar brett men 80 pixlar långt gör att man kommer att få mängder med fel. Skjuter man mot torpeden från sidan kommer explosionen att uppstå 40 pixlar ifrån mitten trots att den bara är 10 pixlar bred.

Ett sätt att komma runt det är att istället tänka sig att torpeden ligger inuti en fyrkant och att kulan som skjuter mot torpeden inte är rund utan också ligger inuti en fyrkant. sedan kontrollerar man om fyrkanterna kommit innanför varandra. Det är enklare än det låter, i alla fall i spel där man bara använder sig av fyrkanter med 90 graders vinklar i hörnen. det är oändligt mycket krångligare i 3D...

Pseudokod är enligt följande:

  • Är kulan rakt ovanför torpeden och har kulans nedre vägg passerat torpedens övre vägg?
  • Är kulan rakt under torpeden och har kulans övre vägg passerat torpedens nedre vägg?
  • Är kulan i nivå med torpeden och har kulans högra vägg passerat torpedens vänstra vägg?
  • Är kulan i nivå med torpeden och har kulans vänstra vägg passerat torpedens högra vägg?
  • Om någon av ovanstående frågor besvarats med ja har en träff skett, annars har ingen träff skett.
  • Eller; om någon del av kulan är under torpedens överkant och över torpedens underkant och till höger om torpedens vänsterkant och till vänster om torpedens högerkant, har en träff skett.

För att det här skall fungera måste vi veta två x och två y värden för båda spelpjäserna som skall testas eller också utgår vi från de två spelpjäsernas mittpunkter (vilket är enklast) och beräknar var kanterna finns utifrån mittpunktens position.

Nu döper vi funktionen till Box_check_hit för att komma ihåg att vi kontrollerar om vi fått en träff genom att box krockar med box:

//Kolla om kulan har träffat kanten av en annan spelare***********************************************************
 bool Box_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite) //se om kulan träffat
 {
 bool kulan_har_traeffat = false; //returvärde
 //Ta fram koordinaterna för de två spritesen. 
 //kulax och kulay är kanonkulans koordinater
 //maolx och maoly är måltavlans koordinater
 double kulax = kulsprite.GetPosition().x;
 double kulay = kulsprite.GetPosition().y;
 double maolx = maolsprite.GetPosition().x;
 double maoly = maolsprite.GetPosition().y;

 //Kulan är 16 x 16 och målet är 64 x 64, vi räknar från varje sprites mittpunkt

 //  innanför vänster kant         innanför höger kant      innanför över kant         innanför neder kant 
 if ((kulax + 8 > (maolx - 32)) && (kulax - 8 < maolx + 32) && kulay + 8  > maoly -32  && kulay - 8 < maoly  + 32 )
 kulan_har_traeffat = true; //returvärde

 return kulan_har_traeffat;
 }

Lägg in funktionsdeklarationen före main (glöm inte semikolon):

bool Box_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite); //se om kulan träffat boxkan

Lägg slutligen in funktionen genom att kommentera bort föregående funktion:

//if ( Cirkular_check_hit(vSkottlista.at(si).Sprite, kanon1.sprite)== true)
if (Box_check_hit(vSkottlista.at(si).Sprite, kanon1.sprite) == true)//se om kulan träffat

När du skjuter nu kan du försöka att träffa kanonens hörn. Eftersom kanonen har en rätt rund form, och även kanonkulan är rund, är det ganska tydligt att kulan träffar alldeles innan den egentligen tar i kanonen, vilket är största problemet när man använder bounding box tester på just runda föremål.

Generell "bounding box" funktion

[redigera]

Funktionen här ovanför är byggd för just det här spelet med just de spelpjäsernas storlekar. Vad händer om man gör ett annat spel med andra spelpjäser? Här följer en mer avancerad funktion men som kan användas för att snabbt se om en kollision skett oavsett storleken på boxarna. Om man sätter koordinatpunkten på något annat ställe än i övre vänstra hörnet för en sprite, måste koden justeras om:

short int Sprite_Collide(sf::Sprite &spelare, sf::Sprite &fiende)
       { //alla "1" är spelaren och "2" är fienden
       //Koden bygger på att man har koordinaterna i övre vänstra hörnet

       //Om man vill utgå ifrån en träff där man beräknar från mittpunkter får man dessutom skriva
         // spelare.SetCenter(spelare.GetSize().x / 2 , spelare.GetSize().y / 2);
         // fiende.SetCenter(fiende.GetSize().x / 2 , fiende.GetSize().y / 2);

       float left1, left2;                                 //Boxarnas vänsterkanter
       float right1, right2;                               //Boxarnas högerkanter
       float top1, top2;                                   //Boxarnas överkanter
       float bottom1, bottom2;                             //Boxarnas underkanter

       left1 = spelare.GetPosition().x;                     //spelarens-x koordinat
       left2 = fiende.GetPosition().x;                      //målets x-koordinat
       right1 = spelare.GetPosition().x + spelare.GetSize().x;     //spelarens högerkant
       right2 = fiende.GetPosition().x + fiende.GetSize().x;      //måltavlans högerkant
       top1 = spelare.GetPosition().y;                      //spelarens överkant
       top2 = fiende.GetPosition().y;                       //målets överkant
       bottom1 = spelare.GetPosition().y + spelare.GetSize().y;   //spelarens underkant
       bottom2 = fiende.GetPosition().y + fiende.GetSize().y;     //målets underkant

       if (bottom1 < top2) return(0);                      // Spelaren flyger över målet
       if (top1 > bottom2) return(0);                      // Spelaren flyger under målet
       if (right1 < left2) return(0);                      // Spelaren är bortanför målet åt höger
       if (left1 > right2) return(0);                      // Spelaren är bortanför målet åt vänster

       return(1); //Träff! Den har skett om inte spelaren är bortanför målet
                  //Originalkod från: http://www.gamedev.net/page/resources/_/technical/game-programming/collision-detection-r735
       };

För att avgöra om det blivit en träff skriver du således bara:

int Sprite_Collide = 0;
if (Sprite_Collide(spelare.sprite, fiende.sprite) == 1)
   {//Träff, gör något}

Kvicksand...

[redigera]

Anta att du har en gravitation som pressar neråt och du, fast du står på en plattform du programmeringsmässigt fått en träff mot enligt koden här ovanför, ändå sjunker neråt långsamt som om spriten fastnat i kvicksand, har du problem. Du har en del av din fyrkant innanför plattformens "bounding box" (om det är ett plattformsspel) vilket indikerar träff hela tiden och du sjunker långsamt, långsamt genom plattformen. För att undvika det får du se till så att din sprite befinner sig alldeles, alldeles utanför boxen, 0.1 pixel fungerar, eller på exakt samma linje.

Var träffar man?

[redigera]

Nu vet du att du fått en träff, men var har du träffat? Det finns egentligen tre olika sätt att lista ut det:

  • Jämför koordinaterna för mittpunterna för både spelaren och motståndaren. Är spelaren ovanför och krockar t.ex. kan vi anta att spelarens undersida krockat med motståndarens översida.
  • Utgå ifrån din hastighet i x och y. Om spelaren färdas med negativt x (åt vänster) kan man anta att man krockat med sin egen vänstersida mot en motståndares högersida.
  • Svårast, utgå ifrån vinkeln mellan spelaren och motståndaren. Är vinkeln 0 grader är man rakt ovanför, 90 grader rakt åt vänster, 180 grader rakt ner osv. Vinkeln räknas lättast ut med cos eller sinus när man vet hypotenusans och kateternas längd.

Tredje varianten: Pixel perfect

[redigera]

Den tredje sortens kontroll om man träffat är tung för datorn att beräkna. Därför används den vanligtvis inte ensam utan tillsammans med någon av de två övriga sätten. Först kontrollerar man om man fått en träff. Sedan gör man en pixel perfect kontroll för att se om det verkligen var en träff. Det är ett bra sätt för att t.ex. undvika att man får explosioner vid hörnen på runda saker om man gör en bounding box test. Så vad innebär egentligen pixel perfect? Jo, man har alltid en färg som är genomskinlig, men det är faktiskt en färg. I just vårt exempel har pansarvagnen mörkgrönt, kulan har nästan svart och kanonen har svart färg som markerats som genomskinliga. Efter det att man fått en "träff" enligt de andra teknikerna kontrollerar man varenda pixel under kanonkulan (i det här fallet) och ser vilken färg den har. Om samma pixelkoordinat i förhållande till spelplanen är genomskinlig både på kanonen och på kanonkulan, eller på någon av spelarna har ingen träff skett men om båda pixlarna inom den överlappande arean inte är genomskinliga kan man anta att det skett en träff. Krångligt? Javisst. Knappast för nybörjare heller, men jag vill i alla fall nämna det.

Tänk efter före

[redigera]

Om du tänker använda dig av pixel perfect i stor utsträckning är det en god idé att se till så att samtliga sprites har exakt samma färg som är genomskinlig. På det viset underlättar du kodningen rejält för dig själv. Om du har sprites som inte skall stoppa kulor, som t.ex. rök, kan du ge den en annan genomskinlig bakgrundsfärg. Genom att ge alla enheter som kan träffas av kulor samma genomskinliga bakgrundsfärg kan du använda samma funktion till samtliga spelpjäser. Något som underlättar din kodning rejält.

Genomskinliga färger

[redigera]

När vi startar kodningen är det bra att veta vilka färger som anses genomskinliga i spelet. I vårt lilla spel är det t.ex:

  • Genomskinlig färg på kulan = 1,0,0 (RGB)
  • Genomskinlig färg på kanonen = 0,0,0 (RGB)
  • Genomskinlig färg på stridsvagnen = 59,147,81 (RGB)

Man använder sig sällan av pixel perfect. Det tar lång tid att gå alla dessa pixlar på alla berörda spelpjäser eftersom man måste göra flera olika beräkningar för att se om man fått en träff. Pseudokoden kan se ut så här:

  • Vilka koordinater har fyrkanten som bildas när två spelpjäser överlappar varandra?
  • Vilka av de koordinaterna finns i både kanonkulan och i målet (i vårt exempelfall)?
  • Har dessa koordinater genomskinliga färger på bägge spelpjäserna på samma plats?
  • Är alfakanalen ställd till 0? Då kan du sluta försöka eftersom genomskinligheten också ställts till 0.

Vill du ändå fördjupa dig finns dels ett exempel på SFML:s hemsida och dels en riktigt bra artikel här:

http://www.gamedev.net/page/resources/_/technical/game-programming/collision-detection-r735


Färg för kollisionsberäkningar

[redigera]

Om du behöver ta fram färgen på en exakt punkt i en image som en sprite har gör du så här:

//Definiera färgerna:
int red = 255;
int green = 255;
int blue = 255;
//koordinater
int x_koordinatibilden=0;
int y_koordinatibilden=0;

//Inuti main
const sf::Color color = Sprite.GetPixel (x_koordinatibilden, y_koordinatibilden); 
red = (int)color.r;
green = (int)color.g; 
blue = (int)color.b;

Därefter får du jämföra pixelns färg och kontrollera om den överensstämmer med RGB värdet för den genomskinliga färgen. Tänk på att om du använder ett spritesheet får du fram hela sprite sheetens koordinater och inte den utvalda spritens koordinater.

Det här kan också användas genom att t.ex. se vilken färg som finns under en racerbil i ett racerspel. Anta att bakgrunden är en separat sprite som heter "bakgrunden" som fyllts med en enda bild på en gräsplan med en racerbana på som är svart. Man har en röd och en blå bil. Kör man på det gröna utanför racerbanan har man ett värde av grönt under bilen som är 65-90. Då kan man anta att hastigheten är halverad. Koden blir då:

//Räkna ut färg under blå  bil
sf::Color colorB = bakgrunden.GetPixel (bluecar.sprite.GetPosition().x,bluecar.sprite.GetPosition().y); 
red = (int)colorB.r;
green = (int)colorB.g; 
blue = (int)colorB.b;

vinkel2 = bluecar.sprite.GetRotation();//Vilken vinkel står bilen i?
double bilhastighet1 = bluecar.hastighet * 0.002;
if (green > 65 && green < 90) //står på gräset
bilhastighet1 = (bluecar.hastighet * 0.002) /2;
else
bilhastighet1 = bluecar.hastighet * 0.002;
newxB= sin(vinkel2 * M_PI/180.0) * bilhastighet1;
newyB= cos(vinkel2 * M_PI/180.0) * bilhastighet1;
bluecar.sprite.Move(newxB, newyB); //åk iväg

Sedan är det bara att köra iväg med bilen. Eftersom färgen är en struct, måste man skapa en ny instans för varje fordon. Till den röda bilen blir det t.ex.:

const sf::Color colorR = bakgrunden.GetPixel (redcar.sprite.GetPosition().x,redcar.sprite.GetPosition().y); 
red = (int)colorR.r;
green = (int)colorR.g; 
blue = (int)colorR.b;

I exemplet använder jag samma red, green och blue för bägge bilarna efter det att jag skapat dem som vatiabler i Main. Det är dock smartare att lägga in re, green och blue inuti en klass istället och mata in värdena till dem i klassen allteftersom man kör runt med de olika bilarna på spelplanen.

Dold bakgrund

[redigera]

Att hitta färgen under en speciell punkt är också ett väldigt enkelt sätt att t.ex. kontrollera om man krockat i ett plattformsspel. I exemplet här ovan kallade vi bakgrunden för "bakgrunden" rätt och slätt och det är den spritens bild vi använder osss av i kodexemplet:

sf::Color colorB = bakgrunden.GetPixel (bluecar.sprite.GetPosition().x,bluecar.sprite.GetPosition().y); 

men anta att vi har en annan sprite som heter "dold_bakgrund" och som vi aldrig ritar ut. På den kan vi ha alla saker man kan hoppa på i plattformsspelet utritade med tydliga färger. Anta att alla plattformarna är knallröda och stationära, då gör du istället

sf::Color colorB = dold_bakgrunden.GetPixel (bluecar.sprite.GetPosition().x,bluecar.sprite.GetPosition().y);
//bortklippt kod 
if (red == 255)
  {collision = true;}
  else
  {collision = false;}

Dvs. om den färg man testar mot är knallröd så har vi krockat med en plattform som är knallröd. Det gör detsamma om plattformarna inte är knallröda på den bild som visas utåt mot spelaren.

Skall man ta detta ett steg längre mäter man på olika punkter. I ett plattformsspel kan man kolla mitten på över- och underkanten samt mitten på höger- och vänster sida. Om någon av de punkterna ligger på en röd yta har man krockat.

Detta system fungerar endast på saker som är fasta i spelet. Saker som tillverkas nya, som kanonkulor t.ex. fungerar det inte på, om man inte skulle få för sig att göra även dem knallröda.

Om spelpjäserna är små är det lättast att bara mäta mot mittenpunkten. Det kan ge litet udda bieffekter men fungerar vanligtvis tillräckligt bra.

Komplett kod med både Cirkular och Box collision detect

[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 rutor 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

//Kolla om kulan har träffat en annan spelare***********************************************************
bool Cirkular_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite); //se om kulan träffat

//Kolla om box passerat box mellan två spelare
bool Box_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite);

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 sedan kanonen
	 stridsvagn kanon1(0.0f, 700.0, 500.0, 0, 50);

            //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);

	   //Skapa bild för kanonen
	          sf::Image kanonbild; //skapa en tom bildhållare som heter bild
      if (!kanonbild.LoadFromFile("AT_Type_1_Cannon.png")) return EXIT_FAILURE; 
      //fyll den tomma bildhållaren med kanonbilden
      kanon1.sprite.SetImage(kanonbild);

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

	   //Placera också ut kanonen på spelplanen
     kanon1.sprite.SetPosition(kanon1.spelare_x,kanon1.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);
	   //Gör samma med kanonen
	   kanon1.sprite.SetCenter(kanon1.sprite.GetSize().x / 2 , kanon1.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


		   //Rotera kanonen med höger/vänster piltangent
         if (App.GetInput().IsKeyDown(sf::Key::Right)) kanon1.sprite.Rotate(- 10); //medsols
         if (App.GetInput().IsKeyDown(sf::Key::Left)) kanon1.sprite.Rotate(+ 10); //motsols

         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 
  
					 /****************************************************************************************************/
					 // Olika rutiner för att se om mman fått en träff:
					 // En standardrutin som kollar om kulan är 50 pixlar från kanten, bra att ha för enkel felkontroll
					 // En för att se om radiena går in i varandra, bra för runda föremål
					 // En för att se om två boxar går in i varandra, bra för långsmala föremål
					 // En för att avgöra om en träff skett beroende på färg.
					 //**************************************************************************************************/
                                         //Kolla träff om kulan är 50 pixlar från kanten
                                        // if (check_hit(vSkottlista.at(si).Sprite.GetPosition().x, vSkottlista.at(si).Sprite.GetPosition().y) == true)
										//kolla träff för cirkular hit mellan kulan och kanonen
										if ( Cirkular_check_hit(vSkottlista.at(si).Sprite, kanon1.sprite)== true) //se om kulan träffat
					                      ///if (Box_check_hit(vSkottlista.at(si).Sprite, kanon1.sprite) == true)//se om kulan träffat
					                 
                                         {//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
				App.Draw(kanon1.sprite); //Rita upp kanonen
                
//********** 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;
}

//Kolla om kulan har träffat en annan spelare***********************************************************
bool Cirkular_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite) //se om kulan träffat
{
bool kulan_har_traeffat = false; //returvärde
double avstaond = 0.0; //Avståndet mellan två sprites mittpunkter
double traeffavstaond = 0.0; //sammanlagda summan av två sprites radie
double tempx; //sträckan i x-led
double tempy; //sträckan i y-led

//Ta fram koordinaterna för de två spritesen. 
//kulax och kulay är kanonkulans koordinater
//maolx och maoly är måltavlans koordinater
double kulax = kulsprite.GetPosition().x;
double kulay = kulsprite.GetPosition().y;
double maolx = maolsprite.GetPosition().x;
double maoly = maolsprite.GetPosition().y;

//Ta fram kulansradie, 16 x 16 pixels stor
double kula_radie = (1.4142135623730950488016887242097 * 16) / 2;
//Ta fram målets radie, 64 x 64 pixels stor
double maol_radie = (1.4142135623730950488016887242097 * 32) / 2;
//Så när träffar dem? vilket avstånd minst från varandra?
traeffavstaond = kula_radie + maol_radie;

if (kulax == maolx  && kulay != maoly ) //de står på samma linje i x-led
	{
	avstaond = kulay - maoly;
	if (avstaond <= traeffavstaond) //inom träffavstånd
	kulan_har_traeffat = true;
	}
 else if (kulay == maoly && kulax != maolx ) //de står på samma linje i y-led
       {
	avstaond = kulax - maolx;
	if (avstaond <= traeffavstaond) //inom träffavstånd
	kulan_har_traeffat = true;
       }
else if(kulax != maolx && kulay != maoly) //De står på olika platser både x och y
	{ //Räkna med pythagoras
	tempx = kulax-maolx;
	tempy = kulay-maoly;
	// Räkna ut hypotenusan/avståndet genom att ta 
	// kvadratroten ur ena sträckan ^2 + andra sträckan ^2
	avstaond = sqrt ( tempx * tempx + tempy * tempy);
	if (avstaond <= traeffavstaond) //inom träffavstånd
	kulan_har_traeffat = true;
	} //Räkna med pythagoras
else if (kulax == maolx && kulay == maoly) //De står på exakt samma plats
	kulan_har_traeffat = true;

return kulan_har_traeffat;
}


 //Kolla om kulan har träffat kanten av en annan spelare***********************************************************
bool Box_check_hit(sf::Sprite &kulsprite, sf::Sprite &maolsprite) //se om kulan träffat
{
bool kulan_har_traeffat = false; //returvärde
//Ta fram koordinaterna för de två spritesen. 
//kulax och kulay är kanonkulans koordinater
//maolx och maoly är måltavlans koordinater
double kulax = kulsprite.GetPosition().x;
double kulay = kulsprite.GetPosition().y;
double maolx = maolsprite.GetPosition().x;
double maoly = maolsprite.GetPosition().y;

//Kulan är 16 x 16 och målet är 64 x 64
//  innanför vänster kant         innanför höger kant      innanför över kant         innanför neder kant 
if ((kulax + 8 > (maolx - 32)) && (kulax - 8 < maolx + 32) && kulay + 8  > maoly -32  && kulay - 8 < maoly  + 32 )
	 kulan_har_traeffat = true; //returvärde

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*****************************************************************//