Ieri ho letto una presentazione di GDC Canada sul sistema di entità Attributo / Comportamento e penso che sia piuttosto eccezionale. Tuttavia, non sono sicuro di come usarlo praticamente, non solo in teoria. Prima di tutto, ti spiegherò rapidamente come funziona questo sistema.
Ogni entità di gioco (oggetto di gioco) è composta da attributi (= dati, a cui è possibile accedere da comportamenti, ma anche da "codice esterno") e comportamenti (= logica, che contengono OnUpdate()
e OnMessage()
). Quindi, ad esempio, in un clone Breakout, ogni mattone sarebbe composto da (esempio!): PositionAttribute , ColorAttribute , HealthAttribute , RenderableBehaviour , HitBehaviour . L'ultimo potrebbe assomigliare a questo (è solo un esempio non funzionante scritto in 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);
}
}
}
Se sei interessato a questo sistema, puoi leggere di più qui (.ppt).
La mia domanda è relativa a questo sistema, ma generalmente a tutti i sistemi di entità basati su componenti. Non ho mai visto come uno di questi funzioni davvero nei giochi per computer reali, perché non riesco a trovare buoni esempi e se ne trovo uno, non è documentato, non ci sono commenti e quindi non lo capisco.
Allora, cosa voglio chiedere? Come progettare i comportamenti (componenti). Ho letto qui, su GameDev SE, che l'errore più comune è quello di creare molti componenti e semplicemente "rendere tutto un componente". Ho letto che si suggerisce di non eseguire il rendering in un componente, ma di farlo al di fuori di esso (quindi invece di RenderableBehaviour , dovrebbe forse essere RenderableAttribute e se un'entità ha RenderableAttribute impostato su true, allora Renderer
(classe non correlata a componenti, ma per il motore stesso) dovrebbe disegnarlo sullo schermo?).
Ma che dire dei comportamenti / componenti? Diciamo di lasciare che ho un piano, e nel livello, c'è un Entity button
, Entity doors
e Entity player
. Quando il giocatore si scontra con il pulsante (è un pulsante sul pavimento, che viene attivato dalla pressione), viene premuto. Quando il pulsante viene premuto, apre le porte. Bene, ora come si fa?
Ho escogitato qualcosa del genere: il giocatore ha CollisionBehaviour , che controlla se il giocatore si scontra con qualcosa. Se si scontra con un pulsante, invia CollisionMessage
a button
all'entità. Il messaggio conterrà tutte le informazioni necessarie: chi si è scontrato con il pulsante. Il pulsante ha ToggleableBehaviour , che riceverà CollisionMessage
. Controllerà con chi si è scontrato e se il peso di quell'entità è abbastanza grande da attivare il pulsante, il pulsante viene attivato. Ora, imposta il ToggledAttribute del pulsante su true. Va bene, ma adesso?
Il pulsante dovrebbe inviare un altro messaggio a tutti gli altri oggetti per dire che è stato attivato? Penso che se facessi tutto così, avrei migliaia di messaggi e diventerebbe piuttosto confuso. Quindi forse questo è meglio: le porte controllano costantemente se il pulsante che è collegato a loro è premuto o meno e cambia il suo OpenedAttribute di conseguenza. Ma allora significa che il OnUpdate()
metodo delle porte farà costantemente qualcosa (è davvero un problema?).
E il secondo problema: cosa succede se ho più tipi di pulsanti. Uno viene premuto dalla pressione, il secondo viene attivato sparando su di esso, il terzo viene attivato se si versa acqua su di esso, ecc. Ciò significa che dovrò avere comportamenti diversi, qualcosa del genere:
Behaviour -> ToggleableBehaviour -> ToggleOnPressureBehaviour
-> ToggleOnShotBehaviour
-> ToggleOnWaterBehaviour
È così che funzionano i giochi reali o sono solo stupido? Forse potrei avere solo un comportamento commutabile e si comporterà secondo l' attributo ButtonType . Quindi se è un ButtonType.Pressure
, lo fa, se è un ButtonType.Shot
, fa qualcos'altro ...
Quindi cosa voglio? Vorrei chiederti se lo sto facendo bene, o sono solo stupido e non ho capito il punto dei componenti. Non ho trovato alcun buon esempio di come funzionano realmente i componenti nei giochi, ho trovato solo alcuni tutorial che descrivono come realizzare il sistema dei componenti, ma non come usarlo.