Hoppa till innehållet

Programmera spel i C++ för nybörjare/Flaggklickare

Från Wikibooks


Peka klicka spel

[redigera]

Om man förstår hur det fungerar med kollisionsberäkningar är det inte särskilt svårt att göra enkla spel där man skall klicka på skärmen för att få upp ett resultat.

Grundprincipen är enkel:

  • En text kommer upp med en fråga.
  • Klicka på en bild för att besvara frågan
  • Var det rätt svar får du en positiv bekräftelse
  • Var det fel svar får du en negativ bekräftelse och ev. rätt svar.
  • En ny fråga kommer upp.

Flaggspel

[redigera]

Ett enkelt exempel är om man skall lära sig Nordens flaggor. Vi behöver en bild på flaggorna, och den hittar vi här: http://www.biblioteken.fi/File/d8811b0a-b714-4911-a69f-d634b8b952a8/width/397/height/119/nordiskt_flaggor.jpg

Därefter behöver vi lika många sprites som det finns flaggor, dvs åtta sprites. Varje sprite ingår i en klass. För att göra det enkelt för oss skapar vi en array med sprites som vi kan räkna igenom.

Vi behöver också en annan array med namnen på länderna.

Danmark, Färöarna, Island, Finland, Norge, Sverige, Sameland, Grönland.

Vi behöver ett slumptal från 0 till 7 för att veta vilket land vi skall slumpa fram.

Flagg-klassen

[redigera]

Flaggorna måste ingå i endera en klass eller en struct. Jag valde en class.

class flagga 
{
public:
//Konstruktordeklaration, definition utanför klassdeklarationen
flagga (int  arraynummer, float hastighet, float flagga_x, float flagga_y);//startvärden
//Destruktion
~flagga(){};

int arraynummer; //Vilket land motsvarar den
float  hastighet; //Hur snabb är den
float flagga_x; // var är den i sidled i programmet
float flagga_y; //var är den i höjdled i programmet
sf::Sprite flaggsprite;
};

//Konstruktion av flaggor
flagga::flagga (int  ut_arraynummer, float ut_hastighet, float ut_flagga_x, float ut_flagga_y)
{
        arraynummer=ut_arraynummer;
        hastighet=ut_hastighet;
        flagga_x=ut_flagga_x;
        flagga_y=ut_flagga_y;
}

Image

[redigera]

Vi måste skapa en Image för att hålla i våra flaggbilder:

      sf::Image flaggbild; //skapa en tom bildhållare som heter flaggbild
      if (!flaggbild.LoadFromFile("nordiskt_flaggor.jpg")) return EXIT_FAILURE; 
      //fyll den tomma bildhållaren med bilden nordiskt_flaggor.jpg

Varför jpg och inte png? Man använder png när man vill ha en färg genomskinlig. Flaggor har inte genomskinliga färger så vi kan behålla den i jpg-format. Du får gärna ändra den till png om du vill.

Vector med flaggor

[redigera]

Vi skapar en vector där vi laddar in de olika klassinstanserna av flaggorna:

std::vector<flagga> Flagglista;

Det sista vi gör innan programmet stängs är att återställa minnet och radera innehållet i vektorn:

Flagglista.clear(); //Radera listan för att undvika minnesläckor

Sedan fyller vi den med de olika flaggorna ovanför varandra:

//Lägg in flaggorna:
Flagglista.push_back( flagga (0, 10.0, 400.0f, 100.0f) ); //Lägger en flagga i vector(0=Danmark)
                            //0=Danmark, 10=hastighet, 400= xposition 100 = yposition
 Flagglista.push_back( flagga (1, 10.0, 400.0f, 150.0f) ); //Lägger en flagga i vector(1=Färöarna)
  Flagglista.push_back( flagga (2, 10.0, 400.0f, 200.0f) ); //Lägger en flagga i vector(2=Island)
   Flagglista.push_back( flagga (3, 10.0, 400.0f, 250.0f) ); //Lägger en flagga i vector(3=Finland)
	 Flagglista.push_back( flagga (4, 10.0, 400.0f, 300.0f) ); //Lägger en flagga i vector(4=Norga)
	  Flagglista.push_back( flagga (5, 10.0, 400.0f, 350.0f) ); //Lägger en flagga i vector(5=Sverige)
	   Flagglista.push_back( flagga (6, 10.0, 400.0f, 400.0f) ); //Lägger en flagga i vector(6=Sameland)
	    Flagglista.push_back( flagga (7, 10.0, 400.0f, 450.0f) ); //Lägger en flagga i vector(7=Åland)
		 Flagglista.push_back( flagga (8, 10.0, 400.0f, 500.0f) ); //Lägger en flagga i vector(8=Grönland)

Glöm inte att lägga till vector i programhuvudet:

 #include <vector>

Att räkna igenom en vector

[redigera]

Vi måste ha en räknare fördefinierad. Vi gör den unsigned (kan inte vara negativ) enbart för att undvika en massa varningsmeddelanden när vi går igenom listan:

unsigned int flagg_i = 0; //räknare

Sedan kan vi gå igenom hela listan med flaggor genom

for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
   { //Gå igenom hela listan
   } //Slut på att gå igenom hela listan

Det första vi måste göra är att ge dem en bild och placera ut dem, så alldeles efter att renderwindow skapats får du skriva:

for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
   { //Gå igenom hela listan
  //Ge spritesen bilden och placera ut flaggorna på spelplanen
  Flagglista.at(flagg_i).flaggsprite.SetImage(flaggbild); //ge flaggbilden till spriten 

//Välj ut rätt del av imagen att visa som flagga. Eftersom flaggorna har olika storlekar och lagts ut ojämt måste 
// varje flagga beräknas för hand. Vi antar att samtliga har storlek 70x50

	if ( Flagglista.at(flagg_i).arraynummer == 0)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(4,2,74,52)); 

	if ( Flagglista.at(flagg_i).arraynummer == 1)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(82,2,152,52));

	if ( Flagglista.at(flagg_i).arraynummer == 2)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(155,2,225,52));

	if ( Flagglista.at(flagg_i).arraynummer == 3)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(233,2,303,52));

	if ( Flagglista.at(flagg_i).arraynummer == 4)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(322,2,392,52));

	if ( Flagglista.at(flagg_i).arraynummer == 5) //Nästa rad
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(36,66,106,116));

	if ( Flagglista.at(flagg_i).arraynummer == 6) 
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(124,66,194,116));

	if ( Flagglista.at(flagg_i).arraynummer == 7) 
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(200,66,270,116));

	if ( Flagglista.at(flagg_i).arraynummer == 8) 
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(285,66,355,116));

Flagglista.at(flagg_i).flaggsprite.SetPosition(Flagglista.at(flagg_i).flagga_x, Flagglista.at(flagg_i).flagga_y);
   } //Slut på att gå igenom hela listan

Likaså när det är dags att rita ut flagorna på spelplanen i slutet av koden:

for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++) 
   { //Gå igenom hela listan
   App.Draw(Flagglista.at(flagg_i).flaggsprite);
   } //Slut på att gå igenom hela listan

Var klickar musen?

[redigera]

Vi måste också veta exakt var på spelplanen som man klickar om man klickar med vänster musknapp. Vi behöver två variabler:

double muskoordinat_x = 0.0f;
double muskoordinat_y = 0.0f;

Så på events lägger vi till:

         if (Event.MouseButton.Button == sf::Mouse::Left)
           { //vänster musknapp
            muskoordinat_x = App.GetInput().GetMouseX(); //Var i X-led klickar vi?
            muskoordinat_y = App.GetInput().GetMouseY(); // Var i Y-led klickar vi?
            std::cout<< "Muskoordinater = (" << muskoordinat_x << ", " << muskoordinat_y << " )" << std::endl;
           } //vänster musknapp

Cout använder vi för att kontrollera var vi klickar, den skall kommenteras bort när spelet är klart.

Slumptal

[redigera]

Som jag visat tidigare är slumptal enkla att ta fram i SFML. Det endas vi behöver tänka på är när vi vill ha slumptalsberäkningen så att man väljer nytt tal vid rätt tidpunkt. Inuti spelloopen sker det 60 ggr/sekunden Utanför spelloopen ändras det aldrig av sig själv. Jag valde att ha ett nytt tal, och nytt land, varje gång man klickar på ”SPACE” tangenten:

 int landsnummer  = -1;
 if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Space)) //SPACE tangent
{
landsnummer  = sf::Randomizer::Random(0, 8); //Välj ett land
}


Få ut namnet på landet till spelaren

[redigera]

Hur gör vi för att få ut namnet på landet till spelaren? Lägg till

#include <string>

Överst sedan fördjupar vi oss i koden. Det är inte så överdrivet svårt. Först måste vi definiera både en text vi skall skriva inuti programmet, och en speciell sf::string för att visa upp text på skärmen.

std::string landsnamn; //Namnet på landet
sf::String uttext; //Texten som skall skrivas ut på skärmen

När man trycker ner SPACE tangenten får man sedan dessutom fram vilket land man valt:


if (Flagglista[landsnummer].arraynummer == 0)
landsnamn = "Danmark";
if (Flagglista[landsnummer].arraynummer == 1)
landsnamn = "Färöarna";
if (Flagglista[landsnummer].arraynummer == 2)
landsnamn = "Island";
if (Flagglista[landsnummer].arraynummer == 3)
landsnamn = "Finland";
if (Flagglista[landsnummer].arraynummer == 4)
landsnamn = "Norge";
if (Flagglista[landsnummer].arraynummer == 5)
landsnamn = "Sverige";
if (Flagglista[landsnummer].arraynummer == 6)
landsnamn = "Sameland";
if (Flagglista[landsnummer].arraynummer == 7)
landsnamn = "Åland";
if (Flagglista[landsnummer].arraynummer == 8)
landsnamn = "Grönland";

Längre ner, när det är dags att rita ut flaggor och text lägger vi in:

uttext.SetText(landsnamn);
uttext.SetFont(sf::Font::GetDefaultFont());
uttext.SetSize(50);
uttext.SetPosition(20.f, 20.f); //placera ut texten 
App.Draw(uttext); //Rita ut landsnamnet

Kollisionshantering

[redigera]

Det är inte förrän nu vi faktiskt kan börja hantera kollisioner. Eftersom flaggorna är rektangulära gör vi en ”bounding box” test först. Vi ser helt enkelt om vi klickat inuti en flagga. Nu har vi inte längre två sprites som krockar, utan det enda vi använder är musens x- och y-värden när man klickar med vänster musknapp. För att göra det enkelt för oss behöver vi en funktion:

bool Box_check_hit(float musx, float musy, sf::Sprite &maolsprite) 
//se om vi klickat inuti flaggan
{
bool klickat_inuti = false; //returvärde
double maolx = maolsprite.GetPosition().x;
double maoly = maolsprite.GetPosition().y;

//Flaggorna är 50 x 70 stora

//  innanför vänster kant         innanför höger kant      innanför över kant         innanför neder kant 
if ((musx > (maolx)) && (musx < maolx + 70) && musy  > maoly  && musy < maoly  + 50 )
klickat_inuti = true; //returvärde

return klickat_inuti;
}



Sedan lägger vi in en kontroll i musklickfunktionen:

for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
     { //Gå igenom hela listan
      if (Box_check_hit(muskoordinat_x, muskoordinat_y, Flagglista.at(flagg_i).flaggsprite) == true)
          {//träff
         if (Flagglista.at(flagg_i).arraynummer == landsnummer)
	//Rätt gissat
	landsnamn = "RÄTT!";
	} //slut rätt gissat
	else
	if (Flagglista.at(flagg_i).arraynummer != landsnummer)
	//Fel gissat
	landsnamn = "FEL!";
	} //slut rätt gissat
               }//slut på träff
             } //Slut, gå igenom hela listan

Funktionen är så enkel att jag inte tror att den behöver så mycket förklaringar.

Anta istället att vi skall se avståndet från mittpunkten på flaggorna. Nu uppstår problemet att flaggorna är rektangulära, så vi får helt enkelt anta att flaggorna har en radie på 50 pixels. Det innebär att man missar om man klickar på kanten, men risken att få ”rätt” på ”fel” flagga försvinner.

bool Cirkular_check_hit (float musx, float musy, sf::Sprite &maolsprite) 
//se om kulan träffat
{
bool har_traeffat = false; //returvärde
double avstaond = 0.0; //Avståndet till spritens mittpunkter
double traeffavstaond = 0.0; //När träffar man?
double tempx; //sträckan i x-led
double tempy; //sträckan i y-led

//Ta fram koordinaterna 
//musx och musy är musens koordinater
//maolx och maoly är flaggans koordinater i mittpunkten
//Vi måste räkna om dem från flaggans övre vänstra hörn
double maolx = maolsprite.GetPosition().x + 35;
double maoly = maolsprite.GetPosition().y + 25;

double maol_radie = (1.4142135623730950488016887242097 * 50) / 2;

//Så när träffar dem? vilket avstånd minst från varandra?
traeffavstaond = maol_radie;

if (musx == maolx  && musy != maoly ) //de står på samma linje i x-led
        {
       avstaond = musy - maoly;
       if (avstaond <= traeffavstaond) //inom träffavstånd
       har_traeffat = true;
       }
else if (musy == maoly && musx != maolx ) //de står på samma linje i y-led
      {
       avstaond = musx - maolx;
       if (avstaond <= traeffavstaond) //inom träffavstånd
       har_traeffat = true;
      }
else if(musx != maolx && musy != maoly) //De står på olika platser både x och y
       { //Räkna med pythagoras
       tempx = musx-maolx;
       tempy = musy-maoly;
       // Räkna ut hypotenusan/avståndet genom att ta 
       // kvadratroten ur ena sträckan ^2 + andra sträckan ^2
       avstaond = sqrt ( tempx * tempx + tempy * tempy);
       if (avstaond <= traeffavstaond) //inom träffavstånd
       har_traeffat = true;
       } //Räkna med pythagoras
else if (musx == maolx && musy == maoly) //De står på exakt samma plats
       har_traeffat = true;

return har_traeffat;
}

Rörliga måltavlor

[redigera]

För att göra det litet mer spännande har alla flaggor en hastighet ställd på 10. Ta istället och gör den hastigheten till en variabel som du kan ändra.

float flaggans_hastighet = 0.0f;

Sedan, när du skall skapa flaggspritens och lägga in dem i vectorn, ger du dem ett framslumpat värde mellan 1-4 istället. Skriv in för allihop:

flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
Flagglista.push_back( flagga (0, flaggans_hastighet, 400.0f, 100.0f) ); //Lägger en flagga i vector(0=Danmark)

Vi vill bara ha dem att studsa fram och tillbaka i X-led, så räkna igenom hela flagglistan och flytta på dem

for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
    { //Gå igenom hela listan
    if(Flagglista.at(flagg_i).flaggsprite.GetPosition().x < 0 || Flagglista.at(flagg_i).flaggsprite.GetPosition().x > 800-70) //Utanför kanten
      {
      Flagglista.at(flagg_i).hastighet = Flagglista.at(flagg_i).hastighet * -1; //Vänd den
      }
     Flagglista.at(flagg_i).flaggsprite.Move(Flagglista.at(flagg_i).hastighet,0); //Låt den dra iväg
     } //Slut igenom hela listan

Koden går att optimera på mängder av olika sätt, men nu har du ett enkelt ramverk till spel som du kan utveckla litet hur du vill.

Färdig kod

[redigera]
//Hämta hem bild på flaggorna från:
// http://www.biblioteken.fi/File/d8811b0a-b714-4911-a69f-d634b8b952a8/width/397/height/119/nordiskt_flaggor.jpg

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

#define SFML_STATIC //Se till så att det inte behövs extra DLL-filer

using namespace std; 

class flagga 
{
public:
//Konstruktordeklaration, definition utanför klassdeklarationen
flagga (int  arraynummer, float hastighet, float flagga_x, float flagga_y);//startvärden
//Destruktion
~flagga(){};

int arraynummer; //Vilket land motsvarar den
float  hastighet; //Hur snabb är den
float flagga_x; // var är den i sidled i programmet
float flagga_y; //var är den i höjdled i programmet
sf::Sprite flaggsprite;
};

//Konstruktion av flaggor
flagga::flagga (int  ut_arraynummer, float ut_hastighet, float ut_flagga_x, float ut_flagga_y)
{
        arraynummer=ut_arraynummer;
        hastighet=ut_hastighet;
        flagga_x=ut_flagga_x;
        flagga_y=ut_flagga_y;
}


bool Box_check_hit(float musx, float musy, sf::Sprite &maolsprite);
bool Cirkular_check_hit (float musx, float musy, sf::Sprite &maolsprite);

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

/* Fyll i variabler inom main */
double muskoordinat_x = 0.0f;
double muskoordinat_y = 0.0f;
unsigned int flagg_i = 0; //räknare
int landsnummer  = -1; //Vilket land skall slumpas fram
std::string landsnamn; //Namnet på landet
sf::String uttext; //Texten som skall skrivas ut på skärmen
int flaggans_hastighet = 0.0f;

/* Ladda in bilden */
sf::Image flaggbild; //skapa en tom bildhållare som heter flaggbild
if (!flaggbild.LoadFromFile("nordiskt_flaggor.jpg")) return EXIT_FAILURE; 
//fyll den tomma bildhållaren med bilden nordiskt_flaggor.jpg

std::vector<flagga> Flagglista;
 
//Lägg in flaggorna:

flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
Flagglista.push_back( flagga (0, flaggans_hastighet, 400.0f, 100.0f) ); //Lägger en flagga i vector(0=Danmark)
                            //0=Danmark, 10=hastighet, 400= xposition 100 = yposition
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (1, flaggans_hastighet, 400.0f, 150.0f) ); //Lägger en flagga i vector(1=Färöarna)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (2, flaggans_hastighet, 400.0f, 200.0f) ); //Lägger en flagga i vector(2=Island)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (3, flaggans_hastighet, 400.0f, 250.0f) ); //Lägger en flagga i vector(3=Finland)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (4, flaggans_hastighet, 400.0f, 300.0f) ); //Lägger en flagga i vector(4=Norge)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (5, flaggans_hastighet, 400.0f, 350.0f) ); //Lägger en flagga i vector(5=Sverige)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (6, flaggans_hastighet, 400.0f, 400.0f) ); //Lägger en flagga i vector(6=Sameland)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (7, flaggans_hastighet, 400.0f, 450.0f) ); //Lägger en flagga i vector(7=Åland)
flaggans_hastighet = sf::Randomizer::Random(1.0f, 4.0f);
 Flagglista.push_back( flagga (8, flaggans_hastighet, 400.0f, 500.0f) ); //Lägger en flagga i vector(8=Grönland)

/* skapa spelfönstret */
sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Gissa Nordiska flaggor"); 


for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++) //for istället för while eftersom vi inte vet antalet poster längre
   { //Gå igenom hela listan
	//Ge spritesen bilden och placera ut flaggorna på spelplanen
	Flagglista.at(flagg_i).flaggsprite.SetImage(flaggbild); //ge flaggbilden till spriten 

	//Välj ut rätt del av imagen att visa som flagga. 
	// Eftersom flaggorna har olika storlekar och lagts ut ojämt måste varje flagga 
	// beräknas för hand. Vi antar att samtliga har storlek 70x50
	if ( Flagglista.at(flagg_i).arraynummer == 0)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(4,2,74,52)); 

	if ( Flagglista.at(flagg_i).arraynummer == 1)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(82,2,152,52));

	if ( Flagglista.at(flagg_i).arraynummer == 2)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(155,2,225,52));

	if ( Flagglista.at(flagg_i).arraynummer == 3)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(233,2,303,52));

	if ( Flagglista.at(flagg_i).arraynummer == 4)
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(322,2,392,52));

	if ( Flagglista.at(flagg_i).arraynummer == 5) //Nästa rad
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(36,66,106,116));

	if ( Flagglista.at(flagg_i).arraynummer == 6) 
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(124,66,194,116));

	if ( Flagglista.at(flagg_i).arraynummer == 7) 
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(200,66,270,116));

	if ( Flagglista.at(flagg_i).arraynummer == 8) 
		Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(285,66,355,116));

	Flagglista.at(flagg_i).flaggsprite.SetPosition(Flagglista.at(flagg_i).flagga_x, Flagglista.at(flagg_i).flagga_y);
    } //Slut på att gå igenom hela listan



while (App.IsOpened())
  { //while 1 startar, spelloopen körs

       sf::Event Event; //kolla om mus/tangentbord används
       while (App.GetEvent(Event))
         { //while 2, kontrollerar events
         if (Event.Type == sf::Event::Closed) //kryssat på [x] symbolen? 
         App.Close(); // stäng programmet

         if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) //ESC tangent
         App.Close(); // stäng programmet

		  if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Space)) //SPACE tangent
		  {
		  	landsnummer  = sf::Randomizer::Random(0, 8); //Välj ett land
			if (Flagglista[landsnummer].arraynummer == 0)
			landsnamn = "Danmark";
			if (Flagglista[landsnummer].arraynummer == 1)
			 landsnamn = "Färöarna";
			if (Flagglista[landsnummer].arraynummer == 2)
			landsnamn = "Island";
			if (Flagglista[landsnummer].arraynummer == 3)
			landsnamn = "Finland";
			if (Flagglista[landsnummer].arraynummer == 4)
			landsnamn = "Norge";
			if (Flagglista[landsnummer].arraynummer == 5)
			landsnamn = "Sverige";
			if (Flagglista[landsnummer].arraynummer == 6)
			landsnamn = "Sameland";
			if (Flagglista[landsnummer].arraynummer == 7)
			landsnamn = "Åland";
			if (Flagglista[landsnummer].arraynummer == 8)
			landsnamn = "Grönland";
			
		  }

		  //Var klickar vi med musen?
		  if (Event.MouseButton.Button == sf::Mouse::Left)
           { //vänster musknapp
            muskoordinat_x = App.GetInput().GetMouseX(); //Var i X-led klickar vi?
            muskoordinat_y = App.GetInput().GetMouseY(); //Var i Y-led klickar vi?

			 for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
            { //Gå igenom hela listan
           // if (Box_check_hit(muskoordinat_x, muskoordinat_y, Flagglista.at(flagg_i).flaggsprite) == true)
				 if (Cirkular_check_hit(muskoordinat_x, muskoordinat_y, Flagglista.at(flagg_i).flaggsprite) == true) 
               {//träff
					if (Flagglista.at(flagg_i).arraynummer == landsnummer)
					{ //Rätt gissat
					landsnamn = "RÄTT!";
					} //slut rätt gissat
					else
					if (Flagglista.at(flagg_i).arraynummer != landsnummer)
					{ //Rätt gissat
					landsnamn = "FEL!";
					} //slut rätt gissat
               }//slut på träff
             } //Slut, gå igenom hela listan


           } //vänster musknapp

         } //slut while 2, kontrollerar events


       uttext.SetText(landsnamn);
       uttext.SetFont(sf::Font::GetDefaultFont());
       uttext.SetSize(50);
		uttext.SetPosition(20.f, 20.f); //placera ut texten


       /* visa upp spelet  */

		for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
           { //Gå igenom hela listan
           if(Flagglista.at(flagg_i).flaggsprite.GetPosition().x < 0 || Flagglista.at(flagg_i).flaggsprite.GetPosition().x > 800 - 70) //Utanför kanten
			 {
			Flagglista.at(flagg_i).hastighet = Flagglista.at(flagg_i).hastighet * -1; //Vänd den
			}
        Flagglista.at(flagg_i).flaggsprite.Move(Flagglista.at(flagg_i).hastighet,0); //Låt den dra iväg
         } //Slut igenom hela listan


       App.Clear(sf::Color(0, 255, 0)); //rensa allt i fönstret och ersätt med grönt
       /*rita upp spelar sprites här */
       // App.Draw(Sprite); //Rita upp figuren på den yta spelaren ser

		App.Draw(uttext); //Rita ut landsnamnet


		for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++) 
       { //Gå igenom hela listan
	    //Rita ut flaggorna på spelplanen
	    App.Draw(Flagglista.at(flagg_i).flaggsprite);
       } //Slut på att gå igenom hela listan
       App.Display(); //visa upp ändringarna för användaren

 } //while 1 slutar, slut på att spelloopen körs

Flagglista.clear(); //Radera listan för att undvika minnesläckor

return 0; //Sista raden för slutet
} //Main slutar

/* Fyll i funktionsbeskrivningar */

//BOX**************************************************************************************'
bool Box_check_hit(float musx, float musy, sf::Sprite &maolsprite) 
//se om vi klickat inuti flaggan
{
bool klickat_inuti = false; //returvärde
double maolx = maolsprite.GetPosition().x;
double maoly = maolsprite.GetPosition().y;

//Flaggorna är 50 x 70 stora

//  innanför vänster kant         innanför höger kant      innanför över kant         innanför neder kant 
if ((musx > (maolx)) && (musx < maolx + 70) && musy  > maoly  && musy < maoly  + 50 )
klickat_inuti = true; //returvärde

return klickat_inuti;
}

//RADIE***************************************************
bool Cirkular_check_hit (float musx, float musy, sf::Sprite &maolsprite) 
//se om kulan träffat
{
bool har_traeffat = false; //returvärde
double avstaond = 0.0; //Avståndet till spritens mittpunkter
double traeffavstaond = 0.0; //När träffar man?
double tempx; //sträckan i x-led
double tempy; //sträckan i y-led

//Ta fram koordinaterna 
//musx och musy är musens koordinater
//maolx och maoly är flaggans koordinater i mittpunkten
//Vi måste räkna om dem från flaggans övre vänstra hörn
double maolx = maolsprite.GetPosition().x + 35;
double maoly = maolsprite.GetPosition().y + 25;

double maol_radie = (1.4142135623730950488016887242097 * 50) / 2;

//Så när träffar dem? vilket avstånd minst från varandra?
traeffavstaond = maol_radie;

if (musx == maolx  && musy != maoly ) //de står på samma linje i x-led
       {
       avstaond = musy - maoly;
       if (avstaond <= traeffavstaond) //inom träffavstånd
       har_traeffat = true;
       }
else if (musy == maoly && musx != maolx ) //de står på samma linje i y-led
      {
       avstaond = musx - maolx;
       if (avstaond <= traeffavstaond) //inom träffavstånd
       har_traeffat = true;
      }
else if(musx != maolx && musy != maoly) //De står på olika platser både x och y
       { //Räkna med pythagoras
       tempx = musx-maolx;
       tempy = musy-maoly;
       // Räkna ut hypotenusan/avståndet genom att ta 
       // kvadratroten ur ena sträckan ^2 + andra sträckan ^2
       avstaond = sqrt ( tempx * tempx + tempy * tempy);
       if (avstaond <= traeffavstaond) //inom träffavstånd
       har_traeffat = true;
       } //Räkna med pythagoras
else if (musx == maolx && musy == maoly) //De står på exakt samma plats
       har_traeffat = true;

return har_traeffat;
}

Påbyggnad

[redigera]

Hastigheten lämnar en del att önska, mycket beroende på att slumptalsgeneratorn skapr heltal och inte decimaltal. Ett sätt att komma runt det är att slumpa fram större tal och multiplicera hastigheten med ett decimaltal innan flaggan flyttas.

kod:

//Ändra alla slumptalen genom att ändra två parametrar
int r1, r2 =0;
r1 = 1.0f;
r2 = 10.0f; 

flaggans_hastighet = sf::Randomizer::Random(r1, r2);
//osv........

Flagglista.at(flagg_i).flaggsprite.Move(Flagglista.at(flagg_i).hastighet * 0.11,0); //Låt den dra iväg, obs multiplikation i x-led

Se till så att spelet börjar med Sverige så att man förstår att man skall klicka på en flagga, och att man skall klicka på SPACE för ny:

std::string landsnamn = "Träffa Sverigeflaggan - [SPACE] för ny flagga";//Namnet på landet
int landsnummer  = 5; //= Sverige

Gör så att man inte kan byta land innan man klickat på en flagga, lägg till följande kod i SPACE nedtryckningen:

if (landsnamn == "RÄTT!" || landsnamn == "FEL!")
   { //innehåller inte ett land
    landsnummer  = sf::Randomizer::Random(0, 8); //Välj ett land
    if (Flagglista[landsnummer].arraynummer == 0)
    landsnamn = "Danmark";
    // osv...         
   } //innehåller inte ett land

Byt bakgrunden till en mörkgrå färg som är tydligare att urskilja flaggorna mot:

App.Clear(sf::Color(100, 100, 100)); //rensa allt i fönstret och ersätt med mörkgrått

Ändra fönstret så att det inte går att ändra storlek, eftersom det förvrider hela spelet:

sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Gissa Nordiska flaggor", sf::Style::Close);

Poängräknare

[redigera]

Visa tydligt vad som händer om man gör rätt eller fel. Skapa en grön fyrkant som växer när man träffar rätt och en röd som växer om man väljer fel.

int Raett = 0;
int Fel = 0;

Där man byter landsnamn vid musklick får du också lägga in:

landsnamn = "RÄTT!";
Raett = Raett + 1;
//osv... gör likadant för fel

Skapa två rektanglar i bottnen, en grön och en röd:

App.Draw(sf::Shape::Rectangle(0,570,10 + (Raett * 30), 580, sf::Color(0, 255, 0) )); //Grön
App.Draw(sf::Shape::Rectangle(0,585,10 + (Fel * 30), 595, sf::Color(255, 0, 0) )); //Röd
//Längden avgörs av Raett poäng eller Fel poäng
//Vinst eller förlust vid 27 Raett eller Fel

Innan texten skrivs ut på spelplanen får du ändra landsnamnet till om man vann eller förlorade:

 if (Raett > 26)
   landsnamn="DU VANN!";
 if (Fel > 26)
   landsnamn="DU FÖRLORADE!";
 uttext.SetText(landsnamn);
//osv...

När man sedan gått igenom hela listan av flaggor vid träff med vänster musknapp kollar vi om vi fått en träff på en flagga. Har vi det är landsnamnet samma som RÄTT! eller FEL!, annars har man missat när man klickat. Då får man straffpoäng.

if (landsnamn != "RÄTT!" && landsnamn != "FEL!")
Fel = Fel + 1;

Kod med alla ändringar

[redigera]
//Hämta hem bild på flaggorna från:
// http://www.biblioteken.fi/File/d8811b0a-b714-4911-a69f-d634b8b952a8/width/397/height/119/nordiskt_flaggor.jpg

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

#define SFML_STATIC //Se till så att det inte behövs extra DLL-filer

using namespace std; 

class flagga 
{
public:
//Konstruktordeklaration, definition utanför klassdeklarationen
flagga (int  arraynummer, float hastighet, float flagga_x, float flagga_y);//startvärden
//Destruktion
~flagga(){};

int arraynummer; //Vilket land motsvarar den
float  hastighet; //Hur snabb är den
float flagga_x; // var är den i sidled i programmet
float flagga_y; //var är den i höjdled i programmet
sf::Sprite flaggsprite;
};

//Konstruktion av flaggor
flagga::flagga (int  ut_arraynummer, float ut_hastighet, float ut_flagga_x, float ut_flagga_y)
{
       arraynummer=ut_arraynummer;
       hastighet=ut_hastighet;
       flagga_x=ut_flagga_x;
       flagga_y=ut_flagga_y;
}


bool Box_check_hit(float musx, float musy, sf::Sprite &maolsprite);
bool Cirkular_check_hit (float musx, float musy, sf::Sprite &maolsprite);

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

/* Fyll i variabler inom main */
double muskoordinat_x = 0.0f;
double muskoordinat_y = 0.0f;
unsigned int flagg_i = 0; //räknare
int landsnummer  = 5; //Vilket land skall slumpas fram
std::string landsnamn = "Träffa Sverigeflaggan - \n[SPACE] för ny flagga"; //Namnet på landet
sf::String uttext; //Texten som skall skrivas ut på skärmen
int flaggans_hastighet = 0.0f;
int Raett = 0; //Hur många rätt har man?
int Fel = 0; //Hur många fel har man?


/* Ladda in bilden */
sf::Image flaggbild; //skapa en tom bildhållare som heter flaggbild
if (!flaggbild.LoadFromFile("nordiskt_flaggor.jpg")) return EXIT_FAILURE; 
//fyll den tomma bildhållaren med bilden nordiskt_flaggor.jpg

std::vector<flagga> Flagglista;

//Lägg in flaggorna:
int r1, r2 =0;
r1 = 1.0f;
r2 = 10.0f; 

flaggans_hastighet = sf::Randomizer::Random(r1, r2);
Flagglista.push_back( flagga (0, flaggans_hastighet, 400.0f, 100.0f) ); //Lägger en flagga i vector(0=Danmark)
                           //0=Danmark, 10=hastighet, 400= xposition 100 = yposition
flaggans_hastighet = sf::Randomizer::Random(r1, r2);
Flagglista.push_back( flagga (1, flaggans_hastighet, 400.0f, 150.0f) ); //Lägger en flagga i vector(1=Färöarna)
flaggans_hastighet = sf::Randomizer::Random(r1, r2);
 Flagglista.push_back( flagga (2, flaggans_hastighet, 400.0f, 200.0f) ); //Lägger en flagga i vector(2=Island)
 flaggans_hastighet = sf::Randomizer::Random(r1, r2);
  Flagglista.push_back( flagga (3, flaggans_hastighet, 400.0f, 250.0f) ); //Lägger en flagga i vector(3=Finland)
       flaggans_hastighet = sf::Randomizer::Random(r1, r2);
        Flagglista.push_back( flagga (4, flaggans_hastighet, 400.0f, 300.0f) ); //Lägger en flagga i vector(4=Norge)
        flaggans_hastighet = sf::Randomizer::Random(r1, r2);
         Flagglista.push_back( flagga (5, flaggans_hastighet, 400.0f, 350.0f) ); //Lägger en flagga i vector(5=Sverige)
         flaggans_hastighet = sf::Randomizer::Random(r1, r2);
          Flagglista.push_back( flagga (6, flaggans_hastighet, 400.0f, 400.0f) ); //Lägger en flagga i vector(6=Sameland)
          flaggans_hastighet = sf::Randomizer::Random(r1, r2);
           Flagglista.push_back( flagga (7, flaggans_hastighet, 400.0f, 450.0f) ); //Lägger en flagga i vector(7=Åland)
               flaggans_hastighet = sf::Randomizer::Random(r1, r2);
                Flagglista.push_back( flagga (8, flaggans_hastighet, 400.0f, 500.0f) ); //Lägger en flagga i vector(8=Grönland)

/* skapa spelfönstret */
sf::RenderWindow App(sf::VideoMode(800, 600, 32), "Gissa Nordiska flaggor", sf::Style::Close); 


for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++) //for istället för while eftersom vi inte vet antalet poster längre
  { //Gå igenom hela listan
       //Ge spritesen bilden och placera ut flaggorna på spelplanen
       Flagglista.at(flagg_i).flaggsprite.SetImage(flaggbild); //ge flaggbilden till spriten 

       //Välj ut rätt del av imagen att visa som flagga. 
       // Eftersom flaggorna har olika storlekar och lagts ut ojämt måste varje flagga 
       // beräknas för hand. Vi antar att samtliga har storlek 70x50
       if ( Flagglista.at(flagg_i).arraynummer == 0)
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(4,2,74,52)); 

       if ( Flagglista.at(flagg_i).arraynummer == 1)
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(82,2,152,52));

       if ( Flagglista.at(flagg_i).arraynummer == 2)
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(155,2,225,52));

       if ( Flagglista.at(flagg_i).arraynummer == 3)
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(233,2,303,52));

       if ( Flagglista.at(flagg_i).arraynummer == 4)
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(322,2,392,52));

       if ( Flagglista.at(flagg_i).arraynummer == 5) //Nästa rad
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(36,66,106,116));

       if ( Flagglista.at(flagg_i).arraynummer == 6) 
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(124,66,194,116));

       if ( Flagglista.at(flagg_i).arraynummer == 7) 
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(200,66,270,116));

       if ( Flagglista.at(flagg_i).arraynummer == 8) 
               Flagglista.at(flagg_i).flaggsprite.SetSubRect(sf::IntRect(285,66,355,116));

       Flagglista.at(flagg_i).flaggsprite.SetPosition(Flagglista.at(flagg_i).flagga_x, Flagglista.at(flagg_i).flagga_y);
   } //Slut på att gå igenom hela listan



while (App.IsOpened())
 { //while 1 startar, spelloopen körs

      sf::Event Event; //kolla om mus/tangentbord används
      while (App.GetEvent(Event))
        { //while 2, kontrollerar events
        if (Event.Type == sf::Event::Closed) //kryssat på [x] symbolen? 
        App.Close(); // stäng programmet

        if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Escape)) //ESC tangent
        App.Close(); // stäng programmet

                 if ((Event.Type == sf::Event::KeyPressed) && (Event.Key.Code == sf::Key::Space)) //SPACE tangent
                 {

					  if (landsnamn == "RÄTT!" || landsnamn == "FEL!")
					  { //innehåller inte ett land
                       landsnummer  = sf::Randomizer::Random(0, 8); //Välj ett land
                       if (Flagglista[landsnummer].arraynummer == 0)
                       landsnamn = "Danmark";
                       if (Flagglista[landsnummer].arraynummer == 1)
                        landsnamn = "Färöarna";
                       if (Flagglista[landsnummer].arraynummer == 2)
                       landsnamn = "Island";
                       if (Flagglista[landsnummer].arraynummer == 3)
                       landsnamn = "Finland";
                       if (Flagglista[landsnummer].arraynummer == 4)
                       landsnamn = "Norge";
                       if (Flagglista[landsnummer].arraynummer == 5)
                       landsnamn = "Sverige";
                       if (Flagglista[landsnummer].arraynummer == 6)
                       landsnamn = "Sameland";
                       if (Flagglista[landsnummer].arraynummer == 7)
                       landsnamn = "Åland";
                       if (Flagglista[landsnummer].arraynummer == 8)
                       landsnamn = "Grönland";
					  } //innehåller inte ett land
                 }

                 //Var klickar vi med musen?
                 if (Event.MouseButton.Button == sf::Mouse::Left)
          { //vänster musknapp
           muskoordinat_x = App.GetInput().GetMouseX(); //Var i X-led klickar vi?
           muskoordinat_y = App.GetInput().GetMouseY(); //Var i Y-led klickar vi?

            for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
           { //Gå igenom hela listan
          if (Box_check_hit(muskoordinat_x, muskoordinat_y, Flagglista.at(flagg_i).flaggsprite) == true)
                                //if (Cirkular_check_hit(muskoordinat_x, muskoordinat_y, Flagglista.at(flagg_i).flaggsprite) == true) 
              {//träff
                                       if (Flagglista.at(flagg_i).arraynummer == landsnummer)
                                       { //Rätt gissat
                                       landsnamn = "RÄTT!";
										Raett = Raett + 1;
                                       } //slut rätt gissat
                                       else
                                       if (Flagglista.at(flagg_i).arraynummer != landsnummer)
                                       { //Rätt gissat
                                       landsnamn = "FEL!";
										Fel = Fel + 1;
                                       } //slut rätt gissat
              }//slut på träff
		     
         
            } //Slut, gå igenom hela listan

			 if (landsnamn != "RÄTT!" && landsnamn != "FEL!")
				 Fel = Fel + 1;
          } //vänster musknapp

        } //slut while 2, kontrollerar events

	   if (Raett > 26)
		   landsnamn="DU VANN!";
	   if (Fel > 26)
		    landsnamn="DU FÖRLORADE!";

      uttext.SetText(landsnamn);
      uttext.SetFont(sf::Font::GetDefaultFont());
      uttext.SetSize(50);
      uttext.SetPosition(20.f, 20.f); //placera ut texten


      /* visa upp spelet  */

               for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++)
          { //Gå igenom hela listan
          if(Flagglista.at(flagg_i).flaggsprite.GetPosition().x < 0 || Flagglista.at(flagg_i).flaggsprite.GetPosition().x > 800 - 70) //Utanför kanten
                        {
                       Flagglista.at(flagg_i).hastighet = Flagglista.at(flagg_i).hastighet * -1; //Vänd den
                       }
       Flagglista.at(flagg_i).flaggsprite.Move(Flagglista.at(flagg_i).hastighet * 0.033,0); //Låt den dra iväg
        } //Slut igenom hela listan


      App.Clear(sf::Color(100, 100, 100)); //rensa allt i fönstret och ersätt med mörkgrått
      /*rita upp spelar sprites här */
      // App.Draw(Sprite); //Rita upp figuren på den yta spelaren ser

	   App.Draw(sf::Shape::Rectangle(0,570,10 + (Raett * 30), 580, sf::Color(0, 255, 0) )); //Grön
	   App.Draw(sf::Shape::Rectangle(0,585,10 + (Fel * 30), 595, sf::Color(255, 0, 0) )); //Röd
	   
	   //Vinst eller förlust vid 27 Raett eller Fel
               App.Draw(uttext); //Rita ut landsnamnet


               for (flagg_i=0; flagg_i < Flagglista.size(); flagg_i++) 
      { //Gå igenom hela listan
           //Rita ut flaggorna på spelplanen
           App.Draw(Flagglista.at(flagg_i).flaggsprite);
      } //Slut på att gå igenom hela listan
      App.Display(); //visa upp ändringarna för användaren

} //while 1 slutar, slut på att spelloopen körs

Flagglista.clear(); //Radera listan för att undvika minnesläckor

return 0; //Sista raden för slutet
} //Main slutar

/* Fyll i funktionsbeskrivningar */

//BOX**************************************************************************************'
bool Box_check_hit(float musx, float musy, sf::Sprite &maolsprite) 
//se om vi klickat inuti flaggan
{
bool klickat_inuti = false; //returvärde
double maolx = maolsprite.GetPosition().x;
double maoly = maolsprite.GetPosition().y;

//Flaggorna är 50 x 70 stora

//  innanför vänster kant         innanför höger kant      innanför över kant         innanför neder kant 
if ((musx > (maolx)) && (musx < maolx + 70) && musy  > maoly  && musy < maoly  + 50 )
klickat_inuti = true; //returvärde

return klickat_inuti;
}

//RADIE***************************************************
bool Cirkular_check_hit (float musx, float musy, sf::Sprite &maolsprite) 
//se om kulan träffat
{
bool har_traeffat = false; //returvärde
double avstaond = 0.0; //Avståndet till spritens mittpunkter
double traeffavstaond = 0.0; //När träffar man?
double tempx; //sträckan i x-led
double tempy; //sträckan i y-led

//Ta fram koordinaterna 
//musx och musy är musens koordinater
//maolx och maoly är flaggans koordinater i mittpunkten
//Vi måste räkna om dem från flaggans övre vänstra hörn
double maolx = maolsprite.GetPosition().x + 35;
double maoly = maolsprite.GetPosition().y + 25;

double maol_radie = (1.4142135623730950488016887242097 * 50) / 2;

//Så när träffar dem? vilket avstånd minst från varandra?
traeffavstaond = maol_radie;

if (musx == maolx  && musy != maoly ) //de står på samma linje i x-led
      {
      avstaond = musy - maoly;
      if (avstaond <= traeffavstaond) //inom träffavstånd
      har_traeffat = true;
      }
else if (musy == maoly && musx != maolx ) //de står på samma linje i y-led
     {
      avstaond = musx - maolx;
      if (avstaond <= traeffavstaond) //inom träffavstånd
      har_traeffat = true;
     }
else if(musx != maolx && musy != maoly) //De står på olika platser både x och y
      { //Räkna med pythagoras
      tempx = musx-maolx;
      tempy = musy-maoly;
      // Räkna ut hypotenusan/avståndet genom att ta 
      // kvadratroten ur ena sträckan ^2 + andra sträckan ^2
      avstaond = sqrt ( tempx * tempx + tempy * tempy);
      if (avstaond <= traeffavstaond) //inom träffavstånd
      har_traeffat = true;
      } //Räkna med pythagoras
else if (musx == maolx && musy == maoly) //De står på exakt samma plats
      har_traeffat = true;

return har_traeffat;
}