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.