Programmera spel i C++ för nybörjare/Flaggklickare
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; }