Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

1

04.03.2011, 11:17

GameStateManager

Hi

Ich programmierte gestern einen GameStateManager für meine Engine. Dieser Funktioniert auch, doch ich muss sagen, er ist nicht so toll gelöst. Ich habe eine Klasse erzeugt welche zuständig ist für den GameStateManager, diese beinhaltet nicht besonders viel, lediglich vier Funktionen und eine Variable die den aktuellen GameState speichert. Ich habe dann in der Sprite sowie in der Font Klasse eine Funktion namens SetGameState geschrieben. Dieser kann man den gewünschten GameState übergeben. Im SceneManager werden alle Objekte durch jeweilige Listen gerendert. Ich prüfe dort, ob das zu rendernde Objekt den gleichen GameState beinhaltet wie der aktuelle GameState, bei erfolgreicher Prüfung rendere ich das Objekt.

Hier ein Codeausschnitt:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
veResult veSceneManager::DrawAll()
{
    // -------------------------------------------------------------------------
    // Alle Objekte der Szene rendern
    // -------------------------------------------------------------------------

    // Alle Sprites rendern
    for(m_SpriteIterator = m_lSprite->begin(); m_SpriteIterator != m_lSprite->end(); m_SpriteIterator++)
    {
        if((*m_SpriteIterator)->IsWithAnimation() == false && m_pGameState->m_ActiveGameState == (*m_SpriteIterator)->m_GameState)
        {
            (*m_SpriteIterator)->RenderSprite();
        }
        else if(m_pGameState->m_ActiveGameState == (*m_SpriteIterator)->m_GameState)
        {
            (*m_SpriteIterator)->RenderSpriteWithAnimation();
        }
    }

    // Alle Fonts rendern
    for(m_FontIterator = m_lFont->begin(); m_FontIterator != m_lFont->end(); m_FontIterator++)
    {
        if(m_pGameState->m_ActiveGameState == (*m_FontIterator)->m_GameState)
        {
            (*m_FontIterator)->Render();
        }
    }

    return VE_OK;
}


Ich habe bereits gegoogelt, und habe einigse gefunden über den GameStateManager. Aber ich kann diese nicht richtig umsetzen, wegen meiner DrawAll Funktion im SceneManager, deshalb musste ich eine SetGameState-Funktion schreiben.

Hat jemand eine Idee wie ich für meine Engine einen effizienten GameStateManager programmieren kann?

LG Patrick

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

2

05.03.2011, 15:30

Zunächst einmal wird nicht so recht klar, was denn in Deiner Engine ein GameStateManager darstellen soll. Nach Deiner Beschreibung sind diese GameStates eher so etwas wie Layer. Denn Du scheinst darüber ja nur zu definieren, welche Objekte aktiv sind. Aber generell gehört zu einem GameState ja doch etwas mehr. Normalerweise steckt dort ja auch ein wenig Logik drin, um in einen anderen State zu wechseln (also von Running zu GameOver) beispielsweise.
Vielleicht wäre es praktischer (und auch in Deinem Zusammenhang leichter verständlich) wenn Du GameState in Layer umbenennst.
Man könnte das ganze wesentlich anders gestalten, aber das wäre dann wohl eher gravierende Änderungen an Deiner Engine und ich weiß nicht, wieviel Du umstellen willst.
Natürlich hast Du recht, dass Deine Methode äußerst ineffektiv ist.

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

3

05.03.2011, 15:37

Vielleicht mal ein Code Beispiel, wie so ein GameStateManager eher aussieht:

C-/C++-Quelltext

1
class GametState {public:  virtual void update(const float& elapsed) = 0;  virtual void onActivation() = 0;  virtual void onDeactivation() = 0;};public GameStateManager {typedef std::vector GameStates;public:  void update(const float& elapsed);  // add / activate / deactivate stateprivate:  GameState m_ActiveState;  GameStates m_GameStates;


Dann rufst Du die update Methode im GameStateManager auf und dort dann die update Methode vom aktive GameState.
Im GameStateManager rufst Du bei activate oder deactivate dann die onActivation etc. Methode auf.
Der Punkt ist, dann Du dann in der konkreten Implenetierung eines GameStates Deine Sprites und Font aktivierst usw.

Leider steh ich mit dem Code formatieren hier auf Kriegsfuß, aber ich hoffe, Du kannst es nachvollziehen.
Wie kriegt man denn den Zeilenumbruch im Code hin?

NachoMan

Community-Fossil

Beiträge: 3 885

Wohnort: Berlin

Beruf: (Nachhilfe)Lehrer (Mathematik, C++, Java, C#)

  • Private Nachricht senden

4

05.03.2011, 15:41

Leider steh ich mit dem Code formatieren hier auf Kriegsfuß, aber ich hoffe, Du kannst es nachvollziehen.
Wie kriegt man denn den Zeilenumbruch im Code hin?

versuch im "antworten" dialog mal den quellcode tab. der ist sowieso viel besser als der editortab, den ich persönlich nie benutze^^
eine alternative wäre http://pastebin.com/
"Der erste Trunk aus dem Becher der Erkenntnis macht einem zum Atheist, doch auf dem Grund des Bechers wartet Gott." - Werner Heisenberg
Biete Privatunterricht in Berlin und Online.
Kommt jemand mit Nach oMan?

KeksX

Community-Fossil

Beiträge: 2 107

Beruf: Game Designer

  • Private Nachricht senden

5

06.03.2011, 03:01

Hey,

ich versuch mal, meinen Gedankengang bei dem Ganzen zu zeigen.

Also zunächst:
Was ist ein GameState?
Ein GameState repräsentiert nicht nur irgendetwas, im Prinzip sagt der GameState aus, wo man sich gerade im Programm befindet:
Intro, Menü, spezifisches Menü oder aber eben das eigentliche Spiel, oder ein Dialog oder sonstwas.

Ein GameState hat also das Grundlegende gemeinsam: ein GameState muss Stati gewisser Variablen updaten können, davor gewisse Initialisierungen treffen können, außerdem das ganze auch noch zeichnen können sowie natürlich hinteher auch wieder aufräumen.

Eine abstrakte Klasse könnte also so aussehen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
class cGameState
{
public:
virtual ~cGameState(); 

virtual void StartState() = 0; // Initialisierungen etc.
virtual void CloseState() = 0; // Aufräumen!

virtual void Update(...) = 0; // Hier findet die Logik statt. Könnte man auch Think() oder so nennen, wies manchmal üblich ist
virtual void Render(...) = 0; // Und hier wird alles gezeichnet. 
};

Mit dieser Klasse haben wir die Basis geschaffen. Was jetzt noch fehlt ist eine Klasse, die die GameStates bearbeitet.
Am besten ist es imho folgendermaßen - so hast dus denk ich ma schon, wills trotzdem nur noch mal kurz klären:
Eine Klasse beinhaltet einen vector von GameStates und ruft dann eben immer die Update() und Render() Funktion des zuletzt hinzugefügten GameStates auf.
Dann fehlen eigentlich nur noch entsprechende Funktionen zum Leeren und Füllen des vectors und dann wars das.
Das könnte z.B. abgespeckt so aussehen:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
class cStateManager
{
public:
cStateManager();
~cStateManager();

void PushState(cGameState* pState); // Einzelnen State hinzufügen
cGameState* PopState(); // Neuesten State entfernen
void Update(); // Alle Update Funktionen aufrufen
void Render(); // Alle Render Funktionen aufufen
};

// blabla

etc etc etc, ich denke du weißt genau wie ich das meine ;).
Jetzt kommt der Clou:
Jeder State ruft den nächsten auf.
Wenn der IntroState bspw, der abgeleitet von cGameState ist, nun Text enthält und Bilder zum Rendern etc, kannst du einfach am Ende des States dem statemanager sagen, dass er einen neuen menustate hinzufügen soll z.B.
Du musst nur dafür sorgen, dass der StateManager von überall aus ansprechbar ist. Ich regel das ganz fies darüber, dass der statemanager nach dem singleton-pattern aufgebaut ist und er auch das Window handlet, so ist in jedem State alles nötig ansprechbar.

Ich verfahre mit diesem System schon eine ganze Weile und ich denke nicht, dass es das beste ist, aber es ist sehr leicht überall umzusetzen(da ich jetzt mehrere Libs etc getestet hab) und birgt für mich auch keine offensichtlichen Probleme.
Man kann da sicher noch modifizieren und optimieren, das werde ich wohl auch tun, aber für den Anfang ist es für mich DIE Lösung ^^
WIP Website: kevinheese.de

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

6

06.03.2011, 11:03

Hi

Ich weiss jetzt nur nicht, wie ich mein RenderSystem mit dem GameStateManager verbinden kann. Also ich kann durch die Device Klasse eine Funkiton AddSprite aufrufen, dieser muss man einen veSprite Zeiger übergeben. In dieser Funktion wird der übergebene Zeiger in einen Vector hinzugefügt. Durch die DrawAll Funktion werden alle Sprites gerendert.

Wie kann ich nun mit diesem RenderSystem die Render Funktion des GameState-Klasse benutzen?

So sehen die beiden Funktionen aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
veResult veDevice::AddSprite(veSprite * pSprite)
{
    // -------------------------------------------------------------------------
    // Ein Sprite hinzufügen
    // -------------------------------------------------------------------------

    // Dieses Sprite zum Vector hinzufügen
    m_vSprite->push_back(pSprite);

    return VE_OK;
}

veResult veDevice::DrawAll()
{
    // -------------------------------------------------------------------------
    // Alle hinzugefügten Objekte rendern
    // -------------------------------------------------------------------------

    // Alle Sprites rendern
    for(unsigned int i = 0; i < m_vSprite->size(); ++i)
    {
        // Die Render-Funktion aufrufen
        m_vSprite->at(i)->RenderSprite();
    }

    return VE_OK;
}


Hat jemand einen Vorschlag?

Rushh0ur

Frischling

Beiträge: 67

Beruf: Student Elektrotechnik

  • Private Nachricht senden

7

06.03.2011, 11:40

Du könntest, wenn man nun bei dem Beispiel von KeksX, bleibt bei der Initialisierung StartState() deine Sprites mit AddSprite hinzufügen.
In der Renderfunktion deine Bilder und Objekte Rendern und beim verlassen einer "GameState" über CloseState() deine Sprites wieder entfernen.

Das ganze natürlich für jede abgeleitete Klasse je nach dem was angezeigt werden soll anpassen.

Mfg Rushh0ur

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

8

06.03.2011, 12:32

Also ich soll, wenn ich AddSprite aufrufe, die Sprites immer noch gleich im Vector hinzufügen und zusätzlich noch in die GameState Klasse hinzufügen. Oder meinst du die Sprites direkt in die GameState Klasse hinzufügen, und das ganze RenderSystem in der GameState Klasse anlegen?

Rushh0ur

Frischling

Beiträge: 67

Beruf: Student Elektrotechnik

  • Private Nachricht senden

9

06.03.2011, 13:02

Naja kommt drauf an wie es bei dir am besten zu Handhaben ist, da du ein eigenständiges Rendersystem hast wird es wohl sinnvoller sein dieses entweder global zu definieren, da du es ja in jeder Instanz einer GameState-Klasse verwenden wirst, oder eventuell in der GameState-Elternklasse dein Renderklasse statisch zu definieren um es in jeder Kindklasse zur Verfügung zu stellen.

Mfg Rushh0ur

Patrick Egli

Treue Seele

  • »Patrick Egli« ist der Autor dieses Themas

Beiträge: 161

Wohnort: Rainstrasse 38

  • Private Nachricht senden

10

08.03.2011, 19:59

Nun funktioniert mein GameStateManager. Danke für eure Antworten.

Doch nun, wenn ich Sprites rendern will, werden sie zwar gerendert, aber mit komischen Farbeffekten, z.B. mit schwarzen Strichen.

Ich habe ein Bild dieser Sprites gemacht:


(Link)


Hat das auch schon jemand gehabt dieses Problem, oder weiss jemand wie man das beheben kann?

LG Patrick

Werbeanzeige