Jestem całkiem nowy w koncepcji systemów encji, po przeczytaniu wielu rzeczy (najbardziej przydatne, ten świetny blog i ta odpowiedź ).
Chociaż mam mały problem ze zrozumieniem, jak coś tak prostego, jak możliwość manipulowania pozycją obiektu przez nieokreśloną liczbę źródeł.
To znaczy, mam swój byt, który ma element pozycji. Mam wtedy jakieś wydarzenie w grze, które każe temu bytowi przemieścić się na określoną odległość w określonym czasie.
Te zdarzenia mogą się zdarzyć w dowolnym momencie i będą miały różne wartości dla pozycji i czasu. W rezultacie zostaną one złożone razem.
W tradycyjnym rozwiązaniu OO miałbym jakąś MoveBy
klasę, która zawiera odległość / czas i tablicę tych w mojej klasie obiektów gry. Każdą klatkę, iterowałbym wszystkie MoveBy
i zastosowałem ją do pozycji. Jeśli a MoveBy
osiągnął swój czas zakończenia, usuń go z tablicy.
W przypadku systemu encji jestem trochę zdezorientowany, jak powinienem replikować tego rodzaju zachowanie.
Gdyby istniał tylko jeden z nich na raz, zamiast być w stanie je połączyć, byłoby to dość proste (tak mi się wydaje) i wyglądałoby mniej więcej tak:
PositionComponent
zawierający x, y
MoveByComponent
zawierający x, y, time
Entity
który ma zarówno a PositionComponent
i aMoveByComponent
MoveBySystem
który wyszukuje byt zawierający oba te składniki i dodaje wartość MoveByComponent
do PositionComponent
. Po time
osiągnięciu usuwa komponent z tego elementu.
Jestem trochę zdezorientowany co do tego, jak zrobiłbym to samo z wieloma ruchami.
Moje początkowe myśli są następujące:
PositionComponent
, MoveByComponent
tak samo jak powyżej
MoveByCollectionComponent
który zawiera tablicę MoveByComponent
s
MoveByCollectionSystem
który szuka bytu oznaczonego „a” PositionComponent
i „ MoveByCollectionComponent
iterującego” MoveByComponent
wewnątrz niego, stosującego / usuwającego w razie potrzeby.
Wydaje mi się, że jest to bardziej ogólny problem polegający na tym, że wiele z tego samego komponentu jest pożądany, a każdy z nich chce mieć odpowiedni system. Moje byty zawierają swoje komponenty w haszu typu komponentu -> komponent, więc ściśle mają tylko 1 komponent określonego typu na jednostkę.
Czy to właściwy sposób, aby na to patrzeć?
Czy jednostka powinna mieć zawsze tylko jeden składnik danego typu?
MoveBy
funkcjonalność była tylko prędkością? Brzmi, jakbyś był na dobrej drodze. W przypadku drugiego pytania istnieje wiele różnych implementacji systemów encji / komponentów. Ten opisany w mojej odpowiedzi, do której linkowałeś, miałby tylko jeden element danego typu.move x by 10 in 2 seconds
amove x by -10 in 2 seconds
istota stałaby idealnie nieruchomo?Odpowiedzi:
W twoim scenariuszu zazwyczaj dodajemy trzy elementy do obiektu gry:
Kiedy obiekty gry wymagają pewnego rodzaju funkcji AI, takich jak poruszanie się po ścieżce, jak opisano, przypisujemy kontroler AIC do jego listy komponentów. Kontrolery AIC są tak naprawdę niczym więcej niż opakowaniem, które kroczy drzewkiem behawioralnym. Drzewo zachowań to miejsce, w którym projektujemy rzeczywistą funkcjonalność, którą chcemy, aby obiekt gry działał, na przykład:
Podsystem AI zarządza kontrolerami AIC, dzięki czemu podsystem zaznacza kontroler, który z kolei przesuwa drzewo zachowań. MoveToNode () sprawdza bieżącą pozycję / orientację, oblicza wektor kierunku i prędkość do miejsca, w którym chcesz się przenieść, na podstawie argumentów konstruktora i ustawia wartości na komponencie prędkości. System ruchu odpowiada za odczytywanie komponentów ruchu z wartościami i stosowanie fizyki, odpowiednio aktualizując pozycję / orientację.
Powyższy kod po prostu przenosi obiekt gry z miejsca odrodzenia do x, y, z w przestrzeni świata, a następnie czeka co najmniej 30 sekund, a następnie przenosi obiekt gry do lokalizacji a, b, c, a następnie czeka kolejne 30 sekund. Po zakończeniu oczekiwania sekwencja zachowań zakończyła się, więc powtarza się od początku.
Pozwala to łatwo zdefiniować dowolną funkcjonalność sztucznej inteligencji, która jest potrzebna w podsystemie sztucznej inteligencji, przy minimalnym wpływie na podsystem Entity. Pozwala także zachować wąską listę komponentów systemu encji bez zbyt dużej szczegółowości.
źródło
Opcją jest dodanie kontrolerów do projektu. Jednostki posiadają dane do reprezentowania pozycji (w przypadku mojego silnika mają dane, które pamiętają również poprzednie pozycje, więc mogę poznać wektor prędkości i czy są one przenoszone lub teleportowane), ale nie wiedzą nic o fizyce ani AI. Kontrolery przenoszą byty i możesz mieć wielu kontrolerów wpływających na ten sam byt lub jednego kontrolera wpływającego na różne byty.
Na przykład: utwórz podstawową klasę Controller za pomocą metody run () lub jeśli nie podoba ci się nazwa, wywołaj think (), update () lub tick (). Następnie dziedziczysz po nim i tworzysz MoveController, NPCController, PlayerInputController (dla encji gracza), PhysicController; następnie implementujesz metodę run (). Umieściłbym twój MoveByComponent w MoveController, a nie w Entity.
Kontrolery te mogą być tworzone przez każdy Podmiot, jeśli przechowują dane specyficzne dla Podmiotu. Można je zniszczyć lub zresetować w celu ponownego użycia. Możesz również użyć kontrolera do przeniesienia grupy jednostek, na przykład w grze RTE, jeśli chcesz przenieść różne jednostki jako grupę, kontroler każdej jednostki może zaszkodzić wydajności gry, możesz po prostu przypisać wszystkie jednostki do GroupController lub LegionController i pozwól mu przenosić jednostki w ramach zorganizowanej grupy. Podczas walki, jeśli gra pozwala na zachowanie poszczególnych jednostek i prawdopodobnie większość gier tak, musisz przełączyć się na kontroler jednostek, ale lepiej jest to robić tylko wtedy, gdy jest to potrzebne, niż od samego początku.
W mojej rozwijającej się grze mam MoveController, który porusza byty podążające ścieżką, jeden MoveController istnieje dla każdego NPC i postaci gracza. Czasami tworzona jest jedna dla pudeł lub kamieni, którą gracz może popchnąć. PhysicController, tylko jedna instancja, która sprawdzi pozycje wszystkich przypisanych do niej jednostek, jeśli jakaś jednostka koliduje z inną przypisaną jednostką, obliczana jest wynikowa pozycja obu (faktycznie robi to więcej, ale o tym wiesz). NPCController to AI, jedna instancja na NPC. Sprawdza sytuację NPC i decyduje, gdzie się przenieść, a następnie przesuwa ścieżkę do MoveController, który faktycznie przenosi NPC. Kontrolery mają priorytet, więc mogę z góry ustalić ich kolejność, PhysicController jest ostatnim do wykonania.
Opowiadam się za kontrolerami, ale nie jest to jedyna „poprawna” opcja. Na przykład pamiętam interfejs Entity w silniku Cafu, który ma metodę think () w samym Entity, użytkownik klasy musi dziedziczyć po Entity i implementować think (), pamiętam klasę pochodną o nazwie CompanyBot (która pochodzi z przykładu gry), które sprawdzają kolizję w tej metodzie, ponieważ nazywa się to „myśl”, możemy założyć, że kod AI również tam będzie. Podczas gdy silnik NeoAxis (ostatni raz w to zaglądałem) ma AI i fizykę oddzielone od bytów.
Istnieje wzorzec kontrolera, którego słuchałem. Może powinieneś go poszukać, a to chyba nie jest dokładnie to, o czym tutaj mówię, ale brzmi to również dobre rozwiązanie.
źródło