Autor Wątek: Organizacja głównej pętli gry  (Przeczytany 1643 razy)

Offline angarek1

  • Użytkownik

# Czerwiec 01, 2013, 18:39:48
Witam!
Mam problem z sensowną organizacją zasobów w main loop'ie gry.
Załóżmy, że jest sobie taki oto kod:

while(gamestate != EXIT)
{
switch(gamestate)
{
case MENU:
break;
case PLAY:
break;
case GAME_OVER:
break;
case WIN:
break;

}
}

I teraz pytanie. W którym miejscu kodu powinno się przechowywać rzeczy typu inicjalizacja zmiennych, wczytywanie tekstur etc. dla poszczególnych stanów.
Przeanalizowałem kilka opcji i jakoś nie mogę znaleźć tej idealnej.
-Wewnątrz pętli nie, bo za każdym razem wczytywałoby wszystko od nowa (rzecz chyba oczywista).
-Poza pętlą też raczej nie bardzo, bo przy dużych projektach gdzie każdy stan gry musi mieć np. kilkadziesiąt/kilkaset tekstur wczytanie wszystkiego naraz niepotrzebnie zmarnuje tylko zasoby.
-Stworzenie oddzielnych pętli dla każdego stanu – też to chyba nie ma sensu. Niepotrzebny bałagan i potrzeba powtarzania części kodu dla każdego stanu oddzielnie.

Istnieje jakieś mądre rozwiązanie?

Offline Mr. Spam

  • Miłośnik przetworów mięsnych

Offline Avaj

  • Użytkownik

# Czerwiec 01, 2013, 18:43:15
Jak zmieniasz stan, to ładujesz zasoby dla tego stanu, albo coś w stylu:

Kod: (java) [Zaznacz]
class GameState
{
  public void onInit()
  {
    ładowaniezasobów();
  }
 
  public void onUpdate(dt)
  {
    ..
  }

  public void onDraw()
  {
    ...
  }
}


Offline angarek1

  • Użytkownik

# Czerwiec 01, 2013, 19:08:05
OK, ale jeżeli np. dla stanu PLAY potrzebuje zmiennej int time to w dalszym ciągu będę musiał ją zainicjalizować na stałe, niezależnie od tego czy akurat ten stan odpalę czy nie. No chyba, że jest jakaś możliwość dynamicznej inicjalizacji zmiennych z poziomu metody klasy tak, żeby zmienna pozostała dostępna w klasie po zakończeniu wykonywania metody (coś jak static tylko, że dla klasy).

Offline Avaj

  • Użytkownik

  • +1
# Czerwiec 01, 2013, 19:42:08
Zrób sobie stos stanów, jak wrzucasz np. stan menu, to pod spodem możesz nadal mieć stan PLAY, więc żadne zmienne nie zginą. Chyba, że nie rozumiem czym ten int time jest...

Offline Frondeus

  • Użytkownik

  • +1
# Czerwiec 01, 2013, 19:44:35
Mając taką klasę/interfejs (To tylko przykład):
class GameState
{
public:
virtual void init()=0;
virtual void update()=0;
virtual void draw()=0;
};
Możesz robić dziedziczne klasy:
class Menu: public GameState
{
public:
void init();
void update();
void draw();
};

I teraz proponuje:
std::vector<GameState*> states;
std::stack<GameState*> currentState;
Oczywiście daje tu przykład c++ ;)

I wtedy Na samym początku gry:

states.push_back(new Menu());
/****/ //Itp. dodajesz wszystkie stany
for(GameState* state: states) state->init();
currentState.push(states[0]); // wrzucasz menu na stos.

Jesli w menu wybierzesz np nowa gre:
currentState.push(states[1]);// wrzucasz stan gry na stos
Jeśli w grze chcesz wrócić do poprzedniego stanu (np gameplay->menu):
currentState.pop();

Możesz pododawać metody w stylu onBegin() onEnd() jeśli chcesz...
Sam trochę pomyśl i napisz coś swojego :) To jest tylko przykład mojego rozwiązania. Niekoniecznie jest najlepszy.

//Edit:: Widze, że Avaj mnie ubiegł ;)

Offline angarek1

  • Użytkownik

# Czerwiec 01, 2013, 20:07:37
@Frondeus, @Avaj
Dzięki wielkie. Tyle kombinowania, a to można tak łatwiutko ;)

Offline Xirdus

  • Redaktor

  • +3
# Czerwiec 01, 2013, 21:34:15


Był ostatnio podobny wątek na tym forum: http://forum.warsztat.gd/index.php?topic=27082.0
Przedstawiono tam bardzo ciekawe rozwiązanie alternatywne do stosu:
Po pierwsze zbuduj sobie dokładny graf stanów. Co gdzie ma się dziać, kiedy przechodzi itp.

To Ci dokładnie wyjaśni kwestię, którą poruszył Xion - czy wszystkie sytuacje są warte rozważania jako "Stany".

Potem już kwestia wyboru konkretnej architektury rozwiązania - ważne, żeby była wygodna. Prosta implementacja maszyny stanów: każdy stan ma wskaźnik do osiągalnych, pod wypływem sygnałów zamiana stanu. Wszystkie stany zaś siedzą w głównej strukturze gdy, tam są tworzone i ew. niszczone(choć to akurat się dzieje dopiero na exit applikacji przez OS, w trakcie zwyczajnie nie ma po co). Wszystkie stany są tworzone raz, buduje graf i sobie śmiga. Nie tworzę stanu Menu za każdym razem gdy do niego wchodzę bo po co? Jedyny wyjątek to stan Levela gry, który tworze na bazie deskryptora poziomu co level.

Co do danych. Pewne dane są oczywiście w przestrzeni współdzielonej - głównej strukturze gry, do której stany mają dostęp.  Reszta to prywatna sprawa stanów.

Moim zdaniem prosta maszyna stanów sterowana konkretnymi sygnałami + aktualny stan w głównej strukturze gry odpowiednio zmieniany będzie znacznie wygodniejszy od pomysłu stosu Xirdusa. Choć to już raczej kwestie indywidualne.

PS. Oczywiście pozostaje jeszcze jedna kwestia. Czy na pewno chcesz mieć jedną pętlę główną i w której działa jakiś polimorficzny stan? To rodzi problemy, o których piszesz(jeżeli analiza gry takie pokazuje). Czy nie lepszy jest state->execute(gameStructure) i posiadanie pętli "głównej" per-stan i pełną kontrolą tego co chcesz. Ja tak mam w jednym silniczku opartym na libgdx. Wygodnie jak cholera!

Bo męczyć się z generycznymi stanami wszędzie można, ale nie koniecznie trzeba ;).

Offline angarek1

  • Użytkownik

# Czerwiec 01, 2013, 22:25:16
Dzięki za linka (jakimś cudem przeoczyłem ten wątek) ;)