Programmera spel i C++ för nybörjare/Exempel på collision detect i spel
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.
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 funtionen 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*****************************************************************//