Wczoraj przeczytałem prezentację GDC Canada na temat systemu encji Attribute / Behavior i myślę, że jest całkiem świetny. Nie jestem jednak pewien, jak z niego korzystać praktycznie, nie tylko teoretycznie. Po pierwsze, szybko wyjaśnię ci, jak działa ten system.
Każda jednostka gry (obiekt gry) składa się z atrybutów (= danych, do których można uzyskać dostęp poprzez zachowania, ale także przez „kod zewnętrzny”) i zachowań (= logika, które zawierają OnUpdate()
i OnMessage()
). Na przykład w klonie Breakout każda cegła składałaby się (przykład!): PositionAttribute , ColorAttribute , HealthAttribute , RenderableBehaviour , HitBehaviour . Ostatni może wyglądać tak (to tylko niedziałający przykład napisany w C #):
void OnMessage(Message m)
{
if (m is CollisionMessage) // CollisionMessage is inherited from Message
{
Entity otherEntity = m.CollidedWith; // Entity CollisionMessage.CollidedWith
if (otherEntity.Type = EntityType.Ball) // Collided with ball
{
int brickHealth = GetAttribute<int>(Attribute.Health); // owner's attribute
brickHealth -= otherEntity.GetAttribute<int>(Attribute.DamageImpact);
SetAttribute<int>(Attribute.Health, brickHealth); // owner's attribute
// If health is <= 0, "destroy" the brick
if (brickHealth <= 0)
SetAttribute<bool>(Attribute.Alive, false);
}
}
else if (m is AttributeChangedMessage) // Some attribute has been changed 'externally'
{
if (m.Attribute == Attribute.Health)
{
// If health is <= 0, "destroy" the brick
if (brickHealth <= 0)
SetAttribute<bool>(Attribute.Alive, false);
}
}
}
Jeśli jesteś zainteresowany tym systemem, możesz przeczytać więcej tutaj (.ppt).
Moje pytanie dotyczy tego systemu, ale ogólnie każdego systemu jednostek opartego na komponentach. Nigdy nie widziałem, jak którekolwiek z nich naprawdę działa w prawdziwych grach komputerowych, ponieważ nie mogę znaleźć dobrych przykładów, a jeśli je znajdę, nie jest to udokumentowane, nie ma komentarzy, więc nie rozumiem tego.
Więc o co chcę zapytać? Jak zaprojektować zachowania (komponenty). Przeczytałem tutaj, na GameDev SE, że najczęstszym błędem jest tworzenie wielu komponentów i po prostu „uczynienie wszystkiego składnikiem”. Czytałem, że sugeruje się, aby nie renderować w komponencie, ale zrobić to poza nim (więc zamiast RenderableBehaviour , może to być RenderableAttribute , a jeśli jednostka ma RenderableAttribute ustawioną na true, to Renderer
(klasa niezwiązana z komponenty, ale do samego silnika) czy należy go narysować na ekranie?).
A co z zachowaniami / komponentami? Powiedzmy, że mam poziom, a na poziomie jest Entity button
, Entity doors
i Entity player
. Kiedy gracz zderza się z przyciskiem (jest to przycisk podłogi, który jest przełączany przez nacisk), zostaje wciśnięty. Naciśnięcie przycisku powoduje otwarcie drzwi. Cóż, teraz jak to zrobić?
Wymyśliłem coś takiego: gracz ma CollisionBehaviour , który sprawdza, czy gracz coś koliduje. Jeśli zderzy się z przyciskiem, wysyła on CollisionMessage
do button
bytu. Wiadomość będzie zawierać wszystkie niezbędne informacje: kto zderzył się z przyciskiem. Przycisk ma ToggleableBehaviour , który otrzyma CollisionMessage
. Sprawdzi, z kim się zderzył, a jeśli waga tego bytu jest wystarczająco duża, aby przełączyć przycisk, przycisk zostanie przełączony. Teraz ustawia ToggledAttribute przycisku na true. W porządku, ale co teraz?
Czy przycisk powinien wysłać kolejną wiadomość do wszystkich innych obiektów z informacją, że został przełączony? Wydaje mi się, że gdybym zrobił wszystko w ten sposób, miałbym tysiące wiadomości i byłoby bardzo bałagan. Może i tak jest lepiej: drzwi stale sprawdzają, czy przycisk, który jest z nimi połączony, jest wciśnięty, czy nie, i odpowiednio zmienia swój OpenedAttribute . Ale to oznacza, że OnUpdate()
metoda drzwi będzie ciągle coś robić (czy to naprawdę problem?).
I drugi problem: co, jeśli mam więcej rodzajów przycisków. Jeden jest naciskany przez nacisk, drugi jest przełączany przez strzelanie do niego, trzeci jest przełączany, jeśli wyleje się na niego wodę itp. Oznacza to, że będę musiał zachowywać się inaczej, coś w tym rodzaju:
Behaviour -> ToggleableBehaviour -> ToggleOnPressureBehaviour
-> ToggleOnShotBehaviour
-> ToggleOnWaterBehaviour
Czy tak działają prawdziwe gry, czy po prostu jestem głupia? Może mógłbym mieć tylko jeden ToggleableBehaviour i będzie się zachowywał zgodnie z ButtonTypeAttribute . Więc jeśli to jest ButtonType.Pressure
, robi to, jeśli to jest ButtonType.Shot
, robi coś innego ...
Więc czego chcę? Chciałbym cię zapytać, czy robię to dobrze, czy po prostu jestem głupi i nie zrozumiałem sensu komponentów. Nie znalazłem żadnego dobrego przykładu tego, jak naprawdę komponenty działają w grach, znalazłem tylko kilka samouczków opisujących, jak zbudować system komponentów, ale nie jak go używać.
źródło
Entity
ma cośEntityBody
, co odciąga wszystkie brzydkie kawałki. Zachowania mogą następnie odczytać pozycję zEntityBody
, zastosować do niej siły, użyć stawów i silników, które ma ciało itp. Posiadanie tak wysokiej wierności symulacji fizyki jak Box2D z pewnością stwarza nowe wyzwania, ale są całkiem zabawne, imo.Component
/System
, do którego kilka razy przywoływano na płytkach. Nasze wdrożenia rzeczywiście mają całkiem sporo podobieństw.