Programmera spel i C++ för nybörjare/Programmera med tid
Att programmera med tid
[redigera]Anpassad för SFML 1.6
Det finns egentligen två olika sätt att använda sig av tid när det gäller alla typer av spelprogrammering. Det ena är att se till så att man har en fast ”framerate”, dvs antalet visade bilder per sekund, och bygger upp spelets rörelser och animationer utifrån det.
Fast tid
[redigera]Man antar att spelet hela tiden visar upp ett visst antal rutor i sekunden och det antalet ändras inte. Fördelen med detta är att spelet går lika fort oavsett processorklocka. Om du vill se hur det inte skall se ut kan du t.ex. ladda hem det gamla DOS spelet ”Command and Conquer 1” och köra det på en modern laptop. Innan du byggt din första lastbil kommer datorn att bomba dig med atombomber helt enkelt eftersom spelet bygger på ett antagande att du och datorn gör ungefär lika många handlingar i sekunden, men med moderna processorer gör datorn tusentals handlingar när du gjort en så spelet är ospelbart med moderna datorers processorer.
Nackdelen är att den omvända situationen kan uppstå när du har en gammal dator som skall köra ditt moderna spel där du har en förväntad framerate på 60. Då kommer framförallt saker som rör sig: fallande stenblock, väggar som rasar, snöbollar i luften osv att gå ryckigt eftersom de helt enkelt inte kan röra sig i den hastighet du skapat spelet i.
Rörlig tid
[redigera]Proffsen röstar på rörlig tid. Då går spelet snabbt på en modern dator men långsamt på en gammal dator, men poängen är att alla handlingar du gör kommer att utföras på ett, för ögat, smidigt sätt så länge frameraten ligger över 25 rutor i sekunden vilket är gränsen för ”hackig” grafik.
Klockan
[redigera]I SFML har du en inbyggd klocka som beräknar tiden i sekunder (float). Klockan är en klass som heter sf::Clock och koden för att skapa en klocka är enkel. Så här skapar du en ny klocka i spelet:
sf::Clock NyKlocka;
Skall du ställa in tiden på 0, ungefär som man återställer ett tidtagarur skriver du:
NyKlocka.reset();
Vill du veta hur lång tid de gått sedan klockan nollställdes skriver du:
float TidSomGaott; TidSomGaott = NyKlocka.GetElapsedTime(); cout << TidSomGaott << endl; //så kan du skriva ut tiden
Varför räknas tiden i float? Du vill antagligen kunna räkna i både hundra- och tusendelssekunder. Skulle tiden vara en integer/heltal skulle alla beräkningar visa att tiden = 0 om det sker något i spelet som har en tid under en hel sekund, och det vill vi definitivt inte använda.
Hastighet utan tidsgräns, exempel
[redigera]Om du vill styra en figur på spelplanen, t.ex. en ko, kan du alltså ge den en fast hastighet. Ger vi den hastigheten 50 kommer den att röra sig 50 pixels varje gång skärmbilden uppdateras. Om vi har en framerate på 25 innebär det att figuren/kon rör sig 25 * 50 = 1250 pixels i sekunden men en framerate på 60 innebär en förflyttning på 60 * 50 = 3000 pixels i sekunden.
Kod:
[redigera]const float Speed = 50.f; //ge den hastigheten 50 float Left = 0.f; //den rör sig inte i sidled float Top = 0.f; //den rör sig inte i höjdled while (NamnPaoSpelfoenstret.IsOpened()) //när spelet startar { //Hastighet Vänster/Höger/Upp/Ner = 50 pixels/frame i samtliga fall if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Left)) Left -= Speed; if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Right)) Left += Speed; if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Up)) Top -= Speed; if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Down)) Top += Speed; }
Hastighet med tidsgräns och klocka
[redigera]Om du vill att en figur skall röra sig lika snabbt på spelplanen oavsett vilken processor eller annan hårdvara datorn har, måste du utgå från hur snabbt bilderna visas och multiplicera hastigheten med det.
Kod:
[redigera]sf::Clock NyKlocka; //skapa en klocka const float Speed = 50.f; //Ge figuren en hastighet float Left = 0.f; //den rör sig inte i sidled float Top = 0.f; //den rör sig inte i höjdled while (NamnPaoSpelfoenstret.IsOpened())//när spelet startar { float TidSomGaott; TidSomGaott = NyKlocka.GetElapsedTime(); //Hastighet Vänster/Höger/Upp/Ner = // 50 * 0.017 = 0.85 pixlar/frame för framerate 60 i samtliga fall // 50 * 0.14 = 7 pixlar/frame för framerate 30 i samtliga fall if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Left)) Left -= Speed * TidSomGaott; if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Right)) Left += Speed * TidSomGaott; if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Up)) Top -= Speed * TidSomGaott; if (NamnPaoSpelfoenstret.GetInput().IsKeyDown(sf::Key::Down)) Top += Speed * TidSomGaott; }
Anta att frameraten är 60 på en snabb dator och 30 på en långsam. Då rör sig figuren 60 * 50 = 3000 pixels på en sekund på den snabba men 1500 pixels på den långsamma. Men för att visa upp 60 bildrutor tar det 1 sekund på en snabb dator men 2 sekunder på den långsamma, så sluthastigheten blir samma (3000 * 1 = 1500 * 2).
Hastighet med tidsgräns utan klocka
[redigera]Om du inte vill hålla på och beräkna tid finns det ett annat sätt, man kan beräkna tiden sedan senaste uppdateringen i spelfönstret. Det gör man med kommandot:
NamnPaoSpelfoenstret.GetFrameTime();
då får du tiden sedan senaste uppdateringen i sekunder. Om du har en framerate på 60/sekunden blir resultatet 1/60 = 0.016666666666 medan om du har en framerate på 25 blir resultatet 1/25= 0.04
Ta reda på spelets framerate
[redigera]Vill man ta reda på vilken framerate man har i spelet kan man göra motsatsen:
sf::Clock NyKlocka; //skapa en klocka while (NamnPaoSpelfoenstret.IsOpened()) //när spelet startar { float Framerate = 0.0f; Framerate = 1.f / NyKlocka.GetElapsedTime(); //ta fram frameraten cout << ”Frameraten = ” << Framerate << endl; //skriv ut frameraten NyKlocka.Reset(); //nollställ klockan } // Eller : while (NamnPaoSpelfoenstret.IsOpened())//när spelet startar { float Framerate = 0.0f; Framerate = 1.f / NamnPaoSpelfoenstret.GetFrameTime(); //Ta fram tid mellan varje visning av skärmbilden }
Vertical sync
[redigera]Om du inte får upp en högre framerate på spelet än ca 60 hur mycket du än försöker beror det på att spelet har en fast "vertical sync". På svenska innebär det att spelet inte kan visa fler bilder än bildskärmens Hertztal/uppdateringar varje sekund, vilket i grund och botten är en fördel eftersom spelaren ändå inte kan se bilder som ritas upp innan datorskärmen kan visa dem. Ibland kanske du ändå vill se exakt hur snabbt spelet är oavsett vad skärmen kan visa, och då skriver du bara i koden:
NamnPaoSpelfoenstret.UseVerticalSync(false);
I början. Observera att "false" är standard, du måste aktivt sätta den till "true" om du vill låsa spelets uppdateringshastighet mot skärmens uppdateringshastighet.
Fast framerate
[redigera]Slutligen kan du rent programmeringsmässigt tvinga spelet att ha en viss framerate genom att skriva:
NamnPaoSpelfoenstret.SetFramerateLimit(60); // begränsad till 60 bilder i sekunden
Undvik det om du tror att spelet kommer att spelas på äldre datorer som inte klarar av så snabb uppdatering. Om du inte vill ha någon begränsning alls skriver du:
NamnPaoSpelfoenstret.SetFramerateLimit(0); // ingen begränsning
SFML2.3.X
[redigera]SFML version 2 hanterar både fönster och kontroller annorlunda. Det är mycket som inte stämmer och just synkroniseringen har fått en rejäl genomgång.
Följande kod fungerar i version 2.3.X
#include <iostream> #include <SFML/Graphics.hpp> int main() { sf::Clock NyKlocka; //skapa en klocka const float Speed = 50.f; //ge den hastigheten 50 float Left = 0.f; //den rör sig inte i sidled float Top = 0.f; //den rör sig inte i höjdled sf::RenderWindow window(sf::VideoMode(300, 300), "SFML -tidsberäkning"); //Tillåt synkronisering till bildskärmens uppdatering, avstängd som standard window.setVerticalSyncEnabled(true); sf::CircleShape shape(100.f); shape.setFillColor(sf::Color::Green); while (window.isOpen()) //när spelet startar { //Se hur mycket tiden som gått sf::Time elapsed1 = NyKlocka.getElapsedTime(); //Starta om klockan NyKlocka.restart(); //Hastighet Vänster/Höger/Upp/Ner = // 50 * 0.017 = 0.85 pixlar/frame för framerate 60 i samtliga fall // 50 * 0.14 = 7 pixlar/frame för framerate 30 i samtliga fall if (sf::Keyboard::isKeyPressed(sf::Keyboard::Left)) Left -= Speed * elapsed1.asSeconds(); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Right)) Left += Speed * elapsed1.asSeconds(); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Up)) Top -= Speed * elapsed1.asSeconds(); if (sf::Keyboard::isKeyPressed(sf::Keyboard::Down)) Top += Speed * elapsed1.asSeconds(); //Kontrollera att hastigheten ändras std::cout << "LeftSpeed= "<< Left << std::endl; sf::Event event; while (window.pollEvent(event)) { if (event.type == sf::Event::Closed) window.close(); } window.clear(); window.draw(shape); window.display(); } return 0; }