Chcę zadać pytanie o sposób wymiany informacji między częściami silnika gry.
Silnik jest podzielony na cztery części: logikę, dane, interfejs użytkownika, grafikę. Na początku dokonałem tej wymiany za pomocą flag. Na przykład, jeśli nowy obiekt zostanie dodany do danych, flaga isNew
w klasie obiektu zostanie ustawiona jako true
. A potem część graficzna silnika sprawdzi tę flagę i doda obiekt do świata gry.
Jednak przy takim podejściu miałem napisać dużo kodu, aby przetworzyć każdą flagę każdego rodzaju obiektu.
Myślałem o użyciu jakiegoś systemu zdarzeń, ale nie mam wystarczającego doświadczenia, aby wiedzieć, czy byłoby to właściwe rozwiązanie.
Czy system zdarzeń jest jedynym właściwym podejściem, czy powinienem użyć czegoś innego?
Jeśli to ma znaczenie, używam Ogre jako silnika graficznego.
źródło
Odpowiedzi:
Moją ulubioną strukturą silnika gry jest model interfejsu i obiektu <-> wykorzystujący komunikację do komunikacji między prawie wszystkimi częściami.
Masz wiele interfejsów dla głównych części silnika, takich jak menedżer scen, moduł ładujący zasoby, dźwięk, renderer, fizyka itp.
Mam menedżera scen odpowiedzialnego za wszystkie obiekty w scenie / świecie 3D.
Obiekt jest bardzo atomową klasą, zawierającą tylko kilka rzeczy, które są wspólne dla prawie wszystkiego w twojej scenie, w moim silniku klasa obiektów ma tylko pozycję, obrót, listę komponentów i unikalny identyfikator. Identyfikator każdego obiektu jest generowany przez statyczny int, dzięki czemu żaden z dwóch obiektów nie będzie miał tego samego identyfikatora, co pozwala na wysyłanie wiadomości do obiektu według jego identyfikatora, zamiast konieczności posiadania wskaźnika do obiektu.
Lista składników obiektu daje główne właściwości obiektu. Na przykład, dla czegoś, co można zobaczyć w świecie 3D, można dać obiektowi komponent renderowania, który zawiera informacje o siatce renderowania. Jeśli chcesz, aby obiekt miał fizykę, nadaj mu komponent fizyki. Jeśli chcesz, aby coś działało jak kamera, daj mu element kamery. Lista komponentów może się zmieniać.
Kluczem jest komunikacja między interfejsami, obiektami i komponentami. W moim silniku mam ogólną klasę wiadomości, która zawiera tylko unikalny identyfikator i identyfikator typu wiadomości. Unikalny identyfikator to identyfikator obiektu, do którego wiadomość ma się udać, a identyfikator typu wiadomości jest używany przez obiekt odbierający wiadomość, aby wiedział, jaki to jest typ wiadomości.
Obiekty mogą obsłużyć komunikat, jeśli są potrzebne, i mogą przekazać wiadomość do każdego ze swoich komponentów, a komponenty często wykonują ważne czynności z komunikatem. Na przykład, jeśli chcesz zmienić i pozycję obiektu, wysyłasz obiektowi komunikat SetPosition, obiekt może zaktualizować swoją zmienną pozycji, gdy otrzyma komunikat, ale komponent renderujący może potrzebować komunikatu, aby zaktualizować pozycję siatki renderowania, i element fizyki może potrzebować komunikatu, aby zaktualizować pozycję ciała fizyki.
Oto bardzo prosty układ menedżera scen, obiektu i komponentu oraz przepływu komunikatów, który wymyśliłem w około godzinę, napisany w C ++. Po uruchomieniu ustawia pozycję na obiekcie, a wiadomość przechodzi przez komponent renderowania, a następnie pobiera pozycję z obiektu. Cieszyć się!
Napisałem również wersję C # i Scala poniższego kodu dla każdego, kto może biegle posługiwać się nimi, a nie C ++.
źródło
Myślę, że to najlepszy sposób na użycie Menedżera scen i interfejsów. Zaimplementowano przesyłanie wiadomości, ale użyłbym go jako drugiego podejścia. Wiadomości są dobre do komunikacji między wątkami. Używaj abstrakcji (interfejsów) gdziekolwiek możesz.
Nie wiem wiele o Ogre, więc mówię ogólnie.
Zasadniczo masz główną pętlę gry. Odbiera sygnały wejściowe, oblicza AI (od prostego ruchu do złożonej AI i logiki gry), ładuje zasoby [itp.] I wyświetla aktualny stan. Jest to podstawowy przykład, dzięki któremu można podzielić silnik na te części (InputManager, AIManager, ResourceManager, RenderManager). I powinieneś mieć SceneManager, który przechowuje wszystkie obiekty obecne w grze.
Każda z tych części i ich części składowe mają interfejsy. Staraj się więc zorganizować te części, aby wykonały swoją i tylko swoją pracę. Powinny używać podelementów, które oddziałują wewnętrznie do celów części nadrzędnej. W ten sposób nie będziesz się wplątać bez szansy na rozwinięcie się bez całkowitego przepisania.
ps, jeśli używasz C ++, rozważ użycie wzorca RAII
źródło