2010-04-14

MVC w obsłudze aktorów (obiektów) gry

Projektując obsługę agentów gry na potrzeby SpaceFight, nieświadomie zaaplikowałem wzorzec MVC (Model-View-Controller) - rozdzielenie aktora na model (czyli obiekt z danymi), widok (encja silnika graficznego/dźwiękowego) i kontroler. Podejście takie skutkuje sporą elastycznością w zakresie działania AI, stąd postaram się klarownie przedstawić koncepcję realizacji MVC w grze.

Aby MVC miało jakikolwiek sens, konieczne jest rozdzielenie rzeczywistego obiektu gry na trzy podobiekty - zbiór danych obiektu (dalej zwany modelem), audiowizualną reprezentację obiektu (dalej zwaną widokiem) oraz kod/skrypt kontrolujący logikę obiektu (zwany... a zgadnij ;-) ). Korzystając z gotowego silnika (w moim przypadku: OGRE), podział da się łatwo uzyskać - właściwy obiekt aktora jest jednocześnie modelem, zawierającym odniesienie (referencję) do encji graficznej (widoku) i abstrakcyjnej klasy kontrolera.

Sam model zawiera wszystkie "fizyczne" dane obiektu - parametry w mechanice gry, dane dotyczące ruchu i pozycji, wszelkie inne potrzebne dane. Update stanu obiektu jest wywoływany przez update stanu modelu - to model odpytuje kontrolera o akcje do wykonania i informuje widok o tym, gdzie i jak ma się rysować/odtwarzać. W przypadku SpaceFight model to abstrakcyjna klasa dziedzicząca z SpaceObject - zbiór tych klas jest jednocześnie zbiorem aktorów dla danej sceny (misji).

Widok to zbiór encji silnika, "włożonych" w jeden scene node. Samodzielnie nie wykonuje żadnych akcji (poza załadowaniem się z zasobów i narysowaniem na żądanie silnika), jest to tylko prezentacja istniejącego obiektu.

W przypadku kontrolera, sytuacja jest zabawniejsza. Mianowicie, żeby stosowanie kontrolera miało sens, musi być on zależny od typu obiektu. Moim rozwiązaniem było osadzenie go w konkretnej klasie modelu (np. klasa Ship posiada odniesienie do ShipController, klasa Missile do MissileController itd.) i wywoływanie aktualizacji stanu kontrolera z poziomu modelu. Mimo to, zostawiłem sobie pewną furtkę w stronę wielowątkowości - każdy model posiada osobną instancję kontrolera, która może być aktualizowana w osobnym wątku. Zadaniem kontrolera jest analiza globalnej sytuacji (w miarę konieczności odczytując dane z modelu) i wysłanie odpowiednich komend do kontrolowanego aktora.

Komunikacja między modelem i kontrolerem została przygotowana w możliwie najprostszy sposób: kontroler wysyła (za pośrednictwem ogólnej kolejki komunikatów) rozkaz do modelu, który w momencie update'u jest wykonywany i usuwany z kolejki. Pozwala to na znaczne uniezależnienie implementacji modelu i kontrolera, ponadto dla AI pozwala na dobrą symulację rzeczywistego gracza (wysyłane są komendy zgodne z możliwym podpięciem sterowania dla gracza).

Moje podejście do kontrolera ułatwiło jeszcze jedną sprawę: obsługę aktora gracza. Dla gracza stosowany jest ogólnosystemowy model z ogólnosystemowym widokiem, wyjątkiem jest wyłącznie kontroler, który zamiast samodzielnie analizować sytuację odczytuje informacje z wejścia. W efekcie gra teoretycznie umożliwia natychmiastowe przełączanie gracza między aktorami, jak również częściową lub całkowitą kontrolę aktora gracza przez AI. Przy planowanym stopniu skomplikowania takie rozwiązanie może okazać się użyteczne.

Brak komentarzy:

Prześlij komentarz