Czytam Game Coding Complete, a autor zaleca komunikację sterowaną zdarzeniami między obiektami i modułami gry.
Zasadniczo wszyscy żyjący aktorzy gier powinni komunikować się z kluczowymi modułami (fizyka, sztuczna inteligencja, logika gry, widok gry itp.) Za pośrednictwem wewnętrznego systemu powiadamiania o zdarzeniach. Oznacza to konieczność zaprojektowania wydajnego menedżera wydarzeń. Źle zaprojektowany system zjada cykle procesora, szczególnie wpływając na platformy mobilne.
Czy jest to sprawdzone i zalecane podejście? Jak zdecydować, czy go użyć?
architecture
software-engineering
events
Bunkai.Satori
źródło
źródło
libuv
niedawno pojawił się jako udana biblioteka wydarzeń. (Znajduje się w C. Node.js jest najbardziej znanym przypadkiem użycia.)Odpowiedzi:
Jest to rozszerzenie mojego komentarza do pełnej odpowiedzi, zgodnie z sugestią.
Tak , jasne i proste. Komunikacja musi się zdarzyć, a zdarzają się sytuacje, w których „Czy już tam jesteśmy?” wymagane jest odpytywanie typu, sprawdzanie, czy powinni robić coś innego, generalnie marnuje czas. Zamiast tego możesz sprawić, by zareagowali na rzeczy, które im powierzono. Ponadto dobrze zdefiniowana ścieżka komunikacji między obiektami / systemami / modułami znacznie przyspiesza konfiguracje równoległe.
Przedstawiłem ogólny przegląd systemu powiadamiania o zdarzeniach w przepełnieniu stosu . Użyłem go od szkoły do profesjonalnych tytułów gier, a teraz produktów innych niż gry, za każdym razem dostosowanych do przypadku użycia.
EDYCJA : Aby odpowiedzieć na pytanie komentarza, skąd wiesz, które obiekty powinny otrzymać komunikat : Same obiekty powinny
Request
być powiadamiane o zdarzeniach. TwójEventMessagingSystem
(EMS) będzie wymagałRegister(int iEventId, IEventMessagingSystem * pOjbect, (EMSCallback)fpMethodToUseForACallback)
zarówno dopasowania, jak i utworzeniaUnregister
(unikalny wpis dlaiEventId
wskaźnika obiektu i wywołania zwrotnego). W ten sposób, gdy obiekt chce wiedzieć o wiadomości, może to zrobićRegister()
w systemie. Kiedy nie musi już wiedzieć o wydarzeniach, może to zrobićUnregister()
. Oczywiście chcesz mieć pulę tych obiektów rejestracji wywołania zwrotnego i skuteczny sposób dodawania / usuwania ich z list. (Zwykle korzystam z tablic z funkcją samodzielnego zamawiania; fantazyjny sposób powiedzenia, że śledzą własne przydziały między stosem puli nieużywanych obiektów a tablicami, które w razie potrzeby zmieniają swoje wymiary)EDYCJA : Aby uzyskać całkowicie działającą grę z pętlą aktualizacji i systemem powiadamiania o zdarzeniach, możesz chcieć sprawdzić mój oldschoolowy projekt . Odwołanie do powyższego wpisu Przepełnienie stosu również się do niego odnosi.
źródło
Uważam, że powinieneś zacząć tworzyć grę i wdrożyć to, co jest dla Ciebie wygodne. Gdy to zrobisz, będziesz używać MVC w niektórych miejscach, ale nie w innych; wydarzenia w niektórych miejscach, ale nie w innych; elementy niektóre miejsca, ale inne dziedziczą; w niektórych miejscach czysty projekt, w innych nieokrzesany.
I to jest OK, ponieważ kiedy skończysz, będziesz mieć grę, a gra będzie o wiele fajniejsza niż system przekazywania wiadomości.
źródło
Lepszym pytaniem jest, jakie są alternatywy? W tak złożonym systemie z odpowiednio podzielonymi modułami dla fizyki, sztucznej inteligencji itp., W jaki sposób można zaaranżować te systemy?
Przekazywanie wiadomości wydaje się być „najlepszym” rozwiązaniem tego problemu. Nie mogę teraz wymyślić alternatyw. Ale w praktyce istnieje wiele przykładów przekazywania wiadomości. W rzeczywistości systemy operacyjne używają przekazywania komunikatów do kilku swoich funkcji. Z Wikipedii :
(Komunikacja międzyprocesowa to komunikacja między procesami (tj. Uruchamianie instancji programów) w środowisku systemu operacyjnego)
Więc jeśli jest wystarczająco dobry dla systemu operacyjnego, prawdopodobnie jest wystarczająco dobry dla gry, prawda? Są też inne korzyści, ale wyjaśnię ten artykuł z Wikipedii na temat przekazywania wiadomości .
Zadałem również pytanie dotyczące przepełnienia stosu: „Struktury danych do przekazywania wiadomości w programie?” , które możesz chcieć przeczytać.
źródło
Wiadomości ogólnie działają dobrze, gdy:
Im bardziej Twoje potrzeby będą pasować do tej listy, tym lepsze będą odpowiednie wiadomości. Na bardzo ogólnym poziomie są całkiem niezłe. Wykonują dobrą robotę, nie marnując cykli procesora tylko po to, aby zrobić nic innego niż odpytywanie lub inne bardziej globalne rozwiązania. Są fantastyczni w rozłączaniu części bazy kodu, co zawsze jest pomocne.
źródło
Świetne odpowiedzi do tej pory od Jamesa i Ricket'a, odpowiadam tylko, aby dodać nutkę ostrożności. Komunikacja przekazująca / sterowana zdarzeniami jest z pewnością ważnym narzędziem w arsenale programisty, ale można ją łatwo wykorzystać. Kiedy masz młotek, wszystko wygląda jak gwóźdź i tak dalej.
Absolutnie, w 100%, powinieneś pomyśleć o przepływie danych wokół swojego tytułu i mieć pełną świadomość tego, jak informacje są przekazywane z jednego podsystemu do drugiego. W niektórych przypadkach przekazywanie wiadomości jest zdecydowanie najlepsze; w innych bardziej odpowiednie może być, aby jeden podsystem działał na liście współdzielonych obiektów, umożliwiając innym podsystemom działanie na tej samej wspólnej liście; i wiele innych metod.
Przez cały czas powinieneś mieć świadomość, które podsystemy potrzebują dostępu do danych i kiedy mają do nich dostęp. Wpływa to na zdolność do równoległości i optymalizacji, a także pomaga uniknąć bardziej podstępnych problemów, gdy różne części silnika są zbyt mocno splecione. Musisz pomyśleć o zminimalizowaniu niepotrzebnego kopiowania danych oraz o tym, jak układ danych wpływa na użycie pamięci podręcznej. Wszystko to poprowadzi Cię do najlepszego rozwiązania w każdym przypadku.
To powiedziawszy, prawie każda gra, nad którą kiedykolwiek pracowałem, ma solidny system asynchronicznego przekazywania wiadomości / powiadamiania o zdarzeniach. Umożliwia pisanie zwięzłego, wydajnego i łatwego w utrzymaniu kodu, nawet w obliczu złożonych i szybko zmieniających się potrzeb projektowych.
źródło
Wiem, że ten post jest dość stary, ale nie mogłem się oprzeć.
Niedawno zbudowałem silnik gry. Wykorzystuje biblioteki stron 3D do renderowania i fizyki, ale napisałem główną część, która definiuje i przetwarza byty i logikę gry.
Silnik z pewnością stosuje tradycyjne podejście. Istnieje główna pętla aktualizacji, która wywołuje funkcję aktualizacji dla wszystkich jednostek. Kolizje są zgłaszane bezpośrednio przez jednostki oddzwaniania. Komunikacja między jednostkami odbywa się za pomocą inteligentnych wskaźników wymienianych między jednostkami.
Istnieje prymitywny system komunikatów, który przetwarza tylko niewielką grupę jednostek w celu przesyłania komunikatów. Te wiadomości są lepiej przetwarzane pod koniec interakcji w grze (na przykład tworzenie lub zniszczenie bytu), ponieważ mogą zepsuć się z listą aktualizacji. Pod koniec każdej pętli gry zużywa się niewielka lista wiadomości.
Mimo prymitywnego systemu komunikatów powiedziałbym, że system jest w dużej mierze „oparty na pętli aktualizacji”.
Dobrze. Po użyciu tego systemu uważam, że jest on bardzo prosty, szybki i dobrze zorganizowany. Logika gry jest widoczna i zamknięta w bytach, a nie dynamiczna jak w quewe wiadomości. Naprawdę nie chciałbym, aby zdarzenia były sterowane zdarzeniami, ponieważ moim zdaniem systemy zdarzeń wprowadzają niepotrzebną złożoność do logiki gry i sprawiają, że kod gry jest bardzo trudny do zrozumienia i debugowania.
Ale myślę też, że czysty system oparty na pętli aktualizacji, taki jak mój, również ma pewne problemy.
Na przykład: W niektórych momentach jedna istota może znajdować się w stanie „nic nie rób”, może czekać na zbliżającego się gracza lub coś innego. W większości tych przypadków jednostka spala czas procesora za darmo i lepiej jest ją wyłączyć i włączyć, gdy nastąpi określone zdarzenie.
Więc w moim następnym silniku gry zamierzam przyjąć inne podejście. Podmioty zarejestrują się do operacji silnika, takich jak aktualizacja, rysowanie, wykrywanie kolizji i tak dalej. Każde z tych zdarzeń będzie miało osobne listy interfejsów encji dla rzeczywistych encji.
źródło
Tak. Jest to bardzo skuteczny sposób komunikacji między systemami gier. Wydarzenia pomagają rozdzielić wiele systemów i umożliwiają nawet samodzielne kompilowanie rzeczy bez wzajemnego poznania się. Oznacza to, że Twoje klasy mogą być łatwiej prototypowane, a czas kompilacji krótszy. Co ważniejsze, zamiast bałaganu zależności powstaje płaska konstrukcja kodu.
Inną dużą zaletą zdarzeń jest to, że można je łatwo przesyłać strumieniowo przez sieć lub inny kanał tekstowy. Możesz je nagrać do późniejszego odtworzenia. Możliwości są nieskończone.
Kolejna korzyść: możesz mieć kilka podsystemów nasłuchujących tego samego zdarzenia. Na przykład wszystkie Twoje zdalne widoki (odtwarzacze) mogą automatycznie subskrybować zdarzenie tworzenia encji i spawnować encje na każdym kliencie przy niewielkiej pracy z twojej strony. Wyobraź sobie, ile by to było pracy, gdybyś nie używał zdarzeń: musiałbyś
Update()
gdzieś dzwonić, a może dzwonićview->CreateEntity
z logiki gry (gdzie wiedza na temat widoku i wymaganych informacji nie należy). Trudno rozwiązać ten problem bez wydarzeń.Dzięki wydarzeniom otrzymujesz eleganckie i oddzielone rozwiązanie, które obsługuje nieskończoną liczbę obiektów o nieograniczonej liczbie rodzajów, które mogą po prostu subskrybować wydarzenia i robić swoje, gdy coś się wydarzy w Twojej grze. Dlatego wydarzenia są świetne.
Więcej informacji i wdrożenie tutaj .
źródło
Z tego, co widziałem, nie wydaje się zbyt powszechne, aby silnik był całkowicie oparty na przesyłaniu wiadomości. Oczywiście istnieją podsystemy, które bardzo dobrze nadają się do takiego systemu przesyłania wiadomości, takiego jak sieć, GUI i ewentualnie inne. Ogólnie rzecz biorąc, jest kilka problemów, o których mogę myśleć:
Dzięki powszechnemu podejściu twórców gier, że „musi być możliwie jak najbardziej wydajny”, taki system przesyłania wiadomości nie jest tak powszechny.
Zgadzam się jednak, że powinieneś po prostu spróbować. Z pewnością taki system ma wiele zalet, a moc obliczeniowa jest dziś tania.
źródło