Jaki jest odpowiedni poziom szczegółowości dla architektury opartej na komponentach?

26

Pracuję nad grą z architekturą opartą na komponentach. Jest Entitywłaścicielem zestawu Componentinstancji, z których każda ma zestaw Slotinstancji do przechowywania, wysyłania i odbierania wartości. Funkcje fabryczne, takie jak Playertworzenie elementów z wymaganymi komponentami i połączeniami gniazd.

Próbuję ustalić najlepszy poziom szczegółowości dla komponentów. Na przykład teraz Position, Velocityi Accelerationsą wszystkie oddzielne elementy, połączone szeregowo. Velocityi Accelerationmoże być łatwo przepisany na jednolity Deltakomponent, lub Position, Velocityi Accelerationmoże być łączony obok takich komponentów jak Frictioni Gravityna monolityczny Physicskomponent.

Czy element powinien ponosić jak najmniejszą odpowiedzialność (kosztem wielu połączeń wzajemnych), czy też należy łączyć pokrewne elementy w elementy monolityczne (kosztem elastyczności)? Skłaniam się ku temu pierwszemu, ale przydałaby mi się druga opinia.

Jon Purdy
źródło
Czy zamierzasz użyć własnego silnika fizyki lub zintegrować istniejący?
Den
@Den: Piszę kod fizyki , ale w żadnym wypadku nie jest to silnik . Po prostu przyziemna kinematyka 2D.
Jon Purdy,

Odpowiedzi:

14

Istnieje granica między całkowitą ziarnistością, prowadzącą do braku marnotrawstwa kodu lub stanu podobnego do obiektu blob (dlatego preferowane są architektury komponentów), a użytecznością.

Oczywiście rzeczy mogą mieć Position, ale niekoniecznie są dynamiczne (więc dlaczego Velocityi Acceleration?). Jednak coś z a Velocitybędzie ruchomym przedmiotem, więc sensowne jest także Accelerationpogrupowanie.

Czy będziesz miał przypadek, w którym będą potrzebne v i a , ale nie chcesz dla nich symulacji fizyki? Podobnie, czy będzie sens, Gravityjeśli nie są obiektami fizyki?

tl; dr Group, co ma sens.

Kaczka komunistyczna
źródło
1
Brzmi uczciwie. Mój system ma bardzo małą centralizację: jeśli an Entityma Positionpewne elementy, które sprawiają, że Positionuczestniczą w fizyce, to Entityjest de facto fizyczny. Myślę, że mogę po prostu dodać niektóre komponenty do logicznego grupowania i utrzymać wszystkie podstawowe komponenty w jednej odpowiedzialności. Więc dodając, powiedzmy, Movabledo Entitybędzie mieć taki sam skutek jak dodanie Position, Velocityi Acceleration.
Jon Purdy
6

Aby uniknąć mikrozarządzania każdą zmienną, którą zasugerowałem, zaczynając od ciężkich, wybierz podziały wokół odpowiedzialności i zreformuj, gdy sytuacja przedstawia się logicznie. Możesz rozpocząć pierwsze projekty na papierze. W pewnym momencie te rażące podziały odpowiedzialności staną się budulcem twoich bytów.

Na przykład w pośpiechu masz grę. Gra dzieli się na środowisko + stan. Środowisko dzieli się na StaticWorld + MovingStuff. Stan dzieli się na AIControlled + PlayerControlled. Rażąca idea Obrażeń dzieli się na TakesDamage + GivesDamage.

I tak dalej.

Podobnie jak powszechna rada „Twórz gry, a nie silniki!” dla nowych praktyków polecam „Twórz gry, a nie rozbudowuj systemy!” ponieważ tylko dzięki osobistemu doświadczeniu z uruchomioną grą dowiesz się, czy w przyszłych pracach potrzebny będzie złożony system komponentów.

Patrick Hughes
źródło
Nie jestem nowym programistą. Jeśli mam komponenty oparte raczej na pojęciach niż na zachowaniu (tj. Odgórne kontra oddolne), czy moja architektura nie będzie bardziej krucha i bardziej złożona? Mogę zaimplementować dowolne zachowanie z małych komponentów, ale nie zawsze mogę uzyskać pożądane zachowanie z wcześniej połączonych. Nie wspominając już o tym, że nie mogę przewidzieć wszystkiego, co chcę osiągnąć, nawet w kontekście jednej gry.
Jon Purdy,
Wadą „od dołu do góry” jest to, że komunikacja między komponentami staje się problemem, a ludzie zaczynają być zbyt mikro w skali tego, co modelują. Starałem się głównie unikać super-mikro „xyz to składnik” „rotacja to składnik” „rgb to składnik” dla kogoś niedoświadczonego.
Patrick Hughes,
Słusznie. Chciałem tylko upewnić się, że dobrze cię zrozumiałem. Dzięki systemowi gniazd, który posiadam, komunikacja elementów jest prosta i wydajna, więc naprawdę nie widzę żadnych wad skali „super-mikro”. Nie zamierzam przekształcać tego w samodzielny silnik, ale jeśli to zrobię, zawsze mogę wprowadzić abstrakcje, takie jak grupy komponentów, o których wspomniałem w komentarzu do odpowiedzi Kaczki komunistycznej.
Jon Purdy,
4

Sądzę, że łączę komponenty, które będą miały wiele interakcji. W twoim przypadku utrzymałbym pozycję w jednym składniku, a prędkość i przyspieszenie utrzymywałyby razem w składniku fizyki.

Ale to zależy w dużej mierze od tego, jakie są twoje funkcje gry (chyba że budujesz framework bez konkretnej gry).

XGouchet
źródło
2
Problem z łączeniem komponentów z dużą ilością interakcji polega na tym, że czasami druga część nie jest potrzebna.
Kaczka komunistyczna
4

Zdaję sobie sprawę, że to stare pytanie, ale może ktoś uzna tę odpowiedź za przydatną.

Wierzę, że Systemsto pomoże ci zrozumieć, jak budować Komponenty. Systemy istnieją w architekturach, w których komponenty nie zawierają własnej logiki, oprócz prostych funkcji pobierających / ustawiających i funkcji pomocniczych. Systemy działają na jednostkach, które spełniają wymagania komponentu. Dlatego najlepiej jest oddzielić dane składowe w takim stopniu, w jakim ma to sens, aby dane te były przetwarzane bez innych danych.

Na przykład możesz mieć MovementSystemaktualizację jego encji Positionna podstawie ich Velocity. Może to dotyczyć prostych bytów w grze. Ale dla Gracza i Wrogów możesz chcieć ruchu, Accelerationktóry jest przetwarzany AcceleratedMovementSystem. Ale na przeszkody możesz chcieć Friction. Terenu może mieć również tarcie, ale nie będzie miał składową prędkości. Ale co z Gravity? Po prostu dodaj go Gracz, Wróg i Przeszkoda i stwórz go, GravitySystemaby go przetworzyć.

Najważniejsze jest to, że im bardziej oddzielona swoje Components, tym bardziej rozciągliwyEntities będzie podczas używania Systems.

Edycja: Sprawiono, że ostatnie oświadczenie stało się jaśniejsze.

Edycja: Ok, pracowałem nad własnym systemem i doszedłem do tego. Przykładem podziału pozycji i prędkości jest to, że manipulowanie prędkością powoduje zmianę pozycji za pomocą systemu Movement. W rezultacie „InputMovementSystem” nie potrzebuje pozycji, aby spowodować przejście odtwarzacza z danych wejściowych użytkownika, ponieważ MovementSystem odbierze zmiany prędkości, aby zastosować się do pozycji. To prawda, że ​​nadal byłoby dobrze, gdyby je zebrać, ale to przykład, dlaczego tak nie jest.


Niedawno przeczytałem post na blogu, który powiedział coś takiego:

„Jeśli masz dane, które można grupować z 2 różnymi komponentami, najlepiej utworzyć trzeci komponent z tymi danymi”.

Dan H.
źródło