Dobre pytanie! Zanim przejdę do konkretnych pytań, które zadałeś, powiem: nie lekceważ potęgi prostoty. Tenpn ma rację. Należy pamiętać, że wszystko, co próbujesz zrobić z tymi podejściami, to znaleźć elegancki sposób na odroczenie wywołania funkcji lub oddzielenie dzwoniącego od odbiorcy. Mogę polecić coroutines jako zaskakująco intuicyjny sposób na złagodzenie niektórych z tych problemów, ale to trochę nie na temat. Czasami lepiej jest po prostu wywołać funkcję i żyć z faktem, że istota A jest sprzężona bezpośrednio z bytem B. Zobacz YAGNI.
To powiedziawszy, użyłem i byłem zadowolony z modelu sygnału / gniazda połączonego z prostym przekazywaniem wiadomości. Użyłem go w C ++ i Lua do dość udanego tytułu iPhone'a, który miał bardzo napięty harmonogram.
W przypadku sygnału / gniazda, jeśli chcę, aby jednostka A zrobiła coś w odpowiedzi na coś, co zrobiła istota B (np. Odblokowała drzwi, gdy coś umiera), mógłbym mieć jednostkę A subskrybującą bezpośrednio zdarzenie śmierci jednostki B. Albo być może jednostka A zasubskrybuje każdą z grup jednostek, zwiększy licznik każdego wystrzelonego zdarzenia i odblokuje drzwi po śmierci N z nich. Ponadto „grupa jednostek” i „N z nich” zwykle byłyby projektowane i zdefiniowane w danych poziomu. (Nawiasem mówiąc, jest to jeden z obszarów, w którym rogowce mogą naprawdę świecić, np. WaitForMultiple („Dying”, entA, entB, entC); door.Unlock ();)
Ale może to być uciążliwe, jeśli chodzi o reakcje ściśle powiązane z kodem C ++ lub z natury efemeryczne zdarzenia w grze: zadawanie obrażeń, przeładowywanie broni, debugowanie, oparte na lokalizacji informacje zwrotne AI oparte na graczach. W tym miejscu przekazywanie wiadomości może wypełnić luki. Zasadniczo sprowadza się do czegoś w rodzaju „powiedz wszystkim bytom w tym obszarze, by otrzymały obrażenia w ciągu 3 sekund” lub „ilekroć ukończysz fizykę, aby dowiedzieć się, kogo zastrzeliłem, powiedz im, aby uruchomiły tę funkcję skryptu”. Trudno wymyślić, jak to zrobić ładnie, używając publikowania / subskrypcji lub sygnału / automatu.
Może to być łatwo przesada (w porównaniu z przykładem tenpn). Może być również niewydajnym wzdęciem, jeśli masz dużo akcji. Ale pomimo swoich wad, podejście do „wiadomości i zdarzeń” bardzo dobrze łączy się ze skryptowym kodem gry (np. W Lua). Kod skryptu może definiować własne komunikaty i zdarzenia i reagować na nie bez względu na kod C ++. A kod skryptu może z łatwością wysyłać wiadomości, które wyzwalają kod C ++, takie jak zmiana poziomów, odtwarzanie dźwięków, a nawet po prostu pozwalanie broni ustawić, ile szkód zadaje wiadomość TakeDamage. Zaoszczędziło mi to mnóstwo czasu, ponieważ nie musiałem ciągle wygłupiać się z luabind. Pozwoliło mi to zachować cały mój kod luabind w jednym miejscu, ponieważ nie było go wiele. Po prawidłowym sprzężeniu
Z mojego doświadczenia w przypadku użycia # 2 wynika, że lepiej jest traktować to jako wydarzenie w innym kierunku. Zamiast pytać o zdrowie jednostki, uruchom zdarzenie / wyślij wiadomość za każdym razem, gdy zdrowie wprowadzi znaczącą zmianę.
Jeśli chodzi o interfejsy, btw, skończyłem z trzema klasami do wdrożenia tego wszystkiego: EventHost, EventClient i MessageClient. EventHosts tworzą boksy, EventClients subskrybują / łączą się z nimi, a MessageClients kojarzą delegata z wiadomością. Zauważ, że cel delegowany przez MessageClient niekoniecznie musi być tym samym obiektem, który jest właścicielem powiązania. Innymi słowy, MessageClients mogą istnieć wyłącznie w celu przekazywania wiadomości do innych obiektów. FWIW, metafora host / klient jest w pewnym sensie nieodpowiednia. Source / Sink może być lepszym pomysłem.
Przepraszam, trochę się tam bawiłem. To moja pierwsza odpowiedź :) Mam nadzieję, że to miało sens.