Wiem, że to pytanie zostało zadane kilka razy, ale nadal nie jestem pewien, jak zaimplementować obsługę danych wejściowych w silniku opartym na komponentach.
Projekt oparty na komponentach, którego użyłem, był oparty na serii blogów T = Machine i na Artemis, w których jednostki to tylko identyfikatory.
Istnieją trzy główne pomysły, które mam przy wdrażaniu obsługi danych wejściowych:
- Komponent wejściowy przechowuje zdarzenia, które są zainteresowane. System wejściowy przełoży zdarzenia kluczowe i myszy na zdarzenia gry i będzie przechodził przez jednostki z komponentem wejściowym, a jeśli są one zainteresowane zdarzeniem, system wejściowy podejmie odpowiednie działania. Ta akcja byłaby zakodowana na stałe w systemie wejściowym.
- Brak elementu wejściowego. Zarejestrowałbyś podmioty z określonymi zdarzeniami w systemie wejściowym. System wejściowy wysyła następnie wiadomości (z identyfikatorem encji i typem zdarzenia) do innych systemów, aby mogły one podjąć odpowiednie działanie. Lub jak w pierwszym przypadku, działania byłyby zakodowane na stałe w systemie wejściowym.
- Podobnie jak w pierwszej metodzie, ale zamiast na stałe kodować akcję do systemu wejściowego, komponent zawierałby mapę zdarzeń do funkcji (tj.
std::map<std::function>
), Które byłyby wywoływane przez system wejściowy. Daje to dodatkowy efekt polegający na powiązaniu tego samego zdarzenia z różnymi działaniami.
Czy poleciłbyś którąś z powyższych metod, czy masz jakieś sugestie, które pomogłyby mi wdrożyć elastyczny system obsługi danych wejściowych? Ponadto nie jestem jeszcze zaznajomiony z wielowątkowością, ale wszelkie sugestie, które uczynią implementację przyjazną dla wątków, są również mile widziane.
Uwaga: Jednym z dodatkowych wymagań, które chciałbym, aby implementacja była spełniona, jest to, że będę w stanie przekazać te same dane wejściowe do wielu jednostek, takich jak na przykład przeniesienie jednostki kamery i odtwarzacza w tym samym czasie.
źródło
Odpowiedzi:
Myślę, że podobnie jak moja odpowiedź dotycząca materiałów w systemie komponentów , napotykasz problem polegający na tym, że próbujesz wszystko przełożyć na „komponent”. Nie musisz tego robić, dlatego prawdopodobnie tworzysz naprawdę niewygodny interfejs, próbując dopasować kilka kwadratowych kołków do okrągłych otworów.
Wygląda na to, że masz już system, który obsługuje pozyskiwanie danych wejściowych z odtwarzacza. Zdecydowałbym się na podejście, które następnie przekłada te dane wejściowe na działania („przejdź do przodu” lub „wstecz”) lub zdarzenia i przekazuje je zainteresowanym stronom. W przeszłości nie pozwalałem komponentom rejestrować się na te zdarzenia, wolałem podejście, w którym system wyższego poziomu wyraźnie wybrał „kontrolowaną jednostkę”. Ale może to działać w inny sposób, jeśli wolisz, zwłaszcza jeśli zamierzasz ponownie użyć tych samych komunikatów do podjęcia działań, które nie zostały bezpośrednio pobudzone przez dane wejściowe.
Jednak niekoniecznie sugerowałbym wdrożenie zachowania polegającego na podążaniu za kamerą, ponieważ zarówno jednostka kamery, jak i jednostka gracza reagują na komunikat „posunąć się do przodu” (i tak dalej). Tworzy to niezwykle sztywne połączenie między dwoma obiektami, które prawdopodobnie nie będą się dobrze czuć dla gracza, a także sprawia, że nieco trudniej jest poradzić sobie z takimi rzeczami, jak kamera na orbicie odtwarzacza, gdy gracz obraca się w lewo lub w prawo: masz byt reagowanie na „obrót w lewo”, zakładając, że jest on niewolnikiem gracza, ale to oznacza, że nie może poprawnie odpowiedzieć, jeśli kiedykolwiek był niewolnikiem ... chyba że wprowadzisz tę koncepcję jako pewien stan, który możesz sprawdzić. A jeśli masz zamiar to zrobić, równie dobrze możesz wdrożyć odpowiedni system do niewolnictwa dwóch fizycznych obiektów razem, wraz z odpowiednimi modyfikacjami elastyczności i tak dalej.
Jeśli chodzi o wielowątkowość, tak naprawdę nie widzę potrzeby stosowania jej tutaj, ponieważ prawdopodobnie spowodowałoby to więcej komplikacji niż jest to warte, a masz do czynienia z nieodłącznym problemem szeregowym, więc musisz po prostu zaangażować dużo wątku operacje podstawowe synchronizacji.
źródło
Moje doświadczenie może być stronnicze, ale w projektach wieloplatformowych urządzenia wejściowe nie są bezpośrednio narażone na system encji.
Urządzenia wejściowe są obsługiwane przez system niższego poziomu, który odbiera zdarzenia z klawiszy, przycisków, osi, myszy, powierzchni dotykowych, akcelerometrów ...
Te zdarzenia są następnie wysyłane przez warstwę zależnych od kontekstu generatorów intencji.
Każdy generator rejestruje zmiany stanu z komponentów, jednostek i systemów, które są istotne dla jego funkcji.
Generatory te wysyłają następnie wiadomości / zamiary dotyczące trasowania do systemu intencji, w którym podmioty mają komponent lub bezpośrednio do odpowiednich komponentów.
W ten sposób możesz po prostu polegać na tym, że „zawsze” ma to samo wejście, tj. JUMP_INTENT (1), JUMP_INTENT (0), AIM_INTENT (1) ...
I „cała” praca związana z brudną platformą pozostaje poza systemem twojego bytu.
Jeśli chodzi o kamerę, jeśli chcesz ją przenosić wokół odtwarzacza, może zarejestrować własny komponent zamiaru i słuchać zamiarów, które wyślesz.
W przeciwnym razie, jeśli podąża za odtwarzaczem, nigdy nie powinien słuchać sygnałów przeznaczonych dla odtwarzacza. Powinien słuchać zmian stanu emitowanych przez odtwarzacz (ENTITY_MOVED (transform)) ... i odpowiednio się poruszać. Jeśli korzystasz z systemu fizyki, możesz nawet podłączyć kamerę do odtwarzacza za pomocą jednego z różnych złączy.
źródło
Jakie korzyści przynosi InputComponent? Z pewnością do komendy wejściowej należy decyzja, na jakich jednostkach wykonuje akcję. Klasycznym przykładem jest zmuszanie gracza do skoku. Zamiast mieć InputComponent na każdej encji, która nasłuchuje zdarzeń „Jump”, dlaczego nie polecić komendzie skoku odszukać encję oznaczoną jako „player” i wykonać samą niezbędną logikę?
Kolejny przykład z PO:
źródło