Kicsit megkésve bár de itt a folytatás. A késedelem oka egyrészt az érettségi vizsgák, másrészt el kellett döntenem, hogy hogyan tovább, mivel a játékprogramozás egyik legnehezebb része jön, az ütközések érzékelése. Számos megközelítés létezik. Az egyik lehetőség pixelre pontosan megnézni, hogy két test átfedésben van-e, de ez időigényes. A másik lehetőségünk az, hogy primitív alakzatokkal közelítsük a testeinket és az ezek koordinátái közti összefüggésekkel állapítsuk meg, hogy van-e átfedés. Ez a megoldás kevésbé pontos, de sokkal kevésbé terheli a processzort. Gyakran használják a kettő keverékét is, amikor a primitív alakzatok közti átfedésből létrehoznak egy újabb primitív alakzatot, és ha ez az alakzat létezik, akkor ezen belül vizsgálják pixel pontossággal az átfedéseket.
Mivel ebben a játékban csak primitív alakzataink vannak, viszonylag könnyű dolgunk lesz. Ebben a részben megcsináljuk a játékosról való visszapattanást úgy, hogy négyzettel közelítjük a labdánkat, a következő részben ezt a megoldást tovább fejlesztjük pixelre pontos megoldássá (ahol a négyzet és a játékos téglalapja közti átfedésben lévő pixeleket fogjuk vizsgálni).
Másik nehéz döntés az volt, hogy a legtöbb játékban sokféle komplex alakzat létezik, ezért általános megoldásokat kell kidolgozni az ütközések deketálására. Ez általában abból áll, hogy minden testet megadunk téglatestek/téglalapok halmazaként is (vagy egyéb primitív testek/alakzatok). Például egy Fifában a játékosokat pálcika emberre hajazó hosszúkás téglatestekkel közelíthetjük. Ezután már a processzornak csupán az ezek közti esetleges átfedéseket kell vizsgálnia, ami csupán néhány egyszerű matematikai művelet. A lényeg hogy ebből kifolyólag a legtöbb játék általános megoldásokkal rendelkezik. Ez azt jelenti, hogy bármely 2 objektum között ( amik így primitív alakzatok halmazai), meg tudja állapítani van-e átfedés. Ez már nehezebb feladat, és ilyen általános megoldásra nekünk nincs is szükségünk, mégis az lenne a legelegánsabb, hogyha ezt szemléltetném. Ugyanakkor a bonyolultsága és a leírásom bevezető mivolta miatt most csupán a pong játék speciális eseteivel fogok foglalkozni, talán egy későbbi cikkben megmutatok általánosabb megközelítéseket is.
Elsőként pár apró módosítást végezzünk a kódon, hogy ne legyen szinte lehetetlen utolérni a labdát. A maximális sebességet a main.h-ban emeljük 400-ra, a CPlayer konstruktorában pedig a gyorsulást 200-ra, valamint a length-et 50-re.
Eztán a player.h-ban a CPlayer class-ban hozzuk létre a Collide metódusunkat, ami paraméterként a labdát fogja várni. Ehhez azonban meg kell mondanunk a fordítónak, hogy mi az a CBall, hiszen itt az még nem került deklarációra. A labdát referenciaként fogjuk átadni, mivel a tulajdonságai az ütközés során módosulni fognak. Ezek után így fest a player.h:
{syntaxhighlighter brush:c}
#ifndef PLAYER_H_INCLUDED
#define PLAYER_H_INCLUDED
class CBall;
class CPlayer
{
public:
CPlayer();
sf::Color Color;
sf::Vector2f Position;
sf::Key::Code Up, Down;
float speed;
float acceleration;
float width, length;
void Move();
void Show();
void Collide(CBall& Ball);
};
#endif // PLAYER_H_INCLUDED
{/syntaxhighlighter}
Eztán lássunk neki a függvény megírásának. Viszont mivel a Cball-t is használni fogjuk, ezért az osztály deklarációjára a fordítónak szüksége van, így a ball.h-t is includeoljuk be a player.cpp-be.
{syntaxhighlighter brush:c}
#include "ball.h"
{/syntaxhighlighter}
Most már nekiállhatunk a Collide függvény megírásának. Az SFML beépített sf::FloatRect osztályát fogjuk használni, ami leegyszerűsíti a dolgunk. A neve a rectangle azaz téglalapból származik. Elsőként a labdából is létrehozunk egy ilyen objektumot, valamint a játékosból is. Az objektumot a bal felső és a jobb alsó koordinátái alapján fogjuk létrehozni. A labdánál ezeket a koordinátákat a középpont és a sugarak segítségével egyszerűen számolhatjuk. A játékosunk koordinátái egy az egyben a játékosunk bal felső sarka, ehhez hozzáadva a hosszt illetve a szélességet megkaphatjuk a jobb alsó sarkot is. Eztán csupán annyi a dolgunk, hogy leellenőrizzük, hogy van-e átfedés az így létrehozott két téglalap között. Ha van, akkor meghívjuk a labdánk Bounce metódusát, ami kezelni fogja, hogy ilyenkor mi a teendő. Ehhez szüksége lesz a játékosunk sebességére, hogy ez alapján elvégezhesse a saját sebességének a módosítását. Így néz ki a kód a gyakorlatban:
{syntaxhighlighter brush:c}
void CPlayer::Collide(CBall& Ball)
{
sf::FloatRect BallRect(Ball.Position.x-Ball.radius,Ball.Position.y-Ball.radius,
Ball.Position.x+Ball.radius,Ball.Position.y+Ball.radius);
sf::FloatRect PlayerRect(Position.x,Position.y,Position.x+width, Position.y+length);
sf::FloatRect Intersection;
if (PlayerRect.Intersects(BallRect,&Intersection))
{
// Pixel pontosságnál itt még az Intersection-ön belül vizsgálni fogjuk a pixeleket
Ball.Bounce(speed);
}
}
{/syntaxhighlighter}
Most, hogy hivatkoztunk a Bounce metódusára a labdánknak, itt az ideje, hogy meg is írjuk. Elsőként adjuk hozzá a metódus deklarációját az osztályunkhoz a ball.h-ban:
{syntaxhighlighter brush:c}
void Bounce(float pspeed);
{/syntaxhighlighter}
Eztán írjuk meg a metódust a ball.cpp-ben. Mit fog csinálni? Egyrészt az x irányú sebesség az ellentétére vált, mivel visszapattan. Másrészt az irányítás lehetősége végett az y irányú sebességéhez hozzáadjuk a játékos sebességét. Másrészt kimozdítjuk az átfedésből, mert ha a következő iterációban gyorsabban futna le, emiatt nem érne ki az átfedésből, akkor duplán pattanna. Ezután a kód így néz ki:
{syntaxhighlighter brush:c}
void CBall::Bounce(float pspeed)
{
float temp = Pong.GetFrameTime();
Position.x -= Speed.x*temp;
Position.y -= Speed.y*temp;
Speed.x *= -1;
Speed.y += pspeed;
}
{/syntaxhighlighter}
Ez azt jelenti, hogy készen is vagyunk az ütközés vizsgálatot végző metódusokkal. Már csak annyi dolgunk maradt, hogy a main.cpp-ben kihasználjuk őket:
{syntaxhighlighter brush:c}
Pong.Clear();
Player1.Move();
Player1.Show();
Player2.Move();
Player2.Show();
Ball.Move();
Ball.Show();
Player1.Collide(Ball);
Player2.Collide(Ball);
Pong.Display();
{/syntaxhighlighter}
Ennyi fért bele ebbe a részbe. A következőben pontosítunk az ütközés vizsgálaton és ha elég rövidre sikerül, akkor a pontozásba is belekezdünk. Azt viszont megállapíthatjuk, hogy az így létrehozott játékunk már játszható és működőképes!