Po pierwsze, nie mam na myśli zarządzania scenami; Stan gry definiuję luźno jako jakikolwiek stan w grze, który ma wpływ na to, czy dane wejściowe użytkownika powinny być włączone, czy też niektórzy aktorzy powinni zostać tymczasowo wyłączeni itp.
Jako konkretny przykład, powiedzmy, że jest to gra w klasyczną Battlechess. Po tym, jak wykonuję ruch, aby wziąć kawałek innego gracza, rozpoczyna się krótka sekwencja bitwy. Podczas tej sekwencji gracz nie powinien mieć możliwości przenoszenia pionków. Jak więc śledziłbyś tego rodzaju zmianę stanu? Maszyna skończona? Prosty czek boolowski? Wygląda na to, że ten drugi działałby dobrze tylko w grze z bardzo niewielką liczbą zmian tego rodzaju.
Mogę wymyślić wiele prostych sposobów radzenia sobie z tym za pomocą automatów skończonych, ale widzę też, jak szybko wymykają się spod kontroli. Jestem ciekawy, czy istnieje bardziej elegancki sposób na śledzenie stanów / przejść gry.
Odpowiedzi:
Kiedyś natknąłem się na artykuł, który dość elegancko rozwiązuje twój problem. Jest to podstawowa implementacja FSM, która jest wywoływana w głównej pętli. W dalszej części tej odpowiedzi opisałem podstawowe podsumowanie tego artykułu.
Twój podstawowy stan gry wygląda następująco:
Każdy stan gry jest reprezentowany przez implementację tego interfejsu. W twoim przykładzie Battlechess może to oznaczać następujące stany:
Stany są zarządzane w silniku stanu:
Zauważ, że każdy stan potrzebuje w pewnym momencie wskaźnika do CGameEngine, więc sam stan może zdecydować, czy należy wprowadzić nowy stan. Artykuł sugeruje przekazanie CGameEngine jako parametru dla HandleEvents, Update i Draw.
W końcu twoja główna pętla zajmuje się tylko silnikiem stanu:
źródło
Zaczynam od obsługi tego rodzaju rzeczy w najprostszy możliwy sposób.
Następnie dodam kontrole względem tej flagi logicznej w odpowiednich miejscach.
Jeśli później stwierdzę, że potrzebuję więcej specjalnych przypadków niż to - i tylko to - ponownie uwzględniam coś lepszego. Zazwyczaj podejmuję 3 podejścia:
enum { PRE_MOVE, MOVE, POST_MOVE }
i dodaj przejścia tam, gdzie to potrzebne. Następnie mogę sprawdzić na podstawie tego wyliczenia, w którym sprawdzałem na podstawie flagi logicznej. Jest to prosta zmiana, ale redukująca liczbę rzeczy, które musisz sprawdzić, pozwala na użycie instrukcji switch do skutecznego zarządzania zachowaniem itp.pieceSelectionManager->disable()
lub podobnie na początku sekwencji, ipieceSelectionManager->enable()
. Nadal zasadniczo masz flagi, ale teraz są one przechowywane bliżej kontrolowanego obiektu i nie musisz utrzymywać żadnego dodatkowego stanu w kodzie gry.Ogólnie rzecz biorąc, nigdy nie muszę iść dalej, jeśli chodzi o podstacje specjalne, więc nie sądzę, że istnieje ryzyko, że „szybko wymknie się spod kontroli”.
źródło
http://www.ai-junkie.com/architecture/state_driven/tut_state1.html to piękny samouczek zarządzania stanem gry! Możesz go użyć do elementów gry lub do systemu menu takiego jak powyżej.
Zaczyna uczyć o wzorcu stanowym , a następnie wdraża
State Machine
i sukcesywnie go rozwija. To bardzo dobra lektura! Daje ci solidne zrozumienie, jak działa cała koncepcja i jak zastosować ją do nowych rodzajów problemów!źródło
Staram się nie używać do tego celu automatu stanów i booleanów, ponieważ oba nie są skalowalne. Oba zamieniają się w bałagan, gdy rośnie liczba stanów.
Zazwyczaj rozgrywkę projektuję jako sekwencję działań i konsekwencji, każdy stan gry przychodzi naturalnie, bez potrzeby definiowania go osobno.
Na przykład w przypadku wyłączania danych wejściowych odtwarzacza: masz pewien moduł obsługi danych wejściowych użytkownika i pewne wizualne wskazanie, że dane wejściowe są wyłączone, powinieneś uczynić z nich jeden obiekt lub komponent, więc aby wyłączyć dane wejściowe, po prostu wyłącz cały obiekt, nie musisz zsynchronizuj je w jakiejś maszynie stanów lub zareaguj na jakiś wskaźnik boolowski.
źródło