Kész a projektünk valamint az alkalmazásunk váza. Itt az ideje, hogy átgondoljuk mikből fog állni az alkalmazás. Mivel egy egyszerű játékról van szó, ez nem hosszú feladat. Van egy labda és két játékos. Ez 2 osztályt (labda és a játékos), valamint 3 objektumot jelent ( 2 a játékos osztályból, 1 a labda osztályból). Alapvetően nagyobb projekteknél minden osztályt külön fájlban szoktak létrehozni, így most én is ezt a sémát fogom követni. Az előző részben leírtakhoz hasonlóan hozzuk létre a projektünkben a következő új fájlokat: player.cpp, player.h, ball.cpp, ball.h. FIGYELEM: a headerek létrehozásánál láthatunk valami szokatlant, ami a source fájlok létrehozásánál nem volt. Mégpedig kér tőlünk header guard word-öt a program, de ezt a header fájlneve alapján magától kitölti. A következőt találjuk az így létrehozott headerjeinkben:
{syntaxhighlighter brush:c}#ifndef PLAYER_H_INCLUDED
#define PLAYER_H_INCLUDED
#endif // PLAYER_H_INCLUDED{/syntaxhighlighter}
Ez a kód a preprocessornak szól (# kódok mindig az előfordítónak szólnak), a célja az, hogy a header még véletlenül se kerülhessen be egyazon fájlba kétszer.
Következő lépésként include-oljuk be az újonnan létrehozott headereket main.cpp-be:
{syntaxhighlighter brush:c}
#include "player.h"
#include "ball.h"
{/syntaxhighlighter}
Azért használunk itt idézőjeleket az includeoláskor < és > helyett, mert ezek a fájlok a projektünk részei, tehát az előfordító nem a saját headerjei hanem a mieink között fogja őket keresni. Hasonló módon includeoljuk be a player.h-t a player.cpp-be és ball.h-t a ball.cpp-be. Adjuk hozzá a 2 új cpp-hez a main-ban is megtalálható cstdlib és SFML/Graphics.hpp includejait.
Ezután nálam így néz ki a player.cpp:
{syntaxhighlighter brush:c}
#include <cstdlib>
#include <SFML/Graphics.hpp>
#include "player.h"
{/syntaxhighlighter}
Álljunk neki a tényleges kódolásnak. Írjuk meg elsőként a ball.h-ba a labda osztályát. Én az osztályokat nagy C-vel jelölöm, ez a saját szokásom, ami szerintem átláthatóbbá teszi a kódokat. Ezt vagy betartjátok vagy nem, teljesen lényegtelen, de ebből kifolyólag az én class-om neve CBall lesz. Gondoljuk végig mit akarunk a ladbától? Azt hogy mozogjon és megjelenjen. A két metódusa az osztálynak ezért Move és Show lesz. Milyen tulajdonságai vannak? Sebesség vektor, ezt én a programomban x és y irányú sebesség komponensek segítségével fogom kezelni. Emellett még koordinátákkal, sugárral és színnel rendelkezik. Mivel ez egy egyszerűbb játék, itt most az osztály minden eleme publikus lesz, bár nagyobb projektekben tanácsos egyes elemeket private vagy protected módon tárolni. Ezeket végiggondolva így néz ki a CBall class, ami a ball.h-ban található:
{syntaxhighlighter brush:c}
#ifndef BALL_H_INCLUDED
#define BALL_H_INCLUDED
class CBall
{
public:
CBall();
sf::Color Color;
sf::Vector2f Position;
sf::Vector2f Speed;
int radius;
void Move();
void Show();
};
#endif // BALL_H_INCLUDED
{/syntaxhighlighter}
Az sf::Vector2f egy olyan osztály ami x és y elemeket tartalmaz, amik float típusúak, így tárolom ezekben a koordinátákat és a sebességkomponenseket. Elsőként írjuk meg a konstruktort a ball.cpp-ben amiben inicializáljuk az elemeket:
{syntaxhighlighter brush:c}
CBall::CBall()
{
Color = sf::Color(255,255,255);
Position = sf::Vector2f(400,300);
Speed = sf::Vector2f(50,50);
radius = 5;
}
{/syntaxhighlighter}
Azt ígértem, hogy ebben a részben már rajzolunk is a képernyőre, úgyhogy a következő függvényünk legyen a kirajzoló. Ehhez viszont először tudatosítanunk kell a fordítóval, hogy létezik egy globális objektum, amit a main.cpp-ben már megdeklaráltunk. Ezért adjuk hozzá a ball.cpp tetejéhez az includeok alá a következő deklarációt:
{syntaxhighlighter brush:c}extern sf::RenderWindow Pong;{/syntaxhighlighter}
Innen tudni fogja, hogy egy másik fájlban már létezik ez az objektum, így itt is megengedi, hogy bátran használhassuk. Ezután a rajzolást végző függvényünk csupán pár sor lesz:
{syntaxhighlighter brush:c}
void CBall::Show()
{
Pong.Draw(sf::Shape::Circle(Position.x, Position.y, radius, Color));
}
{/syntaxhighlighter}
Mi értelme lenne ugyanannak a körnek a kirajzolásának másodpercenként 120szor? Vigyünk bele egy kis mozgást :). Ehhez egy kis kinematika. A mozgást fel fogjuk bontani x és y irányú mozgásokra. A két irányba történt mozgás együttes eredménye ki fogja adni a számunkra szükséges mozgást. A következő probléma, hogy nem minden frame tart ugyanolyan hosszú ideig, ráadásul minden gépteljesítménye eltér, mégis az lenne optimális, ha mindenhol ugyanolyan sebességgel futna a programunk. Tehát a számolásba bele kell venni az előző frame óta eltelt időt is. Ezt a GetFrameTime függvény segítségével kaphatjuk meg, ami a RenerWindow metódusa. Ezzel az idővel szoroznunk kell az elmozdulásunkat, hiszen ha például kétszer annyi idő telt el, akkor kétszer annyit kell mozgatni a labdánkon. Ennek ismeretében már könnyen megírhatjuk a Move függvényt:
{syntaxhighlighter brush:c}
void CBall::Move()
{
float temp = Pong.GetFrameTime();
Position.x += Speed.x*temp;
Position.y += Speed.y*temp;
}
{/syntaxhighlighter}
Eztán térjünk vissza a main.cpp-be, és használjuk az új CBall classunkat. Az sf::Event deklaráció alatt hozzuk is létre gyorsan a lasztink:
{syntaxhighlighter brush:c}CBall Ball;{/syntaxhighlighter}
Ezután csak annyi a dolgunk, hogy a főciklusba a Pong.Display() fölött mozgatjuk illetve kirajzoltatjuk a lasztinkat:
{syntaxhighlighter brush:c}
Ball.Move();
Ball.Show();
{/syntaxhighlighter}
Ha futtatjuk a programunkat azt tapasztaljuk, hogy egy szép vonal egyre nagyobbra nő a képernyőn. Magyarázat: a labdát kirajzoljuk iterációnként, de senki nem mondta a programunknak, hogy az előzőt tüntesse el. Megoldás: Az előzőleg hozzáadott kód elé szúrjuk be ezt a sort:
{syntaxhighlighter brush:c}
Pong.Clear();
{/syntaxhighlighter}
Ezután egy lassan, komótosan mozgó labdát fogunk látni, ahogy kimegy a képernyőnkről. Ebben a részben ennyire futotta, a következőben a labdát már a képernyőn belül tartjuk, és hagyjuk, hogy pattogjon.
Végeredmény a ball.cpp-ben:
{syntaxhighlighter brush:c}
#include <cstdlib>
#include <SFML/Graphics.hpp>
#include "ball.h"
extern sf::RenderWindow Pong;
CBall::CBall()
{
Color = sf::Color(255,255,255);
Position = sf::Vector2f(400,300);
Speed = sf::Vector2f(50,50);
radius = 5;
}
void CBall::Show()
{
Pong.Draw(sf::Shape::Circle(Position.x, Position.y, radius, Color));
}
void CBall::Move()
{
float temp = Pong.GetFrameTime();
Position.x += Speed.x*temp;
Position.y += Speed.y*temp;
}{/syntaxhighlighter}
Végeredmény main.cpp-ben:
{syntaxhighlighter brush:c}
#include <cstdlib>
#include <SFML/Graphics.hpp>
#include "player.h"
#include "ball.h"
sf::RenderWindow Pong;
int main ( int argc, char** argv )
{
Pong.Create(sf::VideoMode(800, 600, 32), "Pong", sf::Style::Close, sf::WindowSettings(24,8,4));
Pong.SetFramerateLimit(120);
Pong.UseVerticalSync(true);
sf::Event Event;
CBall Ball;
while (Pong.IsOpened())
{
while(Pong.GetEvent(Event))
{
if(Event.Type == sf::Event::Closed)
Pong.Close();
}
Pong.Clear();
Ball.Move();
Ball.Show();
Pong.Display();
}
return EXIT_SUCCESS;
}{/syntaxhighlighter}