Wiele źródeł ruchu w systemie encji

9

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ąś MoveByklasę, która zawiera odległość / czas i tablicę tych w mojej klasie obiektów gry. Każdą klatkę, iterowałbym wszystkie MoveByi zastosowałem ją do pozycji. Jeśli a MoveByosią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

Entityktóry ma zarówno a PositionComponenti aMoveByComponent

MoveBySystemktóry wyszukuje byt zawierający oba te składniki i dodaje wartość MoveByComponentdo PositionComponent. Po timeosią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, MoveByComponenttak samo jak powyżej

MoveByCollectionComponentktóry zawiera tablicę MoveByComponents

MoveByCollectionSystemktóry szuka bytu oznaczonego „a” PositionComponenti „ MoveByCollectionComponentiterującego” MoveByComponentwewną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ę.

  1. Czy to właściwy sposób, aby na to patrzeć?

  2. Czy jednostka powinna mieć zawsze tylko jeden składnik danego typu?

Lepki
źródło
1
Brzmi trochę tak, jakby MoveByfunkcjonalność 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.
MichaelHouse
W pewnym sensie, ale różnica polega na tym, że ta prędkość jest ważna tylko od czasu do czasu i wiele z nich można połączyć razem. Myślę, że po prostu potrzebowałem pewności, byłem surowy (analny, prawie) OO dla moich gier w przeszłości - które lata później przy tym samym projekcie sparaliżowały naszą szybkość produkcji - i to jest przerażająco nieznane terytorium;) . Nawiasem mówiąc, świetna odpowiedź na inny post, pomogła wyjaśnić niektóre rzeczy
Przyklejony
Robię to w ten sposób: mam PlayerInputComponent i AIInputComponent (lub systemy), które powie MobileBehaviorComponent, że po kliknięciu na klawiaturze lub w AI, myśląc, że telefon powinien się gdzieś przenieść, MobileBehaviorComponent zapisze, że powinien się gdzieś przenieść (ma FSM wewnątrz do akcji mobilnych) i jakiś system go przeniesie. Twoja ziarnistość jest po prostu za duża, z komponentami wyższego poziomu, takimi jak Transform, Model, Światło, Mob, wszystko działa równie dobrze. Nigdy też nie musiałem usuwać komponentów - myślę o nich bardziej jak o opisie przedmiotu gry, więc nie może po prostu zniknąć.
Kikaimaru,
Ten konkretny przykład MoveBy był tylko przykładem. Pytanie dotyczyło raczej sposobu, w jaki łączysz takie rzeczy. Jeśli muszę konkretnie powiedzieć „przesuń o x = 5 iy = 6 za 5 sekund”, przesuń o x = 10 y = 2 za 10 sekund, to czy tak właśnie to zrobiłbym?
Przyklejony
Co rozumiesz przez „złożony”? Lubisz dodawać prędkości? Więc jeśli się spotkałeś, move x by 10 in 2 secondsa move x by -10 in 2 secondsistota stałaby idealnie nieruchomo?
Tom Dalling

Odpowiedzi:

6

W twoim scenariuszu zazwyczaj dodajemy trzy elementy do obiektu gry:

  1. TransformComponent (pozycja, orientacja, skala)
  2. VelocityComponent (prędkość, kierunek)
  3. ControllerComponent

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:

BehaviorTree* tree(new SequentialNode());
tree->addChild(new MoveToNode(x,y,z));
tree->addChild(new WaitNode(30));
tree->addChild(new MoveToNode(a,b,c));
tree->addChild(new WaitNode(30));
gameObject->addComponent(new AIController(tree));

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.

Naros
źródło
1

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.

Hatoru Hansou
źródło
Jest to w zasadzie projekt OO, który już mamy. Entity, z pochodnymi (Postać, Potwór) itp., Kieruję zespołem z nas, który pracuje nad tą grą przez prawie 2 lata, a kiedy wszyscy zmieniają wszystko, staje się to okropne, okropne baza kodów - zaczyna dostarczać nowe funkcje w żenująco długim czasie. Pomysł Entity System wydaje się być dokładnie tym , czego szukam, więc chociaż twoja odpowiedź nie jest dość istotna, powinieneś sam przeczytać linki na górze pytania, aby sprawdzić, czy mogą ci pomóc :)
Przyklejony
@Sticky Muszę przyznać, że Node System plus Entity wykonany ze składników to sprytny sposób reprezentowania różnych potrzebnych systemów niż moje sugerowane podejście kontrolerów, które jest jak wersja mniej rozwinięta. W końcu naprawdę nie potrzebujesz mojej odpowiedzi.
Hatoru Hansou,
Bez obaw. Sposób OO ma swoje zalety, ale sprawy stają się brzydkie, szybkie
Przyklejony