Zaprojektowałem system encji dla FPS. Zasadniczo działa tak:
Mamy „światowy” obiekt o nazwie GameWorld. Zawiera tablicę GameObject, a także tablicę ComponentManager.
GameObject zawiera tablicę Component. Zapewnia również bardzo prosty mechanizm zdarzeń. Same komponenty mogą wysyłać zdarzenie do jednostki, które jest transmitowane do wszystkich komponentów.
Komponent jest w zasadzie czymś, co nadaje GameObject określone właściwości, a ponieważ GameObject jest tak naprawdę tylko ich pojemnikiem, wszystko, co ma związek z obiektem gry, dzieje się w Components. Przykłady obejmują ViewComponent, PhysicsComponent i LogicComponent. Jeśli potrzebna jest komunikacja między nimi, można to zrobić za pomocą zdarzeń.
ComponentManager to po prostu interfejs podobny do Component, a dla każdej klasy Component powinna zasadniczo istnieć jedna klasa ComponentManager. Ci menedżerowie komponentów są odpowiedzialni za tworzenie komponentów i inicjowanie ich właściwościami odczytanymi z czegoś takiego jak plik XML.
ComponentManager zajmuje się także masowymi aktualizacjami komponentów, takich jak PhysicsComponent, w którym będę korzystać z zewnętrznej biblioteki (która robi wszystko na świecie na raz).
W celu konfiguracji będę używać fabryki dla encji, które będą czytały plik XML lub skrypt, utworzą komponenty określone w pliku (który również dodaje odniesienie do niego w odpowiednim menedżerze komponentów dla masowych aktualizacji) i następnie wstrzyknij je do obiektu GameObject.
Teraz pojawia się mój problem: spróbuję użyć tego do gier wieloosobowych. Nie mam pojęcia, jak do tego podejść.
Po pierwsze: jakie podmioty powinny mieć klienci od samego początku? Powinienem zacząć od wyjaśnienia, w jaki sposób silnik dla jednego gracza określiłby, które podmioty stworzyć.
W edytorze poziomów możesz tworzyć „pędzle” i „byty”. Pędzle służą do takich rzeczy jak ściany, podłogi i sufity, w zasadzie proste kształty. Podmioty to GameObject, o którym ci mówiłem. Podczas tworzenia jednostek w edytorze poziomów można określić właściwości dla każdego z jego składników. Te właściwości są przekazywane bezpośrednio do konstruktora w skrypcie encji.
Po zapisaniu poziomu do załadowania silnik jest rozkładany na listę encji i powiązanych z nimi właściwości. Pędzle są konwertowane na „świat odradzający się”.
Gdy ładujesz ten poziom, po prostu inicjuje on wszystkie jednostki. Brzmi prosto, prawda?
Teraz w sieciach podmiotów napotykam wiele problemów. Po pierwsze, jakie podmioty powinny istnieć na kliencie od samego początku? Zakładając, że zarówno serwer, jak i klient mają plik poziomu, klient może równie dobrze zaimplementować wszystkie jednostki na poziomie, nawet jeśli są one tylko do celów reguł gry na serwerze.
Inną możliwością jest to, że klient inicjuje jednostkę, gdy tylko serwer wyśle o niej informacje, a to oznacza, że klient będzie miał tylko jednostki, których potrzebuje.
Kolejny problem dotyczy sposobu przesyłania informacji. Myślę, że serwer mógłby użyć kompresji delta, co oznacza, że wysyła nowe informacje tylko wtedy, gdy coś się zmienia, zamiast wysyłać migawkę do klienta w każdej ramce. Oznacza to jednak, że serwer musi śledzić to, co wie każdy klient.
I w końcu, jak należy wstrzykiwać sieci do silnika? Myślę o komponencie NetworkComponent, który jest wstrzykiwany do każdej encji, która ma być połączona w sieć. Ale w jaki sposób składnik sieciowy powinien wiedzieć, jakie zmienne podłączyć do sieci i jak uzyskać do nich dostęp, a na koniec, w jaki sposób odpowiedni składnik sieciowy na kliencie powinien wiedzieć, jak zmienić zmienne sieciowe?
Mam ogromne problemy z podejściem do tego. Byłbym bardzo wdzięczny, gdybyś pomógł mi w drodze. Jestem otwarty na wskazówki, jak ulepszyć projekt systemu komponentów, więc nie bój się tego sugerować.
źródło
Chciałem napisać komentarz, ale zdecydowałem, że może to być wystarczająca informacja do odpowiedzi.
Po pierwsze +1 za tak ładnie napisane pytanie z mnóstwem szczegółów, według których można ocenić odpowiedź.
Do ładowania danych chciałbym, aby klient załadował świat z pliku world. Jeśli twoje byty mają identyfikatory, które pochodzą z pliku danych, załadowałbym je również domyślnie, aby twój system sieciowy mógł się do nich odwoływać, aby wiedzieć, o których obiektach mówi. Każdy ładujący te same dane początkowe powinien oznaczać, że wszystkie mają takie same identyfikatory dla tych obiektów.
Po drugie, nie twórz składnika NetworkComponent, ponieważ nie zrobiłoby to nic innego, jak replikowanie danych w innych istniejących komponentach (fizyka, animacja i tym podobne to niektóre typowe rzeczy do przesłania). Aby użyć własnego nazewnictwa, możesz chcieć stworzyć NetworkComponentManager. Byłoby to nieco inne od innych relacji między komponentami a ComponentManager, ale może to zostać utworzone natychmiast po uruchomieniu gry w sieci, a wszelkie komponenty, które mają aspekt sieciowy, przekazują swoje dane menedżerowi, aby mógł je spakować i wyślij to. W tym miejscu można użyć funkcji Zapisz / Załaduj, jeśli masz jakiś mechanizm serializacji / deserializacji, którego można również użyć do pakowania danych, jak wspomniano,
Biorąc pod uwagę twoje pytanie i poziom informacji, nie sądzę, żebym musiał szczegółowo omawiać szczegóły, ale jeśli coś jest niejasne, napisz komentarz, a ja zaktualizuję odpowiedź, aby rozwiązać ten problem.
Mam nadzieję że to pomoże.
źródło