Programmera spel i C++ för nybörjare/Sprites och spelpjäser 1
Sprites och spelpjäser 1
redigeraBaserat på VC++ 2010 express, Win 7 och SFML 1.6, du bör ha gjort installationerna för ett fungerande SFML-fönster i ditt projekt innan du börjar.
Det mest lättfattliga sättet att få in grafik i spelet är att ha lösa filer och ladda in dem i spelet. Vi kan börja med att försöka få in en liten pojke/ko/bil eller välj vad du vill som är 32x32 pixels stor,. Det går att ställa in Google i avancerade inställningar så att Google söker bilder av exakt storlek.
Grafikformat
redigeraVilket format skall man välja? De vanligaste formaten stöds.
Jpg/jpeg bilder är enklast att hitta på Internet. Den stora nackdelen är att det är väldigt svårt att få ett fält i jpg bilder där samtliga pixels har exakt samma färg, därmed blir det onödigt svårt att skapa genomskinliga bakgrunder.
Bmp/bitmap bilder har fullt stöd i Windows, de kan redigeras i alla grafikprogram men blir onödigt stora i storlek.
Png är, enligt mig, det bästa valet eftersom de går att krympa på ett sätt liknande jpg men går att ge genomskinlig bakgrund. De stöder dessutom alfa-kanalen som avgör hur genomskinlig en bild skall vara. Om du vill göra en färg genomskinlig direkt i programmet skriver du t.ex:
<namn på bild>.CreateMaskFromColor(sf::Color(192,248,0));
Färgen är ett RGB (från 0-255) värde och går att hitta med hjälp av något mer avancerat bildbehandlingsprogram.
När man skapar sprites och spelgrafik är det väldigt viktigt att den håller exakta mått, och ett sätt att få till bilderna precis som du vill ha dem är att skapa dem i vektorgrafik och sedan spara om dem till png format när du är nöjd med utseendet. Ett känt gratisprogram för vektorgrafik är ”Inkscape” och det duger gott till enklare bilder. Vill du jobba med pixelgrafik istället är ”Gimp” ett känt gratisprogram med många funktioner du kan ha nytta av.
Storlek
redigeraNär man har grafik i spel finns en gammal tradition av att följa det binära systemet, dvs. storleken skall vara i serien:
1 2 4 8 16 32 64 128 256 512 1024 osv.
Även om tanken bakom detta är historisk förekommer det även i nya system att det uppstår ”glitches”, eller vita streck och punkter när grafiken används om man inte följer systemet av storlekar. Så ett gott råd är att även du följer det, om du inte har någon väldigt speciell anledning att inte göra så.
Dags att skrivs kod för din första spelpjäs. Grundkoden för SFML för att läsa in en bildfil som heter sprite.png är:
sf::Image Image; //tomt bildobjekt som heter Image skapas if (!Image.LoadFromFile("sprite.png")) return EXIT_FAILURE; //Programmet försöker att ladda in en bildfil i det tomma bildobjektet
Var skall man spara filen då? I samma mapp som main.cpp filen. Som standard ligger alla projekt under ”Mina Dokument” mappen i Windows. Om ditt projekt är döpt till sfml_grafiktest sparar du troligen bildfilen i mappen:
C:\Användare\<ditt namn>\Mina dokumen\Visual Studio 2010\Projects\sfml_grafiktest\sfml_grafiktest\
Det som då blir inläst i minnet kan vara en stor karta av bilder som man sedan kan kopiera ut mindre bitar av. Från dessa kan man sedan skapa en sk ”sprite”, en bit grafik som påverkas av din programkod. För att skapa en sprite ur en bild skriver man:
sf::Sprite Sprite(Image);
Spriten får då bilden från den inladdade Image, filen sprite.png.
Sedan skall vi placera ut bilden på spelplanen och det görs med
Sprite.SetPosition(200.f, 100.f);
200 är x-led (vågrät) och 100 är y-led (lodrät)
Hastighet
redigeraNär man kör omkring spelare över spelplanen beräknar datorn hastigheten efter sin egen interna klocka. Det är inte alltid så lyckat. Om man har en gammal stationär dator från 1990 talet går alltså spelarna långsammare än när du har en nyinköpt laptop. Ett sätt att komma runt problemet är att använda en konstant baserad på hur snabbt bilderna växlar på skärmen. Då får du ett värde som gör att hastigheten i spelet håller sig någorlunda likvärdig oberoende av hur gammal eller ny dator du använder. Variabeln för det i SFML är
float ElapsedTime = App.GetFrameTime();
Kör omkring med spelpjäsen
redigeraEnklaste sättet att köra omkring med figuren är att använda tangentbordsstangenterna. OBS! Dessa rutiner är totalt omskrivna i SFML 2.0
Vi kontrollerar vilka tangenter som tryckts ner med koden:
if (App.GetInput().IsKeyDown(sf::Key::Left)) Sprite.Move(-100 * ElapsedTime, 0); if (App.GetInput().IsKeyDown(sf::Key::Right)) Sprite.Move( 100 * ElapsedTime, 0); if (App.GetInput().IsKeyDown(sf::Key::Up)) Sprite.Move(0, -100 * ElapsedTime); if (App.GetInput().IsKeyDown(sf::Key::Down)) Sprite.Move(0, 100 * ElapsedTime);
Istället för att skriva Left kan man skriva A istället, om man vill använda speciella tangenter på tangentbordet:
if (App.GetInput().IsKeyDown(sf::Key::A)) Sprite.Move(-100 * ElapsedTime, 0);
Det fungerar bara med stora bokstäver, undvik att använda Å,Ä eller Ö, utgå ifrån att alla program använder amerikansk tangentbordsstandard för att du skall få så litet problem som möjligt.
Slutligen går det att rotera en spelare runt sin egen axel med koden:
if (App.GetInput().IsKeyDown(sf::Key::Add)) Sprite.Rotate(- 100 * ElapsedTime); //medsols if (App.GetInput().IsKeyDown(sf::Key::Subtract)) Sprite.Rotate(+ 100 * ElapsedTime); //motsols
Tryck ner ”+” tangenten så roterar den medsols, ”-” för motsols, det går att kombinera med att den rör på sig.
Om du använder en laptop som saknar sifferuppsättningen till höger om tangentbordet kommer du inte åt + och - tangenter så lätt. Då får du använda andra tangenter istället. LSHIFT och RSHIFT är de två tangenterna som ger stor bokstav på vänster och höger sida. Det kan vara godtagbara alternativatangenter.
Nu testar vi ett första program med en liten bild som vi kan köra omkring på spelplanen. Om du har kod sedan tidigare raderar du den och klistrar in följande istället:
#include <iostream> #include <SFML\System.hpp> #include <SFML\Graphics.hpp> #include <SFML\Window.hpp> int main() { //Början av programkörningen sf::RenderWindow App(sf::VideoMode(800, 600, 32), "SFML grafiktest"); // Skapa fönstret vi skall visa färgerna i float ElapsedTime = App.GetFrameTime(); //Skapar en konstant för att hålla hastigheten likvärdig på olika datorer sf::Image Image; //skapa en tom bildhållare som heter Image if (!Image.LoadFromFile("boy.png")) return EXIT_FAILURE; //fyll den tomma bildhållaren med bilden boy.png //bilden är en 32x32 .png jag hittade ute på Internet med en googlesökning sf::Sprite Sprite(Image); //Skapar den grafiska spelpjäsen Sprite med grafiken från Image Sprite.SetPosition(200.f, 100.f); //Placera ut bilden while (App.IsOpened()) // Start spel-loopen { //while 1 sf::Event Event; while (App.GetEvent(Event)) // Ta hand om händelser { //while 2 if (Event.Type == sf::Event::Closed) //kryssat på [x] symbolen? stäng programmet App.Close(); if (Event.Type == sf::Event::KeyPressed) // En tangent har tryckts ner { //if 1 if (Event.Key.Code == sf::Key::Escape) // ESC tangenten = stäng programmet App.Close(); } //slut if 1 } //slut, while 2 //Börja med själva spelets olika funktioner ElapsedTime = App.GetFrameTime(); //för att få en konstant hastighet på olika datorer if (App.GetInput().IsKeyDown(sf::Key::Left)) Sprite.Move(-100 * ElapsedTime, 0); //Om man trycker ner vänster piltangent går figuren 100 "steg" // längre åt vänster än vad den var tidigare. //Figurens hastighet går alltså att ställa med den variabeln. if (App.GetInput().IsKeyDown(sf::Key::Right)) Sprite.Move( 100 * ElapsedTime, 0); if (App.GetInput().IsKeyDown(sf::Key::Up)) Sprite.Move(0, -100 * ElapsedTime); if (App.GetInput().IsKeyDown(sf::Key::Down)) Sprite.Move(0, 100 * ElapsedTime); //Rotation if (App.GetInput().IsKeyDown(sf::Key::Add)) Sprite.Rotate(- 100 * ElapsedTime); //Rotera spelaren medsols if (App.GetInput().IsKeyDown(sf::Key::Subtract)) Sprite.Rotate(+ 100 * ElapsedTime); //Rotera spelaren mortsols App.Clear(sf::Color(0, 255, 0)); //rensa allt i fönstret och ersätt med grönfärg App.Draw(Sprite); //Rita upp figuren på den yta spelaren ser App.Display(); //visa upp ändringarna för användaren } //slut, while 1 return 0; } //slut på programkörningen
Flera tangenter fungerar inte samtidigt?
redigeraOm du lägger till kod och skapar två figurer som rör sig samtidigt genom att en figur styrs med piltangenterna och en styrs t.ex. med W, A, S, Z tangenterna kommer du att märka att det inte går att trycka ner flera tangenter samtidigt. Orsaken kallas för "keyboard ghosting" och beror helt och hållet på hårdvaran (tangentbordet). SFML har inte med det att göra alls. Du kan läsa mer om keyboard ghosting på engelska om du följer länken här nedanför, det finns t.o.m. ett litet program direkt på sidan där du kan pröva ditt eget tangentbord och se vilka tangent-kombinationer som går att göra, och vilka som inte fungerar:
http://www.microsoft.com/appliedsciences/content/projects/KeyboardGhostingDemo.aspx
Kod för 2.3.X
redigeraSFML har blivit mer avancerat från version 2.0. Här finns en likvärdig kod som fungerar i den nya versionen av SFML:
#include <iostream> #include <SFML/Graphics.hpp> int main() { sf::Clock NyKlocka; //skapa en klocka const float Speed = 5.f; //ge den grundhastigheten 5 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(800, 600), "SFML -första spelaren"); //Tillåt synkronisering till bildskärmens uppdatering, avstängd som standard window.setVerticalSyncEnabled(true); //Skapa spelpjäs //Ladda in den bild som skall visas på spelaren //En bild med en liten pojke sf::Texture Ny_texture; if (!Ny_texture.loadFromFile("boy.png")) { std::cout << "Kan inte ladda bildfil. " << std::endl; } //Skapa spelaren/spriten sf::Sprite sprite; //Ge spelaren bilden av pojken sprite.setTexture(Ny_texture); //Placera ut pojken på spelplanen sprite.setPosition(sf::Vector2f(10, 30)); 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 = 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(); //Flytta spelpjäsen sprite.move(sf::Vector2f(Left, Top)); //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(sf::Color(0, 255, 0)); //rensa allt i fönstret och ersätt med grönfärg //Rita ut pojken window.draw(sprite); window.display(); } return 0; }