Programmera spel i C++ för nybörjare/Animation 2

Från Wikibooks
Hoppa till navigering Hoppa till sök


(Genomgången förutsätter att du har en fungerande installation av Microsoft Visual C++ 2010 Express och SFML 1.6 på din dator.)

OBS!!!! Detta är en demonstration av hur en animation visas. Den här koden fungerar INTE inuti ett spel!

Explosioner och enkla animationer[redigera]

Den enklaste formen av animation i alla spel är explosionen. Samtidigt är det troligen också den vanligaste animationen. Fördelen med explosionen är att den inte rör på sig. Man behöver bara placera ut den på spelplanen och spela upp den. Här följer ett komplett kommenterat exempel som beskriver hur en animation av en explosion går till. Redan nu vill jag påpeka att den kod som följer här nedanför inte fungerar i ett spel (se nästa kapitel istället) man jag använder den som ett enkelt exempel för hur animationer går till, och den är bra att använda som grund för att synkronisera ljud och bild i animationer i allmänhet.

Vi börjar med att skapa en synnerligen enkel klass, en klass som bara innehåller en sprite - som skall bli vår explosion. I SFML måste man ha en sprite för att ö.h.t. kunna visa upp en bild på bildskärmen.

//Skapa explosionsklassen, så enkel som möjligt
class Explosion
{
public :
 sf::Sprite Sprite; // en per instans
};

När vi sedan låter explosionen synas behöver vi också en funktion för den. Funktionen kan se ut så här:

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::RenderWindow &App );
//Funktionen som initierar explosionen
  • Den heter pang.
  • Explosionen skall placeras ut på x,y koordinaterna.
  • Den är en instans av Explosion som har en sprite vi använder oss av.
  • Bildfilen är den spritemap vi använder för att skapa animationen.
  • Renderwindow är det fönster vi visar spelet i.

För enkelhetens skull har jag tagit samma explosions spritemap som i spelexemplet "Space shooter". Du hittar den här:

http://cdn.pimpmyspace.org/media/pms/c/b9/9e/ez/xplosion17.png

Den animationen är 5x5 bildrutor stor där varje ruta är 64x64 pixels.


Main funktionen blir väldigt simpel, det är trots allt bara ett exempel:

 #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
 using namespace std;    // utifall att konsollen behövs för felsökning
 
 class Explosion
 {
 public :
   sf::Sprite Sprite; // en per instans
 };
 
 
 void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::RenderWindow &App );
 //Funktionen som initierar explosionen
 
int main (int argc, char *argv)
{ //main startar

         //ladda in bild för explosionen
        sf::Image explosionsbildmap;
         //explosionens spritemap är 5 x 5 rutor 64x64 pixels stora
        if (!explosionsbildmap.LoadFromFile("xplosion17.png"))
  {
  cout << "Kan inte hitta bilden: xplosion17.png " << endl;
  }

        //Skapa en kopia av klassen explosionen
        Explosion Explosionskopia;

       sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av explosion"); 
      // Skapa fönstret vi skall testa explosionen i
      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::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                    App.Close();//avsluta programmet

                    if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Return))
                    //Visa upp explosionen om man trycker ner return-knappen
                   pang(100, 100, Explosionskopia, explosionsbildmap, App);

                  //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.Display(); //visa upp ändringarna för användaren
               } //while 2 slutar
         } //while 1 slutar

} //main slutar


Vad är det som händer? När vi trycker ner return/enter tangenten ritas en explosion ut på koordinaterna 100,100. Kordinaterna kan bytas ut och det kan naturligtvis vara vad som helst i programmeringen av ett spel som avgör om funktionen skall användas. Det som är intressant är själva koden till funktionen:

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::RenderWindow &App )

vad gör egentligen den?

Explosionsanimation[redigera]

För att kunna skapa en animation i SFML används vanligtvis.

  • En spritemap
  • En sprite
  • En klocka


Det första man måste göra är att beräkna hur stor varje bildruta är. För att exemplet skall fungera måste varje bildruta i animationen vara exakt lika stor. Högerklicka på bilden och välj egenskaper. Då står det bildens totala bredd och höjd. Ta fram en miniräknare och dividera bredden med antalet olika bilder som finns i sekvensen och gör sedan likadant med höjden. Då får du fram varje enskild bildrutas bredd och höjd.

Du måste ha en sprite. SFML är utformat så att du måste ha en sprite för att kunna visa en bild. Kommer du ihåg att t.o.m. bakgrunden i ett spel är en sprite. Detta är speciellt för SFML och open GL.

Om man vill ta fram en speciell del i en spritemap och använda den på en sprite är koden t.ex.:

Sprite.SetSubRect(sf::IntRect(0,0,64,64)); 

för att visa upp den bild som är längst upp till vänster i spritemapen om bilderna är 64x64 pixels stora. 0,0 är det övre vänstra hörnet i den bild som skall visas upp medan 64,64 (i exemplet) är det nedre högra hörnet på den del av bilden som skall visas upp. Koden fungerar enbart på fyrkantiga bildrutor.

Du måste ha en klocka som avgör när vi skall hoppa till nästa bildruta. Animationsföljden blir:

  • 1: Nollställ klockan och hoppa till första raden
  • 2: Ställ dig på första rutan på första raden och visa upp den för spelaren.
  • 3: Fortsätt att visa bilden tills tillräckligt mycket tid förflutit. 0.1 till 0.3 sekund kan vara lämpligt.
  • 4: Gå till nästa ruta och gör likadant, sedan nästa, sedan nästa tills raden är slut.
  • 5: Nollställ klockan. och hoppa till nästa rad
  • 6: Repetera alltihop tills det är slut på rader.

Om man också har ett ljud som skall spelas upp till explosionen kan det vara vettigt att beräkna animationens längd i tid utifrån explosionsljudets tid så att de börjar och slutar ungefär samtidigt. I exemplet är det 0.01 sekund mellan varje bild och det är 25 bilder vilket ger en animation på ca 0.25 sekunder. Om ljudet låter längre tid än det kan man alltid redigera om det i ett ljudbehandlingsprogram som "Audacity" och "trycka ihop det" så att det inte låter längre än 0.25 sekunder. Läs längre ner om ljud i detalj.

Om man bakar in den sekvensen i vår funktion kallad pang ser det ut så här:

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::RenderWindow &App )
{ 
    
      int rutorY = 0; //max 5, bildrutor i y-led
     // int rutorX = 0; //max 5, bildrutor i x led
	   //Obs, eftersom vi baserar animationen på tid och inte på vilken ruta man står på
	   //behöver vi bara bekymra oss om vilken rad av rutor vi står på.
      
      int bildbredd = 64; //varje enskild rutas bredd
      int bildhojd = 64; //varje enskild rutas höjd
      sf::Clock ExplosionClock;
      //Skapa en timer för explosionen
      float visningstid = 0.01f; //0.01 sekund mellan varje


      Klassinstans.Sprite.SetPosition(x,y);  
      //Placera ut animationen på rätt plats på spelplanen

      Klassinstans.Sprite.SetImage(bildfilsnamn); //ge animationen bilden av explosionen
      ExplosionClock.Reset(); //Se till så att klockan verkligen är 0

      while ( rutorY < 5) //Så länge man är på någon rad
      {//while i y-led
              while (ExplosionClock.GetElapsedTime() < (visningstid * 5)) //Så länge man är på någon bild i en rad          
              { //while i x-led
                  //Välj ut vilken av de 5 bilderna vi skall  ta 
	          //Genom att flytta in 64 eller 128 eller 192 osv.
		  //pixlar in på bildkartan

                  if (ExplosionClock.GetElapsedTime() >= 0.0 && ExplosionClock.GetElapsedTime() < visningstid)
                  {
                   bildbredd = 64; //Gå till första bilden
                   }
                   else if (ExplosionClock.GetElapsedTime() > visningstid && ExplosionClock.GetElapsedTime() < (visningstid * 2))
                   {
                    bildbredd = 128; //Gå till bild 2
                    }
                    else if (ExplosionClock.GetElapsedTime() > (visningstid * 2) && ExplosionClock.GetElapsedTime() < (visningstid * 3))
                    {
                     bildbredd = 192; //Gå till bild 3
                     }
                     else if (ExplosionClock.GetElapsedTime() > (visningstid * 3) && ExplosionClock.GetElapsedTime() < (visningstid * 4))
                     {
                      bildbredd = 256; //Gå till nästa bild
                      }
                     else if (ExplosionClock.GetElapsedTime() > (visningstid * 4) && ExplosionClock.GetElapsedTime() <= (visningstid * 5))
                     {
                      bildbredd = 320; //Gå till nästa bild
                     }
              
                      Klassinstans.Sprite.SetSubRect(sf::IntRect(bildbredd-64,bildhojd-64,bildbredd,bildhojd)); //Visa  bilden
                      App.Draw(Klassinstans.Sprite);//Rita ut explosionen
                      App.Display(); //visa upp ändringarna för användaren
                   

              } //slut i x-led
              ExplosionClock.Reset(); // sätter om klockan till noll
              bildhojd = bildhojd + 64;//Hoppa ner en rad
              rutorY = rutorY + 1; //Öka till nästa rad
      } //while i y-led
}//pang slutar

Färdig kod[redigera]

Observera att i det här enkla exemplet kan du bara stänga fönstret med ESC-tangenten och inte genom att trycka på [x] på fönsterlisten.

Det finns en bugg i koden men den är medveten för att förenkla koden så att den blir lättare för nybörjare att förstå. I C++ räknar man alltid från 0, dvs. första bildrutan som skall visas har egentligen koordinaterna 0,0,63,63. Vanligtvis fungerar animationerna ändå trots att man börjar med 1 istället för 0 och bildrutorna blir 1 pixel för stora, men om du upptäcker "rester" från andra bilder i animationen eller märkliga streck, beror det antagligen på den här buggen. Då är det bara att räkna om hela animationen med basen 0 istället för basen 1 som används här.


 #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
 using namespace std;    // utifall att konsollen behövs för felsökning
 
 class Explosion
 {
 public :
   sf::Sprite Sprite; // en per instans
 };
 
 
 void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::RenderWindow &App );
 //Funktionen som initierar explosionen
 
int main (int argc, char *argv)
{ //main startar

         //ladda in bild för explosionen
        sf::Image explosionsbildmap;
         //explosionens spritemap är 5 x 5 rutor 64x64 pixels stora
        if (!explosionsbildmap.LoadFromFile("xplosion17.png"))
  {
  cout << "Kan inte hitta bilden: xplosion17.png " << endl;
  }

        //Skapa en kopia av klassen explosionen
        Explosion Explosionskopia;

       sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av explosion"); 
      // Skapa fönstret vi skall testa explosionen i
      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::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                    App.Close();//avsluta programmet

                    if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Return))
                    //Visa upp explosionen om man trycker ner return-knappen
                   pang(100, 100, Explosionskopia, explosionsbildmap, App);

                  //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.Display(); //visa upp ändringarna för användaren
               } //while 2 slutar
         } //while 1 slutar

} //main slutar

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::RenderWindow &App )
{ 
    
      int rutorY = 0; //max 5, bildrutor i y-led
     // int rutorX = 0; //max 5, bildrutor i x led
	   //Obs, eftersom vi baserar animationen på tid och inte på vilken ruta man står på
	   //behöver vi bara bekymra oss om vilken rad av rutor vi står på.
      
      int bildbredd = 64; //varje enskild rutas bredd
      int bildhojd = 64; //varje enskild rutas höjd
      sf::Clock ExplosionClock;
      //Skapa en timer för explosionen
      float visningstid = 0.01f; //0.01 sekund mellan varje


      Klassinstans.Sprite.SetPosition(x,y);  
      //Placera ut animationen på rätt plats på spelplanen

      Klassinstans.Sprite.SetImage(bildfilsnamn); //ge animationen bilden av explosionen
      ExplosionClock.Reset(); //Se till så att klockan verkligen är 0

      while ( rutorY < 5) //Så länge man är på någon rad
      {//while i y-led
              while (ExplosionClock.GetElapsedTime() < (visningstid * 5)) //Så länge man är på någon bild i en rad          
              { //while i x-led
                  //Välj ut vilken av de 5 bilderna vi skall  ta 
				   //Genom att flyttain 64 eller 128 eller 192 osv.
				   //Pixlar in på bildkartan

                  if (ExplosionClock.GetElapsedTime() >= 0.0 && ExplosionClock.GetElapsedTime() < visningstid)
                  {
                   bildbredd = 64; //Gå till första bilden
                   }
                   else if (ExplosionClock.GetElapsedTime() > visningstid && ExplosionClock.GetElapsedTime() < (visningstid * 2))
                   {
                    bildbredd = 128; //Gå till bild 2
                    }
                    else if (ExplosionClock.GetElapsedTime() > (visningstid * 2) && ExplosionClock.GetElapsedTime() < (visningstid * 3))
                    {
                     bildbredd = 192; //Gå till bild 3
                     }
                     else if (ExplosionClock.GetElapsedTime() > (visningstid * 3) && ExplosionClock.GetElapsedTime() < (visningstid * 4))
                     {
                      bildbredd = 256; //Gå till nästa bild
                      }
                     else if (ExplosionClock.GetElapsedTime() > (visningstid * 4) && ExplosionClock.GetElapsedTime() <= (visningstid * 5))
                     {
                      bildbredd = 320; //Gå till nästa bild
                     }
              
                      Klassinstans.Sprite.SetSubRect(sf::IntRect(bildbredd-64,bildhojd-64,bildbredd,bildhojd)); //Visa  bilden
                      App.Draw(Klassinstans.Sprite);//Rita ut explosionen
                      App.Display(); //visa upp ändringarna för användaren
                   

              } //slut i x-led
              ExplosionClock.Reset(); // sätter om klockan till noll
              bildhojd = bildhojd + 64;//Hoppa ner en rad
              rutorY = rutorY + 1; //Öka till nästa rad
      } //while i y-led
}//pang slutar

Explosion med ljud[redigera]

En explosion är ju inte en riktig explosion om det inte finns ett ljud i bakgrunden, men det ställer till vissa problem eftersom spelet inte riktigt vet när/hur ljudet skall spelas upp i samband med att animationen visas. Det allra enklaste är att helt enkelt starta ljudet i programkoden och sedan anropa explosionsanimationen. Det fungerar alltid men ger svårläst kod. En mer avancerad lösning är att lägga in ljudet i funktionen, men då måste funtionsdeklarationen byggas ut så att den också tar med ett ljud.

Tänk på att SFML i princip bara klarar ljud av "wav" och "ogg" format. Det bästa är om ljuden också är freeware. Roligast är så klart om du skapat dina ljud själv. Här är en hemsida på Internet med olika ljudeffekter för explosioner:

http://www.mediacollege.com/downloads/sound-effects/explosion/

Till exemplet används ljudfilen "bomb3" som verkar ha rätt ljud men litet för lång längd för vår explosionsanimation.

Lägga till ljudet i kod[redigera]

Först av allt måste vi se till så att det finns en include koppling för sfml:s ljudbibliotek i början av filen:

 #include <SFML\Audio.hpp>

Precis som vi lägger in en bildfil i en imagehållare lägger vi till en ljudfil i en ljudhållare. Det är viktigt att vi laddar in filen i samband med att man startar programmet. Då ligger filen i minnet och anropas när den behövs. Något som går mycket snabbare än om man skall ladda in filen till programmet varje gång en explosion skall ske på spelplanen. Koden för att skapa en ljudbuffer är:

sf::SoundBuffer explosionsljud; //skapa en ljudbuffer/hållare
if (!explosionsljud.LoadFromFile("bomb-03.wav")) //ladda in en fil i hållaren
{
 cout << "Kan inte hitta ljudfilen: bomb-03.wav " << endl;
 }

Sedan får vi skriva om funktionen pang till:

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::SoundBuffer explosionsfilnamn, sf::RenderWindow &App);

När vi anropar explosionen i programkoden får vi skriva:

//Visa upp explosionen om man trycker ner return-knappen
pang(100, 100, Explosionskopia, explosionsbildmap, explosionsljud, App);

Inne i funktionen får du också lov att lägga in kod för att spela upp ljudet.

//Ladda in ljudet
sf::Sound ljudeffekt; //skapa ett ljud i spelet som vi döper till ljudeffekt
ljudeffekt.SetBuffer(explosionsfilnamn); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.
ljudeffekt.Play(); //spela upp ljudet

Det är synnerligen viktigt att du börjar spela upp ljudet innan animationssekvensen börjar. Skriver du istället in koden för att spela upp ljudet inne i animationen kommer det att bara spela lika länge som tiden mellan bildväxlingaran, och börja om uppspelningen varje gång en ny ruta visas upp. Det ger ett konstigt, smattrande oljud men inte ett ljud man kan identifiera.

Fel tid[redigera]

När du nu spelar upp explosionen kommer du att se explosionen, men bara höra en kort, kort bit av ljudet. Det beror på att ljudet avbryts när animationssekvensen är klar. Av någon anledning tycker SFML att bilden är viktigare än ljudet. Det hade så klart varit bättre om ljudet fortsatta att låta efter att bildsekvenen är klar, men för att uppnå det måste du börja programmera i threads/trådar och det är litet avancerat. Det du kan göra för att få kombinationen av animation och ljud att fungera är:

  • endera förkortar du explosionsljudet
  • eller så förlänger du explosionssekvensens tid.

Om du förlänger tiden mellan bildväxlingarna i animationen från 0.01 till 0.05 kommer explosionsanimationen att ta ca. 1.25 sekunder och det blir en relativt snygg explosion. Om du vill veta hur långt du kommit i uppspelningen av en ljudfil finns kommandot:

  cout << "längd i ljudfil=" << ljudeffekt.GetPlayingOffset() << endl;

Då står det i konsollfönstret hur långt du kommit och det går att finjustera explosionerna så att animationens längd stämmer överns med ljudets längd, på ett ungefär. Med 0.05 sekunder mellan bildrutorna kommer explosionen att ske mycket långsammare, men det gör inte lika mycket om man har ett bra bakgrundsljud jämfört med om det är helt tyst.

Koden för funktionen[redigera]

Den färdiga funktionen för pang blir således:

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::SoundBuffer explosionsfilnamn, sf::RenderWindow &App)
{    
	//Ladda in ljudet
	sf::Sound ljudeffekt; //skapa ett ljud i spelet som vi döper till ljudeffekt
       ljudeffekt.SetBuffer(explosionsfilnamn); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.
       ljudeffekt.Play(); //spela upp ljudet

     int rutorY = 0; //max 5, bildrutor i y-led
    // int rutorX = 0; //max 5, bildrutor i x led
    //Obs, eftersom vi baserar animationen på tid och inte på vilken ruta man står på
    //behöver vi bara bekymra oss om vilken rad av rutor vi står på.
     
     int bildbredd = 64; //varje enskild rutas bredd
     int bildhojd = 64; //varje enskild rutas höjd
     sf::Clock ExplosionClock;
     //Skapa en timer för explosionen
     float visningstid = 0.05; //0.05 sekund mellan varje bild


     Klassinstans.Sprite.SetPosition(x,y);  
     //Placera ut animationen på rätt plats på spelplanen

      Klassinstans.Sprite.SetImage(bildfilsnamn); //ge animationen bilden av explosionen
     ExplosionClock.Reset(); //Se till så att klockan verkligen är 0

     while ( rutorY < 5) //Så länge man är på någon rad
     {//while i y-led
	
              while (ExplosionClock.GetElapsedTime() < (visningstid * 5)) //Så länge man är på någon bild i en rad          
             { //while i x-led
                 //Välj ut vilken av de 5 bilderna vi skall  ta 
                 //Genom att flytta in 64 eller 128 eller 192 osv.
                //Pixlar in på bildkartan

                  if (ExplosionClock.GetElapsedTime() >= 0.0 && ExplosionClock.GetElapsedTime() < visningstid)
                 {
                  bildbredd = 64; //Gå till första bilden
                  }
                  else if (ExplosionClock.GetElapsedTime() > visningstid && ExplosionClock.GetElapsedTime() < (visningstid * 2))
                  {
                   bildbredd = 128; //Gå till bild 2
                   }
                   else if (ExplosionClock.GetElapsedTime() > (visningstid * 2) && ExplosionClock.GetElapsedTime() < (visningstid * 3))
                   {
                    bildbredd = 192; //Gå till bild 3
                    }
                    else if (ExplosionClock.GetElapsedTime() > (visningstid * 3) && ExplosionClock.GetElapsedTime() < (visningstid * 4))
                    {
                     bildbredd = 256; //Gå till nästa bild
                     }
                    else if (ExplosionClock.GetElapsedTime() > (visningstid * 4) && ExplosionClock.GetElapsedTime() <= (visningstid * 5))
                    {
                     bildbredd = 320; //Gå till nästa bild
                    }
             
                     Klassinstans.Sprite.SetSubRect(sf::IntRect(bildbredd-64,bildhojd-64,bildbredd,bildhojd)); //Visa  bilden
                     App.Draw(Klassinstans.Sprite);//Rita ut explosionen
                     App.Display(); //visa upp ändringarna för användaren
                 //cout << "längd i ljudfil=" << ljudeffekt.GetPlayingOffset() << endl;
		  //Kolla hur långt in i ljudfilen du är

             } //slut i x-led
             ExplosionClock.Reset(); // sätter om klockan till noll
             bildhojd = bildhojd + 64;//Hoppa ner en rad
             rutorY = rutorY + 1; //Öka till nästa rad
     } //while i y-led

}//pang slutar


Komplett kod[redigera]

#include <iostream>
#include <SFML/System.hpp>
#include <SFML/Window.hpp>
#include <SFML/Graphics.hpp>
#include <SFML/Audio.hpp>


#define SFML_STATIC //Se till så att det inte behövs extra DLL-filer
using namespace std;    // utifall att konsollen behövs för felsökning

class Explosion
{
public :
  sf::Sprite Sprite; // en per instans
};

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::SoundBuffer explosionsfilnamn,sf::RenderWindow &App);

//Funktionen som initierar explosionen

int main (int argc, char *argv)
{ //main startar

        //ladda in bild för explosionen
       sf::Image explosionsbildmap;
        //explosionens spritemap är 5 x 5 rutor 64x64 pixels stora
       if (!explosionsbildmap.LoadFromFile("xplosion17.png"))
 {
 cout << "Kan inte hitta bilden: xplosion17.png " << endl;
 }

		sf::SoundBuffer explosionsljud; //skapa en ljudbuffer/hållare
if (!explosionsljud.LoadFromFile("bomb-03.wav")) //ladda in en fil i hållaren
{
cout << "Kan inte hitta ljudfilen: bomb-03.wav " << endl;
}

       //Skapa en kopia av klassen explosionen
       Explosion Explosionskopia;

      sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Test av explosion"); 
     // Skapa fönstret vi skall testa explosionen i
     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::KeyPressed) && (Event.Key.Code == sf::Key::Escape))
                   App.Close();//avsluta programmet

                   if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Return))
                   //Visa upp explosionen om man trycker ner return-knappen
                  pang(100, 100, Explosionskopia, explosionsbildmap, explosionsljud, App);

                 //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.Display(); //visa upp ändringarna för användaren
              } //while 2 slutar
        } //while 1 slutar

} //main slutar

void pang(float x, float y, Explosion &Klassinstans, sf::Image bildfilsnamn, sf::SoundBuffer explosionsfilnamn,   sf::RenderWindow &App)
{    
       //Ladda in ljudet
       sf::Sound ljudeffekt; //skapa ett ljud i spelet som vi döper till ljudeffekt
      ljudeffekt.SetBuffer(explosionsfilnamn); // Ladda in ljudfilens värden i ljudet så att det  går att spela upp.
      ljudeffekt.Play(); //spela upp ljudet

    int rutorY = 0; //max 5, bildrutor i y-led
   // int rutorX = 0; //max 5, bildrutor i x led
   //Obs, eftersom vi baserar animationen på tid och inte på vilken ruta man står på
   //behöver vi bara bekymra oss om vilken rad av rutor vi står på.
    
    int bildbredd = 64; //varje enskild rutas bredd
    int bildhojd = 64; //varje enskild rutas höjd
    sf::Clock ExplosionClock;
    //Skapa en timer för explosionen
    float visningstid = 0.1; //0.05 sekund mellan varje bild


    Klassinstans.Sprite.SetPosition(x,y);  
    //Placera ut animationen på rätt plats på spelplanen

     Klassinstans.Sprite.SetImage(bildfilsnamn); //ge animationen bilden av explosionen
    ExplosionClock.Reset(); //Se till så att klockan verkligen är 0

    while ( rutorY < 5) //Så länge man är på någon rad
    {//while i y-led
       
             while (ExplosionClock.GetElapsedTime() < (visningstid * 5)) 
            //Så länge man är på någon bild i en rad          
            { //while i x-led
                //Välj ut vilken av de 5 bilderna vi skall  ta 
                //Genom att flytta in 64 eller 128 eller 192 osv.
               //Pixlar in på bildkartan

                 if (ExplosionClock.GetElapsedTime() >= 0.0 && ExplosionClock.GetElapsedTime() < visningstid)
                {
                 bildbredd = 64; //Gå till första bilden
                 }
                 else if (ExplosionClock.GetElapsedTime() > visningstid && ExplosionClock.GetElapsedTime() < (visningstid * 2))
                 {
                  bildbredd = 128; //Gå till bild 2
                  }
                  else if (ExplosionClock.GetElapsedTime() > (visningstid * 2) && ExplosionClock.GetElapsedTime() < (visningstid * 3))
                  {
                   bildbredd = 192; //Gå till bild 3
                   }
                   else if (ExplosionClock.GetElapsedTime() > (visningstid * 3) && ExplosionClock.GetElapsedTime() < (visningstid * 4))
                   {
                    bildbredd = 256; //Gå till nästa bild
                    }
                   else if (ExplosionClock.GetElapsedTime() > (visningstid * 4) && ExplosionClock.GetElapsedTime() <= (visningstid * 5))
                   {
                    bildbredd = 320; //Gå till nästa bild
                   }
            
                    Klassinstans.Sprite.SetSubRect(sf::IntRect(bildbredd-64,bildhojd-64,bildbredd,bildhojd)); //Visa  bilden
                    //App.Clear(sf::Color(0, 100, 0)); //rensa allt i fönstret och ersätt med grönfärg
                    App.Draw(Klassinstans.Sprite);//Rita ut explosionen
                    App.Display(); //visa upp ändringarna för användaren
                //cout << "längd i ljudfil=" << ljudeffekt.GetPlayingOffset() << endl;
                 //Kolla hur långt in i ljudfilen du är

            } //slut i x-led
            ExplosionClock.Reset(); // sätter om klockan till noll
            bildhojd = bildhojd + 64;//Hoppa ner en rad
            rutorY = rutorY + 1; //Öka till nästa rad
    } //while i y-led

}//pang slutar

Är det så att animationsbilderna läggs på varandra får du lägga in raden:

App.Clear(sf::Color(0, 100, 0)); //rensa allt i fönstret och ersätt med grönfärg

före draw. Se själv i koden, jag har kommenterat ut den raden i den färdiga koden på slutet. Det är bara att ta bort // tecknen innan.