Maneira adequada de lidar com a destruição de entidades do jogo

10

Imagine um mundo de jogo em que cargas e cargas de entidades sejam carregadas dinamicamente o tempo todo, eu representaria isso como uma lista de entidades, talvez, mas e quanto a removê-las?

Enquanto, ao adicionar, eu poderia estar adiando a nova entidade, eu poderia ter a necessidade de remover qualquer lugar no contêiner. Para evitar procurar no elemento para encontrar sua posição para remoção, que opções tenho?

Pensei em poder armazenar o ID da entidade como sendo sua posição no contêiner, criando um caminho para remoção direta, mas isso não geraria algum tipo de 'desordem' de dependência mútua?

Eu sei que o caminho certo seria algo como List.RemoveAt (whereToRemove); mas e se apenas a entidade souber quando deve morrer?

Ou estou apenas faltando alguma coisa e um contêiner de lista saberia quando um objeto é destruído e reduziria seu próprio tamanho?

Grimshaw
fonte

Respostas:

10

Aqui estão as suas condições:

  • Outros objetos ainda podem depender da sua entidade removida, depois de removida.

  • Você deseja que apenas a entidade especifique sua própria remoção.

Você não pode ter os dois. Por quê? Como o código em um nível superior ao da sua entidade (veja exemplos abaixo) decide quando essa entidade precisa ser usada. Consequentemente, apenas o código no mesmo nível pode determinar se sua entidade está apta para remoção ou não.

No entanto , o que pode acontecer é que a entidade possa solicitar sua própria remoção, disparando um evento que o código de nível superior esteja ouvindo. Esse nível superior armazena essa solicitação de remoção em uma lista.


Exemplo 1: sem eventos

Você está verificando colisões entre entidades em seu mundo. Isso é tratado mais alto, geralmente no loop principal do jogo, que verifica todas as entidades umas contra as outras. Neste exemplo, especificamente, quando uma entidade colide com outra, apenas a lógica interna dessa entidade pode determinar quanto dano sofreu e se "expirou". Então, vamos seguir o fluxo lógico para colisões onde você tem quatro entidades em seu mundo, A, B, C e D. A é a nossa entidade com a qual estamos preocupados.

Verificamos A para colisão com B. Há uma colisão. A recebe dano de 50%.

Verificamos A para colisão com C. Há uma colisão. A recebe dano de 50%. Como o dano atinge 0, A determina que "morreu". Ele se remove da lista.

Verificamos A quanto a colisão com D. Não haveria colisão, mas você nunca chegará tão longe: você recebe uma exceção de tempo de execução porque sua lista de entidades foi modificada no meio de uma operação traveral.

Exemplo 2: com eventos

Mesma configuração de antes.

Verificamos A para colisão com B. Há uma colisão. A recebe dano de 50%.

Verificamos A para colisão com C. Há uma colisão. A recebe dano de 50%. Como o dano atinge 0, A determina que "morreu". Ele dispara um evento no código de gerenciamento da entidade para dizer "Remova-me o mais rápido possível". O código de gerenciamento da entidade examina a referência da entidade enviada como parte do evento e armazena essa referência em uma lista de entidades a serem removidas.

Verificamos A para colisão com D. Não há colisão, e a verificação funciona muito bem.

Agora, no final da iteração atual do loop do jogo , percorra a lista de entidades a serem removidas e remova cada uma delas da sua lista de entidades principais.


Você pode ver como isso evita o problema completamente. Você não precisa usar eventos, pode usar sinais ou algo mais, mas o princípio é o mesmo - não remova entidades até que você possa fazê-lo com segurança. O lado oposto dessa abordagem, para manter as coisas limpas e organizadas, é fazer o mesmo com as entidades a serem adicionadas - certifique-se de manter referências a elas e adicioná-las apenas no início da próxima iteração do loop do jogo.

Por fim, não esqueça de liberar as listas de remoção e adição, sempre que você as usar para realizar adições / remoções na sua lista principal de entidades.

PS. Não tenha medo de procurar em sua lista principal remoções individuais. É parte integrante do gerenciamento de entidades e até mesmo listas massivas tendem a ser muito rápidas de percorrer - afinal, é para isso que elas são projetadas.

Engenheiro
fonte
0

Você definitivamente está procurando um HashMap / HashTable . Uma hashtable é um mapa que corresponde a uma chave para um valor específico. A chave pode ser qualquer coisa (como o ID da entidade).

Setheron
fonte
0

Acho que você poderia usar a idéia de smartpointer para lidar com desalocações para você, nesse caso, não haverá necessidade de manter uma lista de todas as entidades dentro do seu código.

em alguns casos, você precisa de uma lista para percorrer todos os objetos do seu jogo. essa lista pode ser simplesmente uma lista de links em que inserir e remover objetos dela levará exatamente O (1) tempo.

mesmo para aumentar sua velocidade mais, você pode usar uma matriz estática (possivelmente um vetor). nesse caso, você precisará acompanhar duas listas vinculadas dentro do mesmo vetor, uma iterará sobre objetos válidos e outra iterará sobre objetos livres. sempre que o smartpointer marcar algum lugar a ser excluído, basta remover esse ponteiro e adicionar seu espaço à lista de espaços livres. e sempre que você adicionar alguma entidade, você só precisará remover o primeiro espaço livre, preenchê-lo com o ponteiro de entidade e adicioná-lo à lista de objetos válidos.

Ali1S232
fonte