Ontem, li uma apresentação da GDC Canadá sobre o sistema de entidades de Atributos / Comportamentos e acho ótimo. No entanto, não tenho certeza de como usá-lo na prática, não apenas na teoria. Primeiramente, explicarei rapidamente como esse sistema funciona.
Cada entidade do jogo (objeto do jogo) é composta de atributos (= dados, que podem ser acessados por comportamentos, mas também por 'código externo') e comportamentos (= lógica, que contêm OnUpdate()
e OnMessage()
). Assim, por exemplo, em um clone Breakout, cada tijolo seria composto por (exemplo!): PositionAttribute , ColorAttribute , HealthAttribute , RenderableBehaviour , HitBehaviour . O último poderia ter esta aparência (é apenas um exemplo não útil escrito em 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 você está interessado neste sistema, pode ler mais aqui (.ppt).
Minha pergunta está relacionada a esse sistema, mas geralmente todos os sistemas de entidades baseados em componentes. Eu nunca vi como isso realmente funciona em jogos de computador reais, porque não consigo encontrar bons exemplos e, se o encontrar, não está documentado, não há comentários e, portanto, não o entendo.
Então, o que eu quero perguntar? Como projetar os comportamentos (componentes). Eu li aqui, no GameDev SE, que o erro mais comum é criar muitos componentes e simplesmente "transformar tudo em um componente". Eu li que é sugerido não fazer a renderização em um componente, mas fazê-lo fora dele (portanto, em vez de RenderableBehaviour , talvez seja RenderableAttribute , e se uma entidade tiver RenderableAttribute definido como true, então Renderer
(classe não relacionada a componentes, mas para o próprio mecanismo) deve desenhá-lo na tela?).
Mas e os comportamentos / componentes? Vamos dizer que eu tenho um nível, e no nível, há uma Entity button
, Entity doors
e Entity player
. Quando o jogador colide com o botão (é um botão do piso, que é alternado pela pressão), ele é pressionado. Quando o botão é pressionado, ele abre as portas. Bem, agora como fazer isso?
Eu vim com algo assim: o jogador tem o CollisionBehaviour , que verifica se o jogador colide com alguma coisa. Se ele colidir com um botão, ele envia um CollisionMessage
para a button
entidade. A mensagem conterá todas as informações necessárias: quem colidiu com o botão. O botão possui ToggleableBehaviour , que receberá CollisionMessage
. Ele verificará com quem colidiu e se o peso dessa entidade for grande o suficiente para alternar o botão, o botão será alternado. Agora, ele define o ToggledAttribute do botão como true. Tudo bem, mas e agora?
O botão deve enviar outra mensagem a todos os outros objetos para informar que foi alternado? Eu acho que se eu fizesse tudo assim, eu teria milhares de mensagens e ficaria bem confuso. Então, talvez isso seja melhor: as portas verificam constantemente se o botão que está vinculado a elas está pressionado ou não, e altera seu OpenedAttribute de acordo. Mas então isso significa que o OnUpdate()
método das portas estará constantemente fazendo algo (é realmente um problema?).
E o segundo problema: e se eu tiver mais tipos de botões. Um é pressionado pela pressão, o segundo é alternado ao atirar nele, o terceiro é alternado se a água for derramada sobre ele etc. Isso significa que terei que ter comportamentos diferentes, algo como isto:
Behaviour -> ToggleableBehaviour -> ToggleOnPressureBehaviour
-> ToggleOnShotBehaviour
-> ToggleOnWaterBehaviour
É assim que os jogos reais funcionam ou eu sou apenas estúpido? Talvez eu possa ter apenas um ToggleableBehaviour e ele se comportará de acordo com o ButtonTypeAttribute . Então, se é um ButtonType.Pressure
, faz isso, se é um ButtonType.Shot
, faz outra coisa ...
Então o que eu quero? Eu gostaria de perguntar se estou fazendo certo, ou sou apenas estúpido e não entendi o ponto dos componentes. Não encontrei nenhum bom exemplo de como os componentes realmente funcionam nos jogos, encontrei apenas alguns tutoriais que descrevem como criar o sistema de componentes, mas não como usá-lo.