OOP bei Spiel
-
Hallo.
Ich habe mir einige Klassen für ein 2D-Jump 'n' Run-Spiel gebastelt. Wie kann das folgende Problem elegant lösen?
Ich habe eine Stammklasse, World, von der ich in WinMain eine Instanz erzeuge. Sie beinhaltet u.a. die Klasse Graphics für Grafikfunktionen, sowie Hero für den Helden und bis zu 1024 andere Objekte. Alles läuft problemlos. Nun aber möchte ich (für jedes Frame) die Objekte bewegen lassen (void Object::Move()). Ich werde noch weitere Kreaturentypen hinzufügen, die dann auch primitive KI haben werden. Doch mein Problem ist: Wie können die Objekte auf die Welt (wo befindet sich was; unsigned long *World::world) zugreifen? Muss ich da für jeden Aufruf von Move() einen Zeiger mitgeben? Oder beim Konstruktor des Objekts einen Zeiger auf *world? Ich möchte dieses Problem so lösen, dass ich immer noch eine objektorientiertheit habe, also nicht etwa mit globalen Vars oder so. Danke.
Anmerkung:
- Ich verwende Polymorphie, d.h. ich werde alle Gegner von Object bzw. Creature ableiten
- TEXTURE, WORLD und ähnliches sind enum'sclass World { public: World(); ~World(); void CalcFrame(); Hero *hero; private: void DrawFrame(); unsigned long GetAbsoluteX(D3DXVECTOR2 pos); unsigned long GetAbsoluteY(D3DXVECTOR2 pos); unsigned long GetRelativeX(D3DXVECTOR2 pos); unsigned long GetRelativeY(D3DXVECTOR2 pos); WORLD GetWorld(unsigned long x, unsigned long y); void LoadWorld(unsigned long w); Graphics graphics; Object *object[1024]; unsigned long worldSize; unsigned long *world; float horizScrolling; STATUS status; unsigned long progress; }; class Object { public: Object(D3DXVECTOR2 pos, unsigned long w, unsigned long h); ~Object(); virtual void Move() = 0; D3DXVECTOR2 pos; unsigned long width; unsigned long height; TEXTURE texture[8]; }; class Item : public Object { public: Item(D3DXVECTOR2 pos, unsigned long w, unsigned long h, OBJECT t); ~Item(); void Move(); OBJECT type; }; class Creature : public Object { public: Creature(D3DXVECTOR2 pos, unsigned long w, unsigned long h); ~Creature(); }; class Hero : public Creature { public: Hero(D3DXVECTOR2 pos, unsigned long w, unsigned long h); ~Hero(); void Move(WORLD (*world)(unsigned long, unsigned long)); void SpacePressed(); bool jumpActive; unsigned long jumpCount; bool keyRight; bool keyLeft; };
-
jepp, du könntest die Referenz auf das Worldobjekt einfach in den Constructor von Object mit aufnehmen, also:
class Object { public: Object(World* world, D3DXVECTOR2 pos, unsigned long w, unsigned long h); ~Object(); virtual void Move() = 0; D3DXVECTOR2 pos; unsigned long width; unsigned long height; TEXTURE texture[8]; };und das natürlich entsprechend in den abgeleiteten Klassen ...
[edit]
was sollte daran nich OO sein?
-
Ich habe ähnliche Probleme, ich denke mal die beste Lösung wär, wenn man das ganze Problem mit Iteraroen (statisch verwalten) löst. Frag am besten mal im Spieleprogrammierungsforum nach und lade dir mal die dusmania Engine runter. gibts bei:
http://www.spieleprogrammierer.de/gucke dir einfach mal das Prinzip an, wie sie programmiert ist.
Man kommt mit Iteratoren wesentlich weiter, als mit Vererbungen.
-
Ich möchte dieses Problem so lösen, dass ich immer noch eine objektorientiertheit habe, also nicht etwa mit globalen Vars oder so.
Das hab ich bei meinem Spiel auch zuerst gedacht. Aber dann bin ich draufgekommen dass es mit globalen Variablen sehr einfach und meiner Meinung nach kein bisschen unsauber geht.
GameBase.h:
class CTileMap; class CLap; namespace GameBase { extern CTileMap *map; extern CLap *lap; }GameBase.cpp:
#include "GameBase.h" CTileMap *GameBase::map=0; CLap *GameBase::lap=0;TileMap.cpp:
#include "GameBase.h" CTileMap::CTileMap() { GameBase::map=this; }Wenn ich jetzt in einer Datei auf *map zugreifen will mach ich folgendes:
- GameBase.h inkludieren
- Will ich z.B. CTileMap verwenden, muss ich TileMap.h inkludieren. In Gamebase.h ist zwar deklariert dass es diese Klasse gibt, die genaue Definition ist aber unbekannt. Dadurch inkludiere ich nur das was ich brauche.
- Wenn ich GameBase öfters verwende, mach ich einfachusing namespace GameBase;Meiner Meinung nach ist das eine schnelle und funktionierende Lösung. Ist mir sauber genug.
Ich hab auch noch eine Alternativlösung parat:
// in gameobject.h class CGameObject { static CTileMap *map; static CLap *lap; } // in der cpp CTileMap *CGameObject::map=0; CLap *CGameObject::lap=0;Im Konstruktor von CTileMap beispielsweise wird dann folgendes gemacht:
CGameObject::map=this;Jede Klasse die mit dem Spiel zu tun hat wird davon abgeleitet und kennt dann diese Instanzen.
So kann man's auch machen - ich hab Variante 1 gewählt.mfg. Tubos
-
Globale Variablen sind unsauber und schaden der Datenkapselung.
-
ist bei solchen fällen, wo man weiss, es soll nur EIN objekt einer klasse geben nicht sowas wie ein singleton angesagt? (was ja auch sowas wie ne globale variable ist..) fänd ich im falle von WORLD schon richtig.
-
Man kann ja folgendes machen:
class Cx { private: static Cx m_x; };--> Kannst Die Klasse lokal aufrufen.
-
Erstmal - wieso sind denn alle Methoden der World-Klasse privat? So kann keine deiner "Objekt"-Klassen etwas damit machen.
Zweitens - wenn du virtuelle Methoden verwendest, solltest du überall die selbe Signatur einsetzen (und die Signatur von Hero::Move() sieht sowieso grauenhaft aus - wenn mich nicht alles täuscht, erwartet die eine Funtion als Parameter, die zwei unsigned in ein Enum verrechnet).Zu deinem eigentlichen Problem - da du nur eine Welt hast, bietet sich ein globales Objekt "theWorld" imho an.
-
Ich tät ein Singleton machen
class World { private: static World *Instance; public: World(); static World *Singleton() { return Instance; } }; World::World() { Instance = this; }Ist aber eh Geschmackssache

-
LordJaxom schrieb:
Ich tät ein Singleton machen
class World { private: static World *Instance; public: World(); static World *Singleton() { return Instance; } }; World::World() { Instance = this; }Ist aber eh Geschmackssache

Tut man bei dem einfach "Standardsingleton" den Konstruktor nicht private machen?
-
Na, keine Haare spalten
Dann müsste konsequent auch die Methode Singleton noch die Instanz erzeugen 
-
Ich finde dir Sache mit den Globalen Variablen wirklich blöd, denn wo will man sie deklarieren ?!?
Ich hab auch ne ganze Weile dran gesessen und bin dann zum Entschluss gekommen das man mit statischen Variablen, die in der Klasse deklariert sind vielen Problemen aus dem Weg geht.
ZB.:
ihr habt ne Soundclass, die Waves abspielt --> Wollt iht jede Klasse wie Player, Vehicle, World, Weapon von ihr erben lassen oder wo wollt ihr ne Globale deklarieren?Wenn man ne Enige patch oder nen Wrapper weiß ich ja nicht, wie das mit Globalen Variablen aussehen soll??
-
statische variablen in der klasse sind doch schon fast ein singleton:
class Single { private: static Single* pSingle; Single() { ... //konstruier } ... public: static Single* Get() { if(!pSingle) pSingle = new Single( ...//params); return pSingle; } static void Release() { if pSingle delete pSingle; } ... };aufruf erfolgt dann mit
Single::Get()->memberFun();wenn kein object der klasse existiert wird eins gemacht und memberfun ausgeführt, wenns schon eins gibt wird memberfun sofort ausgeführt. es gibt also immer nur EIN und nur EIN object dieser Klasse...
vor programmschluss nicht vergessen release() aufzurufen. die macht auch nur was, wenn das object existiert.wenn du das includierst kannst du diese klasse von überall aufrufen, wie ne globale variable.
ich weiss nicht wie gut dir das ins system passt, ich hab damit jedenfalls gute erfahrungen gemacht...
du kannst die auch so machen das andere klassen davon erben, geht auch. aber dann schön aufpassen das du nicht irgendwann in globalen vars erstickst!