Programmera spel i C++ för nybörjare/Rotation
Det kan vara svårt att hålla reda på sin cos tan osv. Här har du en "lathund" för trigonometri: http://grebocin.com/upload/trig.png
Att rotera sprites
[redigera]Det finns några olika funktioner i SFML som är bra att kunna när man tillverkar egna spel. I grund och botten finns det två olika sätt att hantera hur sprites ser ut när de rör sig på spelplanen. Den vanligaste är den sk. ”isometriska” vyn.
Du har intrycket av att du ser på spelarna litet snett uppifrån. Den är vacker och man får en känsla av att det är en 3D miljö även om spelet egentligen bara är 2D. Den största nackdelen med den typen av vy är att det kräver så väldigt mycket grafik för att den skall kännas snygg. Vanligen har man bilder på spelaren ur åtta olika vinklar. Om du tänker dig en vanlig fyrkantig ruta skall det vara bilder för att visa hur figuren rör sig ut från varje sida, och ut från varje hörn. Dessutom måste man göra olika bilder beroende på vilka handlingar spriten gör (går, springer, sover, hoppar, skjuter osv.), några kända spel med isometrisk vy är de gamla Starcraft 1 och Age of Empires 1, samt Zyngas Farmville som är världens mest spelade spel just nu.
Den andra vyn är den vanligaste för enkla spel. Då tänker man sig att man ser på spelytan rakt ovanifrån. Vyn kallas ”top down” på fackspråk. I det fallet behöver du normalt sett inte lika mycket grafik. Istället får du rotera figuren i olika vinklar. Rotation är rätt krävande för datorn så det är inte att rekommendera för stora spel med många spelare, men för små spel med en handfull spelare går det bra.
Vinklar
[redigera]I SFML 1.6 har alla figurer vinkel 0 när de skapas i spelet, oavsett hur bilden är utritad. Gör det enkelt för dig; var konsekvent och låt alla spelpjäser vara riktade åt samma håll när de först kommer in på spelplanen oavsett om det är spelarens eller fiendens pjäser. För att göra det ännu mer förvirrande räknar man ut vinkeln motsols. Dvs. om man roterar figuren en grad medsols får den vinkel 360. För att göra det ännu värre räknar man precis tvärtom i SFML 2, men det håller vi inte på med just nu.
Pi
[redigera]Det är ofta man räknar med pi när man skall se hur figurer rör sig på spelplanen. Eftersom det kan vara rätt tungt för datorn att räkna med ett decimaltal med obegränsat antal decimaler är det bättre att helt enkelt definiera pi som en statisk konstant i början av programmet. Pi kommer ju aldrig att ändra sig. Längst upp i koden skriver du:
#define M_PI 3.14159265358979323846 /* pi som statisk konstant*/
Därefter anger du alltid M_PI i alla beräkningar istället för det vanliga pi.
Egentligen är det "C" sättet att skriva på, i C++ förordas att man anger PI som statisk konstant. Jämför dessa två exempel:
#define M_PI 3.14159 //Det gamla sättet som man helst skall undvika, kan lika gärna skrivas in i "define" överst. Inget ";" -tecken i slutet. const double M_PI = 3.14159; //C++ standardsätt att skriva det på och då har det ett ";" -tecken i slutet
Bägge sätten fungerar och hur du själv vill göra är en smaksak.
Grader och radianer
[redigera]Vi vanliga dödliga är vana vid att ett varv är 360 grader. När man är litet mer avancerad i sina uträkningar inser man att det är litet enklare att arbeta med radianer. Ett varv runt en cirkel är 2 * pi radianer.
Sinus och cosinus (och tangens)
[redigera]
Nu skall vi se hur man går runt litet snett på spelplanen. Du vet sedan tidigare att om man går rakt ner ökar Y men X är 0. Om man däremot går rakt åt höger ökar X men Y är 0. Men hur gör man om man skall gå litet grann mittemellan, säg 30 grader upp åt nordost? Hastigheten i SFML styrs av hur många pixlar man går i X- och Y-led per bildvisning, normalt 60 visningar i sekunden. Går man tio pixlar åt höger (X=X+10) samtidigt som man går tio pixlar uppåt (Y=Y-10) går man i precis 45 graders vinkel snett upp åt höger. Visst måste det finnas någon form av matematiskt samband för detta? Naturligtvis. Det heter cosinus och sinus. Om du ser på bilden från wikimedia commons här bredvid har man döpt sinus till sen då bilden är på ett annat språk än svenska, men i övrigt är allt som det skall vara.
- vinkeln är den vinkel vi skall gå i.
- 1 på bilden är den sträcka vi skall gå, den är alltså inte alltid 1.
- cos (cosinus) vinkeln är hur många pixels vi skall gå i X-led.
- sin (sinus) vinkeln är hur många pixels vi skall gå i Y-led.
Anta att vi vill gå 30 grader snett upp åt vänster och vi börjar med att vår figur är placerad med nosen mot överkanten. Då är gradtalet egentligen 330, eftersom 0 är rakt upp och vi går motsols i beräkningen av grader.
Cos 330 = 0.866 (X-led) sin 330 = -0.5 (Y-led)
Så för varje pixel vi går uppåt går vi 1.73 åt höger. Det stämmer ju inte, kanske någon klipsk programmerare säger. Precis. 330 grader i den riktiga världen är egentligen sydvästlig riktning, nästan västlig (360 grader är rakt västlig). Den vinkel vi fått fram är egentligen en spegelbild av den riktning vi egentligen ville ha.
Den vinkel vi egentligen vill gå i, enligt det normala gradsystemet är 120 grader. Dvs. 90 grader är rakt upp och så lägger vi på 30 grader till.
- Cos 120= -0.5
- Sin 120 = 0.866
Så för varje pixel vi går åt vänster (!) går vi också 1.73 neråt. Hmm. Vinkeln blir helt korrekt, men den går ju åt fel håll! Det finns två sätt att komma runt det problemet, endera multiplicera talen med -1 för att göra positiva tall negativa och negativa tal positiva, alternativt spegelvända beräkningen. Om vi tänker oss att vi drar ett streck rakt igenom origo är den spegelvända vinkeln 300 grader.
- Cos 300 = 0.5
- sin 300 = -0.866
För varje pixel åt höger går vi 1.73 uppåt. Nu stämmer beräkningen och vår figur traskar glatt iväg i 30 graders vinkel uppåt mot överkanten.
För att bättre förklara vad som händer....
0 grader, rakt åt vänster:
cos 0 (x-led) = 1 sin 0 (y-led) = 0 (I spelvärlden går vi rakt åt höger)
90 grader, rakt upp:
cos 90 (x-led) = 0 sin 90 (y-led) = 1 (I spelvärlden går vi rakt ner)
180 grader, rakt åt höger:
cos 180 (x-led) = -1 sin 180 (y-led) = 0 (I spelvärlden går vi rakt åt vänster)
270 grader, rakt ner:
cos 270 (x-led) = 0 sin 270 (y-led) = -1 (I spelvärlden går vi rakt åt upp)
360 grader, rakt åt vänster:
cos 360 (x-led) = 0 sin 360 (y-led) = 1 (I spelvärlden går vi rakt åt höger)
Multiplicera både cosinus och sinus värdet med en viss hastighet, 10 t.ex. så ökar du hastigheten. Skall man vara riktigt kinkig kan man strunta i att sträckan är 1 och beräkna hastigheten utifrån sträckans längd istället. (x hastighet = cos vinkel * sträckan, y hastighet = sin vinkel * sträckan) men: eftersom vi inte alltid vet vad sträckan är, och eftersom det inte blir någon skillnad när vi multiplicerar sträckorna med samma tal, och eftersom vi vill kunna styra sträckornas längd med en variabel för hastigheten, kan du göra så här istället.
Vill du veta sträckan, hur långt du går, använder du Pythagoras sats: kvadratroten ur (cosinusvärdet * cosinusvärdet) + (sinusvärdet * sinusvärdet) = kvadraten ur sträckan (1 på bilden). Eller, som man kan skriva i C++ (sqrt är en funktion i STD biblioteket).
c = sqrt( a*a + b*b );
Tangens
[redigera]Tangens behöver du inte kunna (men se gärna på http://sv.wikipedia.org/wiki/Trigonometri) så länge du bara räknar med rätvinkliga trianglar där en vinkel alltid är 90 grader, däremot kan du använda tangens i olika mer avancerade funktioner där andra sorters trianglar skall beräknas eftersom tangens är den tredje delen av trion cosinus-sinus-tangens för att beräkna vinklar och sträckor i trianglar.
Radianer
[redigera]
Radianer visar den faktiska sträckan man ”gått” längs kanten på en cirkel. I förra exemplet antog vi att hypotenusan alltid är 1, när det gäller radianer kan vi anta att radien ”r” alltid är 1. Det gör allting lättare att förstå men förändrar ingenting i koden. Omkretsen runt cirkeln motsvarar 2 PI och det är också 360 grader. Vi har alltså en koppling mellan gradsystemet och radiansystemet.
1 rad ≈ 57,296 grader. 1 grad ≈ 0.017 radianer = PI/180
Vad har det för betydelse för oss? Vill du inte att det skall bli ett litet ”hopp” när du kör omkring med figurer på spelplanen får du lov att lägga till värdet i radianer. Anta att du vill att en figur skall röra sig på spelplanen mot en x,y koordinat när du trycker ner pil-upp tangenten.
Float newx = 0.0; //dit den skall x float newy = 0.0; //dit den skall y float vinkel = 0.0; //vinkel på figuren int speed = 10; //Hastigheten if (App.GetInput().IsKeyDown(sf::Key::Up)) { vinkel = <sprite>.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel) * speed; newy= cos(vinkel) * speed; <sprite>.Move(newx*-1, newy*-1); // * -1 eftersom rakt upp är 0 och inte 90 grader }
Den här koden tycker man borde räcka, men om du kör figuren nu kommer den att skutta litet eftersom antalet decimaler blir för lågt. Du måste räkna om vinkeln till radianer för att få en mjuk gång. 2 pi/360 = Pi/180 = 1 radian = 0.017 grad.
if (App.GetInput().IsKeyDown(sf::Key::Up)) { vinkel = <sprite>.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel * PI/180.0) * speed; newy= cos(vinkel * PI/180.0) * speed; <sprite>.Move(newx*-1, newy*-1); // * -1 eftersom rakt upp är 0 och inte 90 grader }
Exempelkod stridsvagn
[redigera]Jag tankade hem en bild på en stridsvagn från: http://media.strategywiki.org/images/2/2a/MH_Tank.gif
Nu är den i gif-format så jag fick dels konvertera den till png format och rotera den 180 grader så att kanonpipan pekar uppåt. Jag döpte den till tank och sparade filen i samma mapp som main.cpp filen.
Vi börjar med att göra en stridsvagn som kan gå fram och tillbaka.
//Bild på stridsvagn från http://media.strategywiki.org/images/2/2a/MH_Tank.gif //Konverterad till png-format eftersom sfml inte stöder gif. //Roterad 180 grader så att den pekar uppåt vid spelstart #include <iostream> #include <SFML/System.hpp> #include <SFML/Window.hpp> #include <SFML/Graphics.hpp> #define SFML_STATIC //Se till så att det inte behövs extra DLL-filer #define M_PI 3.14159265358979323846 /* pi som statisk konstant*/ using namespace std; // utifall att konsollen behövs för felsökning int main (int argc, char *argv) { //main startar 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; //Styridsvagnens vinkel sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av spriteförflyttning"); // Skapa fönstret vi skall gå omkring i //Du skapar ett fönster som är 1024x768 med 32 bitars färgdjup. // Style innebär att det finns en minimera ikon [-] och en stäng ikon [x] //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 tank.png sf::Sprite tank(bild); //Skapar den grafiska spelpjäsen tank med grafiken från bild //nu har vi en stridsvagn tank.SetPosition(400.f, 300.f); //Placera ut stridsvagnen mitt på spelplanen //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. tank.SetCenter(tank.GetSize().x / 2 , tank.GetSize().y / 2); //Glöm inte att rita ut den med App.Draw i slutet. /*****************************************************************************************/ while (App.IsOpened()) { //while 1 startar sf::Event Event; //kolla om mus/tangentbord används while (App.GetEvent(Event)) { //while 2 börjar if (Event.Type == sf::Event::Closed) { //if 1 börjar App.Close();//avsluta programmet }//if 1 slutar if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) // en tangent har tryckts ner och tangenten är escape-tangenten { //if 2 börjar App.Close();//avsluta programmet } //if 2 slutar //Röra mus************************************************************************ //Få stridsvagnen att rotera när musen rör sig //static_cast<double> = förvandlar en float till en double, går att göra i C med t.ex: (float)x eller i C++ med: static_cast<float>(x)) if (App.GetInput().IsKeyDown(sf::Key::Up)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx*-1, newy*-1); //följer musen, * -1 eftersom rakt upp är 0 och inte 90 grader } if (App.GetInput().IsKeyDown(sf::Key::Down)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx, newy); //åk ifrån musen } //Se till så att det går att rotera stridsvagnen if (App.GetInput().IsKeyDown(sf::Key::A)) tank.Rotate(1); //Rotera spelaren motsols if (App.GetInput().IsKeyDown(sf::Key::S)) tank.Rotate(-1); //Rotera spelaren medsols //Här skriver vi in vad som händer i ett spel //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 App.Draw(tank); //Rita ut stridsvagnen App.Display(); //visa upp ändringarna för användaren } //while 2 slutar } //while 1 slutar //När alla ändringar gjorts avslutar vi programmet return 0; }
Provkör programmet och se om stridsvagnen går fram- och tillbaka när musknapparna trycks ner. Trycker du ner A och S tangenten skall stridsvagnen dessutom rotera mot- eller medsols. Inget nytt i det, koden är likvärdig den vi en gång använde för att flytta kor och pojkar i ett av de tidigaste projekten.
Automatisk styrning
[redigera]Tänk om du skulle vilja styra stridsvagnen automatiskt. Tänk om stridsvagnen skulle ändra riktning beroende på var musen finns på spelplanen? Koden för det är inte helt enkel, men så här kan den se ut.
//Få stridsvagnen att rotera när musen rör sig //static_cast<double> = förvandlar en float till en double, går att göra i C med t.ex: (float)x eller i C++ med: static_cast<float>(x)) if (App.GetInput().GetMouseX() <= tank.GetPosition().x) { tank.SetRotation (((-1* 360 / M_PI * (atan2(static_cast<double> (tank.GetPosition().y - App.GetInput().GetMouseY()) , static_cast<double> (tank.GetPosition().x – App.GetInput().GetMouseX()))))/2) +90 ); } else { tank.SetRotation (((-1* 360 / M_PI *(atan2(static_cast<double> (tank.GetPosition().y - App.GetInput().GetMouseY()) , static_cast<double> (tank.GetPosition().x – App.GetInput().GetMouseX()))))/2) +90 ); }
Multiplikation med -1 är för att spegelvända riktningarna och tillägget +90 finns för att vi började med stridsvagnen vänd uppåt mot överkanten (90 grader). Om stridsvagnen däremot skulle ha kanonpipan riktad neråt roterar du spriten 180 grader genom att skriva -90 istället för +90 när du beräknar vinkeln när man klickar med musen.
Koden blir:
[redigera]//Bild på stridsvagn från http://media.strategywiki.org/images/2/2a/MH_Tank.gif //Konverterad till png-format eftersom sfml inte stöder gif. //Roterad 180 grader så att den pekar uppåt vid spelstart #include <iostream> #include <SFML/System.hpp> #include <SFML/Window.hpp> #include <SFML/Graphics.hpp> #define SFML_STATIC //Se till så att det inte behövs extra DLL-filer #define M_PI 3.14159265358979323846 /* pi som statisk konstant*/ using namespace std; // utifall att konsollen behövs för felsökning int main (int argc, char *argv) { //main startar 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; //Styridsvagnens vinkel sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av spriteförflyttning"); // Skapa fönstret vi skall gå omkring i //Du skapar ett fönster som är 1024x768 med 32 bitars färgdjup. // Style innebär att det finns en minimera ikon [-] och en stäng ikon [x] //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 tank.png sf::Sprite tank(bild); //Skapar den grafiska spelpjäsen tank med grafiken från bild //nu har vi en stridsvagn tank.SetPosition(400.f, 300.f); //Placera ut stridsvagnen mitt på spelplanen //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. tank.SetCenter(tank.GetSize().x / 2 , tank.GetSize().y / 2); //Glöm inte att rita ut den med App.Draw i slutet. /*****************************************************************************************/ while (App.IsOpened()) { //while 1 startar sf::Event Event; //kolla om mus/tangentbord används while (App.GetEvent(Event)) { //while 2 börjar if (Event.Type == sf::Event::Closed) { //if 1 börjar App.Close();//avsluta programmet }//if 1 slutar if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) // en tangent har tryckts ner och tangenten är escape-tangenten { //if 2 börjar App.Close();//avsluta programmet } //if 2 slutar //Röra mus************************************************************************ //Få stridsvagnen att rotera när musen rör sig //static_cast<double> = förvandlar en float till en double, går att göra i C med t.ex: (float)x eller i C++ med: static_cast<float>(x)) if (App.GetInput().GetMouseX() <= tank.GetPosition().x) { tank.SetRotation (((-1* 360 / M_PI * (atan2(static_cast<double> (tank.GetPosition().y - App.GetInput().GetMouseY()) , static_cast<double> (tank.GetPosition().x - App.GetInput().GetMouseX()))))/2)+90); } else { tank.SetRotation (((-1* 360 / M_PI *(atan2(static_cast<double> (tank.GetPosition().y - App.GetInput().GetMouseY()) , static_cast<double> (tank.GetPosition().x - App.GetInput().GetMouseX()))))/2)+90); } if (App.GetInput().IsKeyDown(sf::Key::Up)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx*-1, newy*-1); //följer musen, * -1 eftersom rakt upp är 0 och inte 90 grader } if (App.GetInput().IsKeyDown(sf::Key::Down)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx, newy); //åk ifrån musen } //Se till så att det går att rotera stridsvagnen if (App.GetInput().IsKeyDown(sf::Key::A)) tank.Rotate(1); //Rotera spelaren motsols if (App.GetInput().IsKeyDown(sf::Key::S)) tank.Rotate(-1); //Rotera spelaren medsols //Här skriver vi in vad som händer i ett spel //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 App.Draw(tank); //Rita ut stridsvagnen App.Display(); //visa upp ändringarna för användaren } //while 2 slutar } //while 1 slutar //När alla ändringar gjorts avslutar vi programmet return 0; }
Den här koden visar flera saker:
- 1: Det går inte längre att rotera pansarvagnen, så fort du försöker att rotera den manuellt kommer den att söka sig mot muspekaren igen. Du kan ana ett svagt ryck i tanken när du trycker på rotationstangenterna, det är allt.
- 2: det går inte att dra musen och trycka ner tangenterna samtidigt. Det är en inneboende brist i sfml 1.6 att det bara går att hantera en händelse i taget (mus/tangentbord/joystick). Det går att skapa olika trådar/threads för att komma runt det problemet, men det är inte för nybörjare...
Automatisk vändning
[redigera]Att vända sig mot en speciell vinkel är så vanligt att det är lika bra att skapa en funktion av ovanstående kod som gör att en spelare vänder sig mot en speciell koordinat. För att få ändå mer nytta av funktionen ser vi till att vi får tillbaka vinkeln på spriten i return.
//Funktionsdeklaration double mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare); //vi får till baka ett double värde=spritens vinkel när vändningen är klar. //&spriote och &window är aktuell sprite och det fönster spelet har. // position_X och position_Y är de två koordinater figuren skall vända sig mot. //gradjusterare finns där för att justera vinkeln om det är så att figuren inte har 0 mot vänster.
Funktionen i botten efter main
double mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare) { double spritensvinkel; //Den vinkel vår sprite stannar på double PI = 3.14159265358979323846; //Utifall att pi inte är definierad if (position_X <= sprite.GetPosition().x) //Kollar om inte x eller y är likadana sprite.SetRotation (((-1* 360 / PI * (atan2(static_cast<double> (sprite.GetPosition().y - position_Y) , static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); else sprite.SetRotation (((-1* 360 / PI *(atan2(static_cast<double> (sprite.GetPosition().y - position_Y) , static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); window.Draw(sprite); //Rita ut ändringen spritensvinkel = sprite.GetRotation(); //ta fram vinkeln // cout << spritensvinkel << endl; kontrollera vinkeln i konsollfönstret return spritensvinkel; //Skicka tillbaka vinkeln }
Kör mot muspekaren
[redigera]I många spel är det så att man markerar en figur på spelplanen och sedan klickar man med musen. Då får man X- och Y-värdena för den plats man klickat på. Det kan vara rätt lämpligt att få figuren att vända sig mot den platsen. koden för att få fram var man klickat är:
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 //Lägg in den funktionen vi skapade här ovanför, t.ex. mot_koordinat(tank, App, App.GetInput().GetMouseX(), App.GetInput().GetMouseY(), 90); //musens X= App.GetInput().GetMouseX() //musens Y= App.GetInput().GetMouseY() } //vänster musknapp }//slut mus, vilken knapp som helst
Sista exemplet visar hur du får stridsvagnen att köra mot en punkt på spelplanen. Enklast är det att skapa en funktion som man bara matar med ett x,y värde oavsett om det är så att man klickar på spelplanen eller om det är något sopm händer i spelet.
Funktionsdeklarationen kan se ut så här:
Du kan se på exempelkoden i Programmera spel i C++ för nybörjare/Enkelt stridsvagnspel hur det fungerar i ett spel.
Slutgiltig kod:
[redigera]//Bild på stridsvagn från http://media.strategywiki.org/images/2/2a/MH_Tank.gif //Konverterad till png-format eftersom sfml inte stöder gif. //Roterad 180 grader så att den pekar uppåt vid spelstart #include <iostream> #include <SFML/System.hpp> #include <SFML/Window.hpp> #include <SFML/Graphics.hpp> #define SFML_STATIC //Se till så att det inte behövs extra DLL-filer #define M_PI 3.14159265358979323846 /* pi som statisk konstant*/ using namespace std; // utifall att konsollen behövs för felsökning //Funktionsdeklaration double mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare); //vi får till baka ett double värde=spritens vinkel när vändningen är klar. //&spriote och &window är aktuell sprite och det fönster spelet har. // position_X och position_Y är de två koordinater figuren skall vända sig mot. //gradjusterare finns där för att justera vinkeln om det är så att figuren inte har 0 mot vänster. int main (int argc, char *argv) { //main startar 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; //Styridsvagnens vinkel sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av spriteförflyttning"); // Skapa fönstret vi skall gå omkring i //Du skapar ett fönster som är 1024x768 med 32 bitars färgdjup. // Style innebär att det finns en minimera ikon [-] och en stäng ikon [x] //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 tank.png sf::Sprite tank(bild); //Skapar den grafiska spelpjäsen tank med grafiken från bild //nu har vi en stridsvagn tank.SetPosition(400.f, 300.f); //Placera ut stridsvagnen mitt på spelplanen //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. tank.SetCenter(tank.GetSize().x / 2 , tank.GetSize().y / 2); //Glöm inte att rita ut den med App.Draw i slutet. /*****************************************************************************************/ while (App.IsOpened()) { //while 1 startar sf::Event Event; //kolla om mus/tangentbord används while (App.GetEvent(Event)) { //while 2 börjar if (Event.Type == sf::Event::Closed) { //if 1 börjar App.Close();//avsluta programmet }//if 1 slutar if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) // en tangent har tryckts ner och tangenten är escape-tangenten { //if 2 börjar App.Close();//avsluta programmet } //if 2 slutar //Röra mus************************************************************************ //Få stridsvagnen att rotera när musen rör sig //static_cast<double> = förvandlar en float till en double, går att göra i C med t.ex: (float)x eller i C++ med: static_cast<float>(x)) if (App.GetInput().GetMouseX() <= tank.GetPosition().x) { tank.SetRotation (((-1* 360 / M_PI * (atan2(static_cast<double> (tank.GetPosition().y - App.GetInput().GetMouseY()) , static_cast<double> (tank.GetPosition().x - App.GetInput().GetMouseX()))))/2)+90); } else { tank.SetRotation (((-1* 360 / M_PI *(atan2(static_cast<double> (tank.GetPosition().y - App.GetInput().GetMouseY()) , static_cast<double> (tank.GetPosition().x - App.GetInput().GetMouseX()))))/2)+90); } if (App.GetInput().IsKeyDown(sf::Key::Up)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx*-1, newy*-1); //följer musen, * -1 eftersom rakt upp är 0 och inte 90 grader } if (App.GetInput().IsKeyDown(sf::Key::Down)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx, newy); //åk ifrån musen } //Se till så att det går att rotera stridsvagnen if (App.GetInput().IsKeyDown(sf::Key::A)) tank.Rotate(1); //Rotera spelaren motsols if (App.GetInput().IsKeyDown(sf::Key::S)) tank.Rotate(-1); //Rotera spelaren medsols //Tryck ner en pilknapp och kör stridsvagnen if (App.GetInput().IsKeyDown(sf::Key::Up)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx*-1, newy*-1); //följer musen, * -1 eftersom rakt upp är 0 och inte 90 grader } if (App.GetInput().IsKeyDown(sf::Key::Down)) { vinkel = tank.GetRotation(); // 0 = rakt upp, startposition. Motsols 0-360 grader. newx= sin(vinkel*M_PI/180.0) * speed; newy= cos(vinkel*M_PI/180.0) * speed; tank.Move(newx, newy); //åk ifrån musen } 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 //Lägg in den funktionen vi skapade här ovanför, t.ex. mot_koordinat(tank, App, App.GetInput().GetMouseX(), App.GetInput().GetMouseY(), 90); //musens X= App.GetInput().GetMouseX() //musens Y= App.GetInput().GetMouseY() } //vänster musknapp }//slut mus, vilken knapp som helst } //while 2 slutar //Här skriver vi in vad som händer i ett spel //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 App.Draw(tank); //Rita ut stridsvagnen App.Display(); //visa upp ändringarna för användaren } //while 1 slutar //När alla ändringar gjorts avslutar vi programmet return 0; } double mot_koordinat(sf::Sprite &sprite, sf::RenderWindow &window, double position_X, double position_Y, int gradjusterare) { double spritensvinkel; //Den vinkel vår sprite stannar på double PI = 3.14159265358979323846; //Utifall att pi inte är definierad if (position_X <= sprite.GetPosition().x) //Kollar om inte x eller y är likadana sprite.SetRotation (((-1* 360 / PI * (atan2(static_cast<double> (sprite.GetPosition().y - position_Y) , static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); else sprite.SetRotation (((-1* 360 / PI *(atan2(static_cast<double> (sprite.GetPosition().y - position_Y) , static_cast<double> (sprite.GetPosition().x - position_X))))/2)+gradjusterare); window.Draw(sprite); //Rita ut ändringen spritensvinkel = sprite.GetRotation(); //ta fram vinkeln // cout << spritensvinkel << endl; kontrollera vinkeln i konsollfönstret return spritensvinkel; //Skicka tillbaka vinkeln }