Sistema de entidade componente - Atualizações e ordens de chamada


10

Para que os componentes possam atualizar todos os quadros (e deixar essa funcionalidade fora dos componentes que não precisam), tive a ideia de criar um componente UpdateComponent. Outros componentes como MovableComponent(que mantém a velocidade) herdariam da IUpdatableclasse abstrata. Isso força MovableComponenta implementar um Update(gametime dt)método e outro RegisterWithUpdater()que fornece UpdateComponentum ponteiro para o MovableComponent. Muitos componentes poderiam fazer isso e, em seguida, UpdateComponentpoderiam chamar todos os seus Update(gametime dt)métodos sem precisar se preocupar com quem ou o que são.

Minhas perguntas são:

  1. Parece algo normal ou usado por alguém? Não consigo encontrar nada sobre o assunto.
  2. Como eu poderia manter uma ordem para os componentes como física e depois mudar de posição? Isso é mesmo necessário?
  3. Quais são outras maneiras de garantir que os componentes que devem ser processados ​​em cada quadro sejam realmente processados?

EDIT
Acho que vou considerar como fornecer ao gerente da entidade uma lista dos tipos que são atualizáveis. Todos os componentes desse tipo podem ser atualizados em vez de gerenciá-los por entidade (que são apenas índices no meu sistema).

Ainda. Minhas perguntas permanecem válidas para mim. Não sei se isso é razoável / normal ou o que os outros tendem a fazer.

Além disso, o pessoal da Insomniac é incrível! /EDITAR

Código resumido para o exemplo anterior:

class IUpdatable
{
public:
    virtual void Update(float dt) = 0;
protected:
    virtual void RegisterAsUpdatable() = 0;
};

class Component
{
    ...
};

class MovableComponent: public Component, public IUpdatable
{
public:
    ...
    virtual void Update(float dt);
private:
    ...
    virtual void RegisterWithUpdater();
};

class UpdateComponent: public Component
{
public:
    ...
    void UpdateAll();
    void RegisterUpdatable(Component* component);
    void RemoveUpdatable(Component* component);
private:
    ...
    std::set<Component*> updatables_;
};

Você tem um exemplo para um componente que não é atualizável? Isso parece bastante inútil. Coloque a função de atualização como uma função virtual no componente. Em seguida, basta atualizar todos os componentes na entidade, não há necessidade para o "UpdateComponent"
Maik Semder

Alguns componentes são mais como titulares de dados. Como PositionComponent. Muitos objetos podem ter uma posição, mas se estiverem estacionários, o que poderia ser a maioria, todas essas chamadas virtuais extras poderão se acumular. Ou diga um HealthComponent. Isso não precisa fazer nada a cada quadro, apenas modifique-o quando necessário. Talvez a sobrecarga não seja tão ruim, mas meu UpdateComponent foi uma tentativa de não ter Update () em TODOS os componentes.
Ptpaterson 17/08/11

5
Um componente que contém apenas dados e nenhuma funcionalidade para alterá-los ao longo do tempo não é, por definição, um componente. Deve haver alguma funcionalidade no componente que mude ao longo do tempo, portanto, precisa de uma função de atualização. Se o seu componente não precisar de uma função de atualização, você não o saberá e deverá repensar o design. Uma função de atualização no componente de saúde faz sentido; por exemplo, você deseja que seu NPC se recupere de danos por algum tempo.
Maik Semder

@Maik Meu argumento não era que são componentes que nunca / nunca poderão mudar. Eu concordo que isso é bobagem. Eles simplesmente não precisam atualizar todos os quadros, mas devem ser notificados para alterar suas informações quando necessário. No caso da saúde ao longo do tempo, haveria um componente de bônus de saúde que possui uma atualização. Eu não acho que haja qualquer razão para combinar os dois.
Ptpaterson 17/08/11

Gostaria também de observar que o código que publiquei tem apenas o necessário para explicar o conceito UpdateComponent. Exclui todas as outras formas de comunicação entre componentes.
Ptpaterson 17/08/11

Respostas:


16

Um dos principais benefícios de um sistema de componentes é a capacidade de tirar proveito dos padrões de armazenamento em cache - bom icache e previsão porque você executa o mesmo código repetidamente, bom dcache porque pode alocar os objetos em conjuntos homogêneos e porque as tabelas v, se qualquer, fique quente.

Da maneira como você estruturou seus componentes, essa vantagem desaparece completamente e, de fato, pode se tornar um risco de desempenho em comparação com um sistema baseado em herança, à medida que você faz muito mais chamadas virtuais e mais objetos com vtables.

O que você deve fazer é armazenar pools por tipo e iterar cada tipo independentemente para executar atualizações.

Parece algo normal ou usado por alguém? Não consigo encontrar nada sobre o assunto.

Não é tão comum em jogos grandes, porque não é vantajoso. É comum em muitos jogos, mas não é tecnicamente interessante, por isso ninguém escreve sobre isso.

Como eu poderia manter uma ordem para os componentes como física e depois mudar de posição? Isso é mesmo necessário?

Código em linguagens como C ++ tem uma maneira natural de ordenar a execução: digite as instruções nessa ordem.

for (PhysicsComponent *c : physics_components)
    c->update(dt);
for (PositionComponent *c : position_components)
    c->update(dt);

Na realidade, isso não faz sentido, porque nenhum sistema físico robusto está estruturado dessa maneira - você não pode atualizar um único objeto de física. Em vez disso, o código seria mais parecido com:

physics_step();
for (PositionComponent *c : position_components)
    c->update(dt);
// Updates the position data from the physics data.

Obrigado. Comecei todo esse projeto com o objetivo de orientar os dados (não os mesmos que os orientados por dados), evitando erros de cache e outros. Mas uma vez que comecei a codificar, decidi criar algo que funcionasse pontocom. Acontece que isso tornou as coisas mais difíceis para mim. E, falando sério, esse artigo sobre insones foi ótimo!
Ptpaterson 17/08/11

@ Joe +1 para uma resposta muito boa. Embora eu gostaria de saber o que são icache e dcache. Graças
Ray Dey

Cache de instruções e cache de dados. Qualquer texto introdutório sobre arquitetura de computadores deve cobri-los.

@ptpaterson: Observe que há um pequeno erro na apresentação do Insomniac - a classe de componente deve conter seu índice de lista, ou você precisa de um mapeamento separado de índices de pool para índices de lista.

3

O que você está falando é razoável e bastante comum, eu acho. Isso pode lhe dar mais algumas informações.


Acho que me deparei com esse artigo algumas semanas atrás. Consegui codificar algumas versões muito simples de um sistema de entidade baseado em componentes, cada uma muito diferente à medida que tento coisas diferentes. Tentando reunir todos os artigos e exemplos em algo que eu quero e sou capaz de fazer. obrigado!
ptpaterson

0

Eu gosto dessas abordagens:

Resumidamente: Evite manter o comportamento de atualização nos componentes. Componentes não são comportamento. O comportamento (incluindo atualizações) pode ser implementado em alguns subsistemas de instância única. Essa abordagem também pode ajudar a processar em lote comportamentos semelhantes para vários componentes (talvez usando instruções parallel_for ou SIMD nos dados dos componentes).

A ideia IUpdatable e o método Update (gametime dt) parecem um pouco restritivos demais e apresentam diferenças adicionais. Pode ser bom se você não estiver usando a abordagem de subsistemas, mas se você os usar, IUpdatable é um nível de hierarquia redundante. Afinal, o MovingSystem deve saber que deve atualizar os componentes Location e / ou Velocity diretamente para todas as entidades que possuem esses componentes, portanto, não há necessidade de algum componente IUpdatable intermediário.

Mas você pode usar o componente Atualizável como um mecanismo para ignorar a atualização de algumas entidades, apesar delas possuírem componentes Localização e / ou Velocidade. O componente Updatable pode ter um sinalizador bool que pode ser definido como falsee que sinalizaria para cada subsistema compatível com Updatable que essa entidade específica atualmente não deveria ser atualizada (embora, nesse contexto, Freezable pareça ser o nome mais apropriado para o componente).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.