Programmera spel i C++ för nybörjare/Sprites och spelpjäser 1


Sprites och spelpjäser 1

redigera

Baserat 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

redigera

Vilket 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

redigera

Nä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

redigera

Nä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

redigera

Enklaste 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?

redigera

Om 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

redigera

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