Hoppa till innehållet

Programmera spel i C++ för nybörjare/Space shooter

Från Wikibooks


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. För ljud bör du ha gjort de ändringar som behövs enligt det kapitlet. Se till så att main.cpp ligger i samma mapp som alla mediafielr (ljud och bild).

Nu är det dags att se på mer avancerade komponenter för programmering. Många gånger behöver man kunna lagra mer än bara ett heltal i en variabel. Anta att du har en scout. Du vill veta hur stark, smart och uthållig scouten är (integers) men även vad han har i ryggsäcken (strings). En sådan blandad variabel är t,ex struct.

Struct

[redigera]

En struct kan innehålla olika variabler men samlade i en struktur. Den kan man sedan kopiera och bygga ut. Miniorscouterna kan ha en liten struct och ledarna en stor. I vårt spel skall vi ha en generell struct som alla andra spelare utgår ifrån och som vi döper till Image:

struct Image
{
       sf::Sprite sprite;
       //Följande int/heltal innehåller koordinaterna för varje bild. De har värdena:
       // 'x', 'y', 'x2' och 'y2'. precis som i skolgeometrin :) 
		//x,y är övre vänstra och x2,y2 är nedre högra hörnet.

       int x;
       int y;
       int x2;
       int y2;

       int speed;//Figurens hastighet

} ;//slut på generell image struct

Vi vill veta var i världen den befinner sig (x,y,x2,y2) och hastigheten.

Från denna kan vi sedan skapa hjälten i spelet, du.

struct PlayerShip: Image
{

       PlayerShip()

       {
               sprite.SetImage(data.player1);
               sprite.SetPosition(350,475); //placeras 350 till höger och 475 ner på spelplanen
               x = 350; //Ge spriten sitt x värde.
               y = 475; //Ge spriten sitt y värde.
               speed = 6; //hastighet är relativ till frameraten

       }
}player; //struct tilldelas player

Nu kan vi anropa ”player.speed” t.ex. var vi vill i koden och vi kommer åt värdena. Dessutom har vi lagt till två andra värden så att structen byggts ut litet. Den har fått en bild som representerar den och en position på spelplanen. På det viset är en struct behändig att använda. Är det så att en struct innehåller pekare måste du helt plötsligt ange de värdena med -> symbol istället för . symbolen (se nedan vad det gäller att komma åt just kulorna man skjuter som sparas i en pekare. Det finns en annan behållare som heter class. Den enda egentliga skillnaden i dagens läge är att i en class är alla delar som standard deklarerade som privata, medan de som standard är publika i en struct. Du kommer nog ändå att använda class oftare än struct när du blivit mer van vid oop programmering.

I början av spelets kod skapar vi dessutom en stor struct som bara innehåller de olika data som spelet använder sig av:

struct Data {
       //skapa imagefil-hållarna
       sf::Image bullet1; //projektiler bild 1 i animationen - vilken storlek som helst, max storlek 32x32
       sf::Image bullet2; //projektiler bild 2 i animationen
       sf::Image Nme1; //fiende bild 1 i animationen - storlek 32x32
       sf::Image Nme2; //fiende bild 2 i animationen
       sf::Image player1;//hjälte bild 1 i animationen - storlek 32x32
       sf::Image player2;//hjälte bild 2 i animationen

       Data()

       {//ladda in filerna till imagefil-hållarna
               bullet1.LoadFromFile("bala1.png"); 
               bullet2.LoadFromFile("bala2.png");
               Nme1.LoadFromFile("malo1.png");
               Nme2.LoadFromFile("malo2.png");
               player1.LoadFromFile("nave.png");
               player2.LoadFromFile("nave2.png");
       } //slut på bild till imagehållare

}data; //"data" skall använda denna struct

Då har vi allt samlat på samma ställe och får lättare överblick. Som synes behövs sex bilder till spelet. Två olika för rymdskeppet, två olika för fiendeskeppet och två olika för avlossade granater. Två olika har vi för att göra en superenkel animation som ger ögat intrycket av att de rör sig. Skeppen är 32x32 och skotten 16 x 32. Överst i programkoden finns olika dresser där jag hämtade bilder själv, men då de inte innehåller frias bilder kan jag inte lägga upp dem åtkomliga för dig genom wikibooks.

Kulorna

[redigera]

Hade alla värden varit av samma sort kunde man lika gärna ha använt en array/vektor. Struct-en för kulorna är litet annorlunda:

struct BulletStruct: Image

{

      BulletStruct()
      {
              speed = 10;//hastigheten högre än något skepp
              sprite.SetImage(data.bullet1);                      //Tilldelas bild
              sprite.SetPosition( (player.x + 15),(player.y) );   // Placeras i mitten av spelarens skepp 
              y = player.y; //Kulorna tilldelas samma y-värde som skeppet.
      }

}*bullet[3]; //Tilldelas bullet genom pekare till kulorna
          //Man kan ha tre kulor i luften samtidigt

Som du ser är själva structen samma som de övriga, men däremot sker tilldelningen till en pekare. D.v.s. utan struct skulle man kunna ha en array men då måste alla poster i vår array vara av samma sort. En array är per definition alltid en pekare i C++. Nu gör vi så att vi tilldelar vår struct till en pekare och på det sättet har vi en array som kan innehålla ett flertal olika sorters variabler. Fiffigt va?

New - nya skott

[redigera]

De andra struct kommandona skapar en av varje enhet. Vi har ett spelarskeppp och ett fiendeskepp. Kulorna däremot mpste det finnas mängder av. Därför mpåste vi skapa dem hela tiden. Principen är enkel, i= vilken post i vår array kulan ligger på:

bullet[i] = new BulletStruct;

Då har vi skapat en ny kula. Det går att göra likadant med fiendeskepp... Man kan inte skapa saker i all oändlighet. Har man använt new, så måste man manuellt ta bort kulan efteråt annars får man minnesläckor i spelet och efter en tid kraschar det.

void Shoot()
{ //Börja skjuta.
      for (int i = 0; i < 3; i++) //kolla kulor 1, 2 och 3
      { //starta loop kolla tre kulor
              if ( (i != 0) && (!bulletExists[i]) && (bulletExists[i-1]) )
              { //starta kontroll om att det är ok att skjuta
                      if ( (window.GetInput().IsKeyDown(sf::Key::Space)) && (bullet[i-1]->y < 370) )
                      { //Start, kontroll om senaste kulan är 370 pixlar bort

                              bullet[i] = new BulletStruct;
                              bulletExists[i] = true;

                      } //Slut, kontroll om senaste kulan är 370 pixlar bort

              } //Sluta kontroll om att det är ok att skjuta

              else if ( (i == 0) && (window.GetInput().IsKeyDown(sf::Key::Space)) && (bulletExists[i] == false) )
              { //Start, kontroll om alla kulor är borta 
                      bullet[i] = new BulletStruct;
                      bulletExists[i] = true;
              } //Slut, kontroll om alla kulor är borta

      }//sluta loop kolla tre kulor
} //Slut på att skjuta

Längre ner i koden, i funktionen för att flytta kulorna, ser vi att det finns in kontroll. Om kulorna har ett y-värde under 0, vilket innebär att de försvunnit utanför överkanten, tas de bort ur spelet och förstörs.

       if (bullet[i]->y < 0) 
         { //Om kulan försvunnit utanför överkanten
          delete bullet[i]; 
          bulletExists[i] = false;
          }

Orsaken till att -> symbolen används är för att vi vill komma åt ett värde inuti en pekare, du kommer väl ihåg att kulorna skapades som pekare? Om de inte varit pekare utan vanliga variabler i en strrukt hade man istället skrivit: bullet[i].y.

Enkel animation

[redigera]

I koden finns en enklaste formen av animation man kan tänka sig. Den bygger på två olika bilder av samma storlek och en klocka. Så här ser den ut för spelaren (sprite=player).

       if ((Clock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.1)) player.sprite.SetImage(data.player1);
       if ((Clock.GetElapsedTime() > 0.1) && (Clock.GetElapsedTime() < 0.2)) player.sprite.SetImage(data.player2);
       if (Clock.GetElapsedTime() > 0.2) Clock.Reset();

Bilderna laddas in 1/10 sekund så att man får en blinkande effekt. Spelet ändras 60 ggr i sekunden och bilderna 10 ggr = varje bild visas 300 ggr i sekunden. Ställer man om klockan ställer man också om hastigheten som animationen visas upp på.

Loop

[redigera]

Eftersom klockan sätts på "reset" på sista raden kommer animationen att loopa, dvs börja om hela tiden. Vill man ha en funktion som bara utlöser en sekvens, en explosion t.ex., skall du sätta klockan på reset innan animationen börjar.

Spritesheet

[redigera]

Om du vill använda ett spritesheet, dvs en stor bild som består av en mängd små bilder, får du kopiera ut rätt bild ur spritesheeten vid varje givet ögonblick. Då får man ladda in rätt spritesheet till structen i början av koden, sedan är koden istället:

 if ((Clock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.1)) //animation bild 1
 <structname>.sprite.SetSubRect(sf::IntRect(0,0,64,64)); //Visa första bilden

Static

[redigera]

Allra högst upp i koden finns statiska värden. Om man har ett statiskt värde i programmet som aldrig ändras kan man lägga in det på det viset. Statiska värden är vanligtvis skrivna med versaler:

#define RIGHT_BORDER 725 //höger kant

Felsökning

[redigera]

Nu har vi kommit så långt i våra projekt att det där svarta konsollfönstret kan börja komma till användning. I enklare program är det mest till besvär, men om man vill se vad olika variabler är inuti programmet är det tacksamt att slippa skapa textstreams bara för att få ut siffror på skärmen. Då är det mycket enklare att bara skriva

cout << värde << endl;

Kod för ett första fungerande ramverk

[redigera]
//Spaceshooter
//Koden i detta spel bygger på annan kod hittad på internet
//på http://pastebin.com/kAS9E164
// Varningar: warning C4244: 'argument' : conversion from 'int' to 'float', possible loss of data
//beror på att spelarna placeras ut på exakta pixlar, det påverkar inte spelet.
//Rymdskeppsbilder från http://www.fromsmallpixels.co.uk/pixelart.html
//Kulbilder från http://mab.eviscerate.net/bullets.png

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

//using namespace std;    // utifall att konsollen behövs

//Lägg till statiska värden som aldrig ändras***************************************
#define RIGHT_BORDER 725 //höger kant
#define LEFT_BORDER 25 //vänster kant
#define TOP_BORDER 0 //överkant
#define BOTTOM_BORDER 600 //nederkant
#define SPAWN_POS1 350 //Där fientliga rymdskepp spawnas i x-led

//Globala variabler*******************************************************************
sf::RenderWindow window(sf::VideoMode(800,600,32), "Enkel Space Shooter"); //Fönstret spelet visas i
sf::Event event; //ev. händelser i spelet
sf::Clock Clock; //en spelklocka för att hålla reda på animationer o. dyl.
sf::Clock bulletClock[3]; //Vi skjuter tre skott varje gång space-tangenetn trycks ner
                         //Varje skott måste ha en egen klocka
bool bulletExists[3] = {false,false,false}; //I början finns inte skotten

//Funktioner som används i spelet deklareras i förväg*********************************
void EndProgram(); //Hur spelet avslutas
void Move(); //Hur figurerna gör när de skjuter
void Shoot(); //Hur figurerna gör när de förflyttar sig
void MoveBullet(int i); //Hur kulorna rör sig
void MoveNme(); //Hur fiendeskeppet förflyttar sig

//Structs som spelet använder sig av för att skapa spelarna***************************
//Hade kunnat varit class, men allt är public i dem och struct bearbetas snabbare*****

//Första structen innehåller alla bilderna********************************************
//Bilderna ändras från 1 till 2 med 1/10 sekunds skillnad för att simulera
//en animation av rymdskeppen och kulorna/projektilerna.
struct Data {
       //skapa imagefil-hållarna
       sf::Image bullet1; //projektiler bild 1 i animationen - vilken storlek som helst, max storlek 32x32
       sf::Image bullet2; //projektiler bild 2 i animationen
       sf::Image Nme1; //fiende bild 1 i animationen - storlek 32x32
       sf::Image Nme2; //fiende bild 2 i animationen
       sf::Image player1;//hjälte bild 1 i animationen - storlek 32x32
       sf::Image player2;//hjälte bild 2 i animationen

       Data()

        {//ladda in filerna till imagefil-hållarna
               bullet1.LoadFromFile("bala1.png");
               bullet2.LoadFromFile("bala2.png");
               Nme1.LoadFromFile("malo1.png");
               Nme2.LoadFromFile("malo2.png");
               player1.LoadFromFile("nave.png");
               player2.LoadFromFile("nave2.png");
       } //slut på bild till imagehållare

}data; //"data" skall använda denna struct

//Andra structen, generell struct för alla bildobjekten*****************************
struct Image
{ 
       sf::Sprite sprite;
       //Följande int/heltal innehåller koordinaterna för varje bild. De har värdena:
       // 'x', 'y', 'x2' och 'y2'. precis som i skolgeometrin :) 
		//x,y är övre vänstra och x2,y2 är nedre högra hörnet.

       int x;
       int y;
       int x2;
       int y2;

       int speed;//Figurens hastighet

};//slut på generell image struct

// I de tre sista structs har varje objekt ärvt från image struct. 
//De får: en sprite, en position och en hastighet.

//Spelarens rymdskepp*****************************************************************

struct PlayerShip: Image
{

       PlayerShip()

       {
               sprite.SetImage(data.player1);
               sprite.SetPosition(350,475); //placeras 350 till höger och 475 ner på spelplanen
               x = 350; //Ge spriten sitt x värde.
               y = 475; //Ge spriten sitt y värde.
               speed = 6; //hastighet är relativ till frameraten

       }
}player; //struct tilldelas player


//Fiendens rymdskepp*******************************************************************
struct Nme: Image

{
       Nme()
       {
               sprite.SetImage(data.Nme1); //Fienden Nme får datan från structen data
               sprite.SetPosition(SPAWN_POS1,0);//=350,0 eftersom 
                                                //SPAWN_POS1 är en fördefinierad konstant
               x = 350; //placeras ungefär mitt på spelplanen
               y = 0; //placeras vid överkanten
               speed = 2; //fart 1/3 av spelarens
       }

}enemy; //structen tilldelas enemy/fienden

//De kulor som skeppen skjuter************************************************************
struct BulletStruct: Image

{

       BulletStruct()
       {
               speed = 10;//hastigheten högre än något skepp
               sprite.SetImage(data.bullet1);                      //Tilldelas bild
               sprite.SetPosition( (player.x + 15),(player.y) );   // Placeras i mitten av spelarens skepp 
               y = player.y; //Kulorna tilldelas samma y-värde som skeppet.
       }

}*bullet[3]; //Tilldelas bullet genom pekare till kulorna
           //Man kan ha tre kulor i luften samtidigt




//spelet startar*********************************************************************
int main (int argc, char *argv)
  { //Main startar
       window.SetFramerateLimit(60); //Antal bilder per sekund
       Clock.Reset(); //Sätt klockan på 0
		
     while (window.IsOpened())
      {//Fönstret visas
       //Kör de olika funktionerna
               EndProgram(); //Skall spelet avslutas?
               window.Clear(); //Rensa skärmen
               window.Draw(player.sprite); //Skriv ut sprites
               Shoot(); //Skjut kulorna
               Move(); //Flytta spelaren
               MoveNme(); //Flytta fienden

				//Hantera kulorna
        for (int i = 0; i < 3; i++) //Räkna igenom alla tre kulorna
         { //Start kolla kulor
              if (bulletExists[i]) MoveBullet(i); //Flytta den om den finns
              if (bulletExists[i]) window.Draw(bullet[i]->sprite); //Rita upp den om den finns
              if (!bulletExists[i]) bulletClock[i].Reset(); //Återställ klockan för kulorna
         } //slut kolla kulor
		 window.Display(); //Visa upp spelfönstret
       } //Avslut om fönstret visas

       //return EXIT_SUCCESS;

       return 0;
  }//Main slutar

/*------------------------------------------------------------------------------------
Här nedanför följer de olika funktionerna som används i spelet.
Att hålla funktionerna utanför programmet gör koden
lättare att hålla ren och programmet blir också lättare att felsöka.
------------------------------------------------------------------------------------*/

// Funktion för att se om programmet skall stängas************************************
void EndProgram()
{ //start stängningskontroll
       while(window.GetEvent(event))
       {
             if (event.Type == sf::Event::Closed)
             window.Close();
       }
} //slut stängningskontroll

//Flytta spelaren*************************************************************************
void Move()
{//Börja flytta spelaren
//Spelaren flyttar med höger- och vänster piltangent
//Skeppet stoppar när det når de fördefinierade värdena för kanten

       if ( window.GetInput().IsKeyDown(sf::Key::Left) && (player.x > LEFT_BORDER) ) {player.sprite.Move(-player.speed,0); player.x -= player.speed;}
       if ( window.GetInput().IsKeyDown(sf::Key::Right) && (player.x < RIGHT_BORDER) ) {player.sprite.Move(player.speed,0); player.x += player.speed;}


//Följande kod ändrar spritens bild, den ändras 10 ggr per sekund.

       if ((Clock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.1)) player.sprite.SetImage(data.player1);
       if ((Clock.GetElapsedTime() > 0.1) && (Clock.GetElapsedTime() < 0.2)) player.sprite.SetImage(data.player2);
       if (Clock.GetElapsedTime() > 0.2) Clock.Reset();
}//Slut på att flytta spelaren

/*---------------------------------------------------
Spelaren skjuter genom att trycka ner Space-tangenten.
Skjut-funktionen måste gå igenom tre stadier:
1: Vilken kula (av tre) skjuter vi?
2: Finns redan den kulan?
3: har den senaste kulan rört sig tillräckligt långt bort
(Y-värdet är bortom 370) för att tillåta nästa kula att skjutas.
--------------------------------------------------------*/

//Skjuta kulor**************************************************************************
void Shoot()
{ //Börja skjuta.

       for (int i = 0; i < 3; i++) //kolla kulor 1, 2 och 3
       { //starta loop kolla tre kulor


               if ( (i != 0) && (!bulletExists[i]) && (bulletExists[i-1]) )
               { //starta kontroll om att det är ok att skjuta

                       if ( (window.GetInput().IsKeyDown(sf::Key::Space)) && (bullet[i-1]->y < 370) )
                       { //Start, kontroll om senaste kulan är 370 pixlar bort

                               bullet[i] = new BulletStruct;
                               bulletExists[i] = true;

                       } //Slut, kontroll om senaste kulan är 370 pixlar bort

               } //Sluta kontroll om att det är ok att skjuta

               else if ( (i == 0) && (window.GetInput().IsKeyDown(sf::Key::Space)) && (bulletExists[i] == false) )

               { //Start, kontroll om alla kulor är borta

                       bullet[i] = new BulletStruct;
                       bulletExists[i] = true;

               } //Slut, kontroll om alla kulor är borta


       }//sluta loop kolla tre kulor


} //Slut på att skjuta

//Flytta kulorna****************************************************************************
void MoveBullet(int i)

{ //Start flytta kulor

        bullet[i]->sprite.Move(0,-bullet[i]->speed); //Flytta kulan
        bullet[i]->y -= bullet[i]->speed;


       if ((bulletClock[i].GetElapsedTime() > 0.0) && (bulletClock[i].GetElapsedTime() < 0.1)) //Visa bild 1
           bullet[i]->sprite.SetImage(data.bullet1);


       if ((bulletClock[i].GetElapsedTime() > 0.1) && (bulletClock[i].GetElapsedTime() < 0.2)) //Visa bild 2
            bullet[i]->sprite.SetImage(data.bullet2);

       if (bulletClock[i].GetElapsedTime() > 0.2) bulletClock[i].Reset(); //Återställ klockan

       window.Draw(bullet[i]->sprite); //Rita ut kulan


       if (bullet[i]->y < 0) 
          { //Om kulan försvunnit utanför överkanten
           delete bullet[i]; 
           bulletExists[i] = false;
           }


} //Slut flytta kulor

//Flytta fiendeskeppet*********************************************************************
void MoveNme()

{ //Start flytta fiendeskepp
       enemy.sprite.Move(0,enemy.speed); //flytta fienden
       enemy.y += enemy.speed; //avgör nytt y

       if (enemy.y > BOTTOM_BORDER) //Om det flugit utanför nederkanten
          {
           enemy.sprite.SetPosition(SPAWN_POS1,TOP_BORDER); //sätt det överst igen
           enemy.y = 0;
          }

       if ((Clock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.1)) //animation bild 1
            enemy.sprite.SetImage(data.Nme1);

       if ((Clock.GetElapsedTime() > 0.1) && (Clock.GetElapsedTime() < 0.2)) //animation bild 2
            enemy.sprite.SetImage(data.Nme2);

       window.Draw(enemy.sprite);//Rita ut fienden


} //Slut flytta fiendeskepp

/*--------------------------------------------------
PROGRAMSLUT
---------------------------------------------------*/

Nu har vi en svart yta med ett rymdskepp på och ett annat vi kan skjuta på, men ingenting händer vid en träff, det får vi ändra på. Vi måste ta reda på om vi träffar fienden eller inte, och tyvärr ingår en litet otäck bugg - x värdet för kulorna vi skjuter finns i vår struct, men definieras inte. Om inte programmet vet vad x är hittar det på något och kulorna är dömda att missa för evigt. Vi får lägga till:

x = player.x + 15; //halva spelaren in

i koden för att kunna se om vi träffar eller missar i fiendeskeppets struct.

Kollision

[redigera]

Vi måste ha en ny funktion

headoncollisioncheck(i);//Kollisionskontroll för att se om en kula träffat

Funktionen kollar inte om man flyger in i en´granat från sidan.

void headoncollisioncheck(int i)
//Förutsätter att man vet storleken på föremålen i förväg
//Kulorna är 16 pixel breda
//Fiendeskeppen är 32x32 pixel stora
{ //starta collison detect

	if ((bullet[i]->x > (enemy.x-16)) && (bullet[i]->x < enemy.x +32) && bullet[i]->y < enemy.y  )
		//Om x värdet för kulan ryms inom överkanten för fienden
		//och kulan egentligen passerat skeppet (men det går så fort
		//att det inte syns) händer följande:
	{//träff
		            delete bullet[i]; //radera kulan
                           bulletExists[i] = false; //ta bort kulan ur listan
		            enemy.sprite.SetPosition(SPAWN_POS1,TOP_BORDER); //sätt fiendeskeppet överst igen
                   enemy.y = 0;
	} //slut vid träff


}//slut på collision

Explosion

[redigera]

Sist måste vi skapa en enkel explosion. Vi styr även den med en klocka, men har en egen klocka till exoplosionen. Skapa en explosionsklocka med koden:

sf::Clock ExplosionClock; //en explosionsklocka för att hålla reda på explosionsanimationer.

Nollställ den i början av spelloopen:

ExplosionClock.Reset(); //Ställ explosionsklockan på 0

I struct struct måste du:

sf::Image explosion;//explosionsanimationen

Sedan skall filen läsas in

explosion.LoadFromFile("xplosion17.png"); //en hel spritekarta för en explosionsanimation

Skapa ännu en struct

struct expl: Image
{

        expl()

       {
               sprite.SetImage(data.explosion);
               sprite.SetPosition(0,0); //placeras 350 till höger och 475 ner på spelplanen
               x = 0; //Ge spriten sitt x värde.
               y = 0; //Ge spriten sitt y värde.

       }
}explosion; //struct tilldelas explosioner

//Rita upp explosionerna
void SkapaExplosion(int x, int y)
{//Börja visa explosionen
	explosion.sprite.SetPosition(x,y); //Placera ut där explosionen skall ske
	explosion.x = x;
	explosion.y = y;

	//Rad 1. X förskjuts 64 för varje bild. Y är hela tiden 0 i första och 64 i andra koordinaten
if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.2)) //animation bild 1
             explosion.sprite.SetSubRect(sf::IntRect(0,0,64,64)); //Visa första bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.4)) //animation bild 2
            explosion.sprite.SetSubRect(sf::IntRect(64,0,128,64)); //Visa andra bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.6)) //animation bild 3
            explosion.sprite.SetSubRect(sf::IntRect(128,0,192,64)); //Visa tredje bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.8)) //animation bild 4
            explosion.sprite.SetSubRect(sf::IntRect(192,0,256,64)); //Visa fjärde bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 10.0)) //animation bild 5
            explosion.sprite.SetSubRect(sf::IntRect(256,0,320,64)); //Visa femte bilden

//4 rader till
window.Draw(explosion.sprite);//Rita ut explosionen
}//Sluta visa explosionen

Lägg slutligen till i koden var explosionen skall uppstå genom att rita upp explosionen vid träff, lägg till:

SkapaExplosion(enemy.x,enemy.y); //Rita upp en explosion där fienden 

inuti den funktion som avgör om man fått en träff där x,y är fiendeskeppets x,y värden.

Den slutgiltiga koden:

[redigera]
--------------------
komplett kod
------------
//Spaceshooter
//Koden i detta spel bygger på annan kod hittad på internet
//på http://pastebin.com/kAS9E164
// Varningar: warning C4244: 'argument' : conversion from 'int' to 'float', possible loss of data
//beror på att spelarna placeras ut på exakta pixlar, det påverkar inte spelet.
//Rymdskeppsbilder från http://www.fromsmallpixels.co.uk/pixelart.html
//Kulbilder från http://mab.eviscerate.net/bullets.png
//Explosion från http://cdn.pimpmyspace.org/media/pms/c/b9/9e/ez/xplosion17.png
//varje del är 64x 64, 5x5 bilder.

#include <iostream>
#include <SFML\System.hpp>
#include <SFML\Window.hpp>
#include <SFML\Graphics.hpp>
//#include <SFML\Audio.hpp> //OBS se till så att du verkligen kan spela upp ljud

//using namespace std;    // utifall att konsollen behövs
                        //synnerligen användbart om man skall felsöka

//Lägg till statiska värden som aldrig ändras***************************************
#define RIGHT_BORDER 725 //höger kant
#define LEFT_BORDER 25 //vänster kant
#define TOP_BORDER 0 //överkant
#define BOTTOM_BORDER 600 //nederkant
#define SPAWN_POS1 350 //Där fientliga rymdskepp spawnas i x-led

//Globala variabler*******************************************************************
sf::RenderWindow window(sf::VideoMode(800,600,32), "Enkel Space Shooter"); //Fönstret spelet visas i
sf::Event event; //ev. händelser i spelet
sf::Clock Clock; //en spelklocka för att hålla reda på animationer o. dyl.
sf::Clock ExplosionClock; //en explosionsklocka för att hålla reda på explosionsanimationer.
sf::Clock bulletClock[3]; //Vi skjuter tre skott varje gång space-tangenetn trycks ner
                         //Varje skott måste ha en egen klocka
bool bulletExists[3] = {false,false,false}; //I början finns inte skotten

//Funktioner som används i spelet deklareras i förväg*********************************
void EndProgram(); //Hur spelet avslutas
void Move(); //Hur figurerna gör när de skjuter
void Shoot(); //Hur figurerna gör när de förflyttar sig
void MoveBullet(int i); //Hur kulorna rör sig
void MoveNme(); //Hur fiendeskeppet förflyttar sig
void headoncollisioncheck(int i); //Har en kula träffat fienden?

//Structs som spelet använder sig av för att skapa spelarna***************************
//Hade kunnat varit class, men allt är public i dem och struct bearbetas snabbare*****

//Första structen innehåller alla bilderna********************************************
//Bilderna ändras från 1 till 2 med 1/10 sekunds skillnad för att simulera
//en animation av rymdskeppen och kulorna/projektilerna.
struct Data {
       //skapa imagefil-hållarna
       sf::Image bullet1; //projektiler bild 1 i animationen - vilken storlek som helst, max storlek 32x32
       sf::Image bullet2; //projektiler bild 2 i animationen
       sf::Image Nme1; //fiende bild 1 i animationen - storlek 32x32
       sf::Image Nme2; //fiende bild 2 i animationen
       sf::Image player1;//hjälte bild 1 i animationen - storlek 32x32
       sf::Image player2;//hjälte bild 2 i animationen
		sf::Image explosion;//explosionsanimationen

       Data()

       {//ladda in filerna till imagefil-hållarna
        bullet1.LoadFromFile("bala1.png"); 
        bullet2.LoadFromFile("bala2.png");
        Nme1.LoadFromFile("malo1.png");
        Nme2.LoadFromFile("malo2.png");
        player1.LoadFromFile("nave.png");
        player2.LoadFromFile("nave2.png");
	 explosion.LoadFromFile("xplosion17.png"); //en hel spritekarta för en explosionsanimation
       } //slut på bild till imagehållare

}data; //"data" skall använda denna struct

//Andra structen, generell struct för alla bildobjekten*****************************
struct Image
{
       sf::Sprite sprite;
       //Följande int/heltal innehåller koordinaterna för varje bild. De har värdena:
       // 'x', 'y', 'x2' och 'y2'. precis som i skolgeometrin :) 
	//x,y är övre vänstra och x2,y2 är nedre högra hörnet.

       int x;
       int y;
       int x2;
       int y2;

       int speed;//Figurens hastighet

};//slut på generell image struct

// I de tre sista structs har varje objekt ärvt från image struct. 
//De får: en sprite, en position och en hastighet.

//Spelarens rymdskepp*****************************************************************

struct PlayerShip: Image
{

       PlayerShip()

       {
        sprite.SetImage(data.player1);
        sprite.SetPosition(350,475); //placeras 350 till höger och 475 ner på spelplanen
        x = 350; //Ge spriten sitt x värde.
        y = 475; //Ge spriten sitt y värde.
        speed = 6; //hastighet är relativ till frameraten

       }
}player; //struct tilldelas player


//Fiendens rymdskepp*******************************************************************
struct Nme: Image

{
       Nme()
       {
        sprite.SetImage(data.Nme1); //Fienden Nme får datan från structen data
        sprite.SetPosition(SPAWN_POS1,0);//=350,0 eftersom 
               //SPAWN_POS1 är en fördefinierad konstant
        x = 350; //placeras ungefär mitt på spelplanen
        y = 0; //placeras vid överkanten
        speed = 2; //fart 1/3 av spelarens
       }

}enemy; //structen tilldelas enemy/fienden

//De kulor som skeppen skjuter************************************************************
struct BulletStruct: Image

{

       BulletStruct()
       {
               speed = 10;//hastigheten högre än något skepp
               sprite.SetImage(data.bullet1);  //Tilldelas bild
		    //tydliga x och y värden måste finnas för att man skall avgöra om det blivit en träff
	        y = player.y; //Kulorna tilldelas samma y-värde som skeppet.
		x = player.x + 15; //halva spelaren in
               sprite.SetPosition((player.x + 15),(player.y));   // Placeras i mitten av spelarens skepp 

       }

}*bullet[3]; //Tilldelas bullet genom pekare till kulorna
           //Man kan ha tre kulor i luften samtidigt


//Den explosion som uppstår när en kula träffar ett fiendeskepp***********************

struct expl: Image
{
       expl()
       {
        sprite.SetImage(data.explosion);
        sprite.SetPosition(0,0); //
	 sprite.SetSubRect(sf::IntRect(0,0,32,32));

        x = 100; //Ge spriten sitt x värde.
        y = 100; //Ge spriten sitt y värde.
       }
}explosion; //struct tilldelas explosioner


//spelet startar*********************************************************************
int main (int argc, char *argv)
  { //Main startar
       window.SetFramerateLimit(60); //Antal bilder per sekund
       Clock.Reset(); //Sätt klockan på 0
	ExplosionClock.Reset(); //Ställ explosionsklockan på 0
		
     while (window.IsOpened())
      {//Fönstret visas
       //Kör de olika funktionerna
               EndProgram(); //Skall spelet avslutas?
               window.Clear(); //Rensa skärmen
               window.Draw(player.sprite); //Skriv ut sprites
               Shoot(); //Skjut kulorna
               Move(); //Flytta spelaren
               MoveNme(); //Flytta fienden

				//Hantera kulorna
        for (int i = 0; i < 3; i++) //Räkna igenom alla tre kulorna
         { //Start kolla kulor
              if (bulletExists[i]) MoveBullet(i); //Flytta den om den finns
	       if (bulletExists[i]) headoncollisioncheck(i);//Kollisionskontroll för att se om en kula träffat
              if (bulletExists[i]) window.Draw(bullet[i]->sprite); //Rita upp den om den finns
              if (!bulletExists[i]) bulletClock[i].Reset(); //Återställ klockan för kulorna
         } //slut kolla kulor

		 

		 window.Display(); //Visa upp spelfönstret
       } //Avslut om fönstret visas

       return 0;
  }//Main slutar

/*------------------------------------------------------------------------------------
Här nedanför följer de olika funktionerna som används i spelet.
Att hålla funktionerna utanför programmet gör koden
lättare att hålla ren och programmet blir också lättare att felsöka.
------------------------------------------------------------------------------------*/

// Funktion för att se om programmet skall stängas************************************
void EndProgram()
{ //start stängningskontroll
       while(window.GetEvent(event))
       {
             if (event.Type == sf::Event::Closed)
             window.Close();
       }
} //slut stängningskontroll

//Flytta spelaren*************************************************************************
void Move()
{//Börja flytta spelaren
//Spelaren flyttar med höger- och vänster piltangent
//Skeppet stoppar när det når de fördefinierade värdena för kanten

       if ( window.GetInput().IsKeyDown(sf::Key::Left) && (player.x > LEFT_BORDER) ) {player.sprite.Move(-player.speed,0); player.x -= player.speed;}
       if ( window.GetInput().IsKeyDown(sf::Key::Right) && (player.x < RIGHT_BORDER) ) {player.sprite.Move(player.speed,0); player.x += player.speed;}


//Följande kod ändrar spritens bild, den ändras 10 ggr per sekund.

       if ((Clock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.1)) player.sprite.SetImage(data.player1);
       if ((Clock.GetElapsedTime() > 0.1) && (Clock.GetElapsedTime() < 0.2)) player.sprite.SetImage(data.player2);
       if (Clock.GetElapsedTime() > 0.2) Clock.Reset();
}//Slut på att flytta spelaren

/*---------------------------------------------------
Spelaren skjuter genom att trycka ner Space-tangenten.
Skjut-funktionen måste gå igenom tre stadier:
1: Vilken kula (av tre) skjuter vi?
2: Finns redan den kulan?
3: har den senaste kulan rört sig tillräckligt långt bort
(Y-värdet är bortom 370) för att tillåta nästa kula att skjutas.
--------------------------------------------------------*/

//Skjuta kulor**************************************************************************
void Shoot()
{ //Börja skjuta.

       for (int i = 0; i < 3; i++) //kolla kulor 1, 2 och 3
       { //starta loop kolla tre kulor

               if ( (i != 0) && (!bulletExists[i]) && (bulletExists[i-1]) )
               { //starta kontroll om att det är ok att skjuta

                       if ( (window.GetInput().IsKeyDown(sf::Key::Space)) && (bullet[i-1]->y < 370) )
                       { //Start, kontroll om senaste kulan är 370 pixlar bort

                        bullet[i] = new BulletStruct;
                        bulletExists[i] = true;

                       } //Slut, kontroll om senaste kulan är 370 pixlar bort

               } //Sluta kontroll om att det är ok att skjuta

               else if ( (i == 0) && (window.GetInput().IsKeyDown(sf::Key::Space)) && (bulletExists[i] == false) )
               { //Start, kontroll om alla kulor är borta

                       bullet[i] = new BulletStruct;
                       bulletExists[i] = true;

               } //Slut, kontroll om alla kulor är borta


       }//sluta loop kolla tre kulor


} //Slut på att skjuta

//Flytta kulorna****************************************************************************
void MoveBullet(int i)

{ //Start flytta kulor

       bullet[i]->sprite.Move(0,-bullet[i]->speed); //Flytta kulan
       bullet[i]->y -= bullet[i]->speed;


       if ((bulletClock[i].GetElapsedTime() > 0.0) && (bulletClock[i].GetElapsedTime() < 0.1)) //Visa bild 1
           bullet[i]->sprite.SetImage(data.bullet1);


       if ((bulletClock[i].GetElapsedTime() > 0.1) && (bulletClock[i].GetElapsedTime() < 0.2)) //Visa bild 2
            bullet[i]->sprite.SetImage(data.bullet2);

       if (bulletClock[i].GetElapsedTime() > 0.2) bulletClock[i].Reset(); //Återställ klockan

       window.Draw(bullet[i]->sprite); //Rita ut kulan


       if (bullet[i]->y < 0) 
          { //Om kulan försvunnit utanför överkanten
           delete bullet[i]; //radera kulan i ena listan
           bulletExists[i] = false; // markera den som förbrukad i andra
           }


} //Slut flytta kulor

//Flytta fiendeskeppet*********************************************************************
void MoveNme()

{ //Start flytta fiendeskepp
       enemy.sprite.Move(0,enemy.speed); //flytta fienden
       enemy.y += enemy.speed; //avgör nytt y

       if (enemy.y > BOTTOM_BORDER) //Om det flugit utanför nederkanten
          {
           enemy.sprite.SetPosition(SPAWN_POS1,TOP_BORDER); //sätt det överst igen
           enemy.y = 0;
          }

       if ((Clock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.1)) //animation bild 1
            enemy.sprite.SetImage(data.Nme1);

       if ((Clock.GetElapsedTime() > 0.1) && (Clock.GetElapsedTime() < 0.2)) //animation bild 2
            enemy.sprite.SetImage(data.Nme2);

       window.Draw(enemy.sprite);//Rita ut fienden


} //Slut flytta fiendeskepp


void SkapaExplosion(int x, int y)
{//Börja visa explosionen
	explosion.sprite.SetPosition(x,y); //Placera ut där explosionen skall ske
	explosion.x = x;
	explosion.y = y;

	//Rad 1. X förskjuts 64 för varje bild. Y är hela tiden 0 i första och 64 i andra koordinaten
if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.2)) //animation bild 1
            explosion.sprite.SetSubRect(sf::IntRect(0,0,64,64)); //Visa första bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.4)) //animation bild 2
            explosion.sprite.SetSubRect(sf::IntRect(64,0,128,64)); //Visa andra bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.6)) //animation bild 3
            explosion.sprite.SetSubRect(sf::IntRect(128,0,192,64)); //Visa tredje bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 0.8)) //animation bild 4
            explosion.sprite.SetSubRect(sf::IntRect(192,0,256,64)); //Visa fjärde bilden

if ((ExplosionClock.GetElapsedTime() > 0.0) && (Clock.GetElapsedTime() < 10.0)) //animation bild 5
            explosion.sprite.SetSubRect(sf::IntRect(256,0,320,64)); //Visa femte bilden

//4 rader till


window.Draw(explosion.sprite);//Rita ut explosionen
}//Sluta visa explosionen

//Kontrollera om ditt skott träffar fienden**********************************************

void headoncollisioncheck(int i)
//Förutsätter att man vet storleken på föremålen i förväg
//Kulorna är 16 pixel breda
//Fiendeskeppen är 32x32 pixel stora
{ //starta collison detect

	if ((bullet[i]->x > (enemy.x-16)) && (bullet[i]->x < enemy.x +32) && bullet[i]->y < enemy.y  )
		//Om x värdet för kulan ryms inom överkanten för fienden
		//och kulan egentligen passerat skeppet (men det går så fort
		//att det inte syns) händer följande:
	{//träff
		    delete bullet[i]; //radera kulan
                   bulletExists[i] = false; //ta bort kulan ur listan
		    enemy.sprite.SetPosition(SPAWN_POS1,TOP_BORDER); //sätt fiendeskeppet överst igen
                   enemy.y = 0;

		    SkapaExplosion(enemy.x,enemy.y); //Rita upp en explosion där fienden fanns

	} //slut vid träff

}//slut på collision

/*--------------------------------------------------
PROGRAMSLUT
---------------------------------------------------*/

Bygg vidare

  • Lägg in en snygg bakgrund.
  • Se till så att ljudet fungerar.
  • Gör så att fiendeskeppet startar på olika ställen med hjälp av slumptalsgenerator.
  • Gör så att fiendeskeppet också kan skjuta.
  • Ge fiendeskeppet andra granater.
  • Gör så att flera skepp kan tas fram samtidigt på skärmen.