Livrar-se de objetos mortos em um jogo de forma eficaz?

10

Estou usando um loop for ou foreach (não importa) para percorrer todos os meus objetos quando eles precisam ser atualizados ou desenhados. No entanto, quando um objeto é morto, quero que ele seja removido da coleção novamente.

Eu faço isso adicionando o objeto a uma lista de objetos mortos e, quando tudo foi desenhado e atualizado, passo por tudo nessa lista, removendo também os objetos da lista da fonte original.

Existe um jeito melhor de fazer isso?

Mathias Lykkegaard Lorenzen
fonte
2
Isso realmente não tem nada a ver com coleta de lixo, apesar do uso da palavra "lixo" no título e das respostas até agora mencionadas no .NET Garbage Collector.
Andrew Russell
Tomei a liberdade de alterar a palavra "lixo" para "morto", para evitar confusão com o coletor de lixo do .NET.
Nevermind
Oh, eu estou ciente disso. O artigo de coleta de lixo ainda é relevante porque pode ser útil no que você faz com esses objetos mortos. Por exemplo, detritos mortos? Desvincule-o de tudo, redefina-o para os padrões, coloque-o novamente no balde de detritos.
Doppelgreener 28/10/11

Respostas:

11

Primeiro de tudo: terminologia. Se você diz "lista de lixo", as pessoas pensam que você está falando sobre o coletor de lixo. Vamos chamá-lo de "lista morta".

Como você provavelmente descobriu, daí a sua pergunta, você não pode remover itens de uma coleção enquanto estiver iterando por ela. Se sua coleção é uma List<>maneira mais simples de remover itens, é:

for(int i = list.Count-1; i >= 0; --i)
{
    if(list[i].IsDead)
        list.RemoveAt(i);
}

Observe que você itera para trás . Você pode remover itens enquanto faz a iteração para a frente, mas é mais confuso e requer um goto. Você não pode fazer isso com foreach.

Você pode fazer a remoção separadamente do seu loop de atualização. Ou você pode mesclá-lo ao seu loop de atualização. Sempre faça o RemoveAtfinal do loop - depois desse ponto no loop, list[i]não poderá ser considerado válido (ele se tornará válido novamente na próxima iteração).

Observe também que cada objeto tem seu próprio IsDeadsinalizador (se você estiver usando o padrão de descarte, poderá fazê-lo IsDisposed) - você realmente não precisa manter uma "lista de mortos".

É preferível o desempenho de um sinalizador em cada item, pois você evita procurar na lista para fazer a remoção. E também é preferível para o design - significa que cada objeto pode verificar facilmente se está morto - para que você não invoque acidentalmente nenhum método (se esses objetos estiverem implementando o padrão descartável, eles podem ser lançados ObjectDisposedExceptionnesse caso).

Se você tiver uma coleção diferente de uma List<>, a remoção de itens inativos dessa coleção ainda poderá envolver a criação de uma lista inoperante (pois a remoção durante a iteração pode não ser possível). Na minha opinião, é melhor, em termos de design, criar a lista de mortos imediatamente antes de ser usada, percorrendo a coleção procurando IsDeadsinalizadores, em vez de tentar manter a lista à medida que os objetos são mortos.

Depois de terminar com uma lista de mortos, você deve Clear()fazê - lo e mantê-lo por perto para reutilizá-lo mais tarde.

Andrew Russell
fonte
Então o padrão descartável é considerado mais 'eficaz' então?
Jonathan Connell
@ Jonathan: Eu não entendo sua pergunta. O IsDeadpadrão é funcionalmente idêntico a IsDisposed. O uso semântico IsDeadimplica que você esteja mantendo uma lista Draw / Update desses objetos que terão itens mortos removidos posteriormente. O padrão de descarte não implica isso. Além disso, o padrão de disposição não implica que você pode ter recursos não gerenciados detidas por esse objeto (o que geralmente não é apropriado para objectos de jogo).
Andrew Russell
Você pode fazer isso com o foreach usando o reverse.
shinzou
0

Em 99,9% do tempo, o coletor de lixo (GC) cuidará de tudo sem aborrecimentos. Apenas deixe-o fazer o seu trabalho depois de excluir / anular esses objetos. Há uma latência na limpeza que depende de vários fatores (quantos blocos de memória foram atribuídos, por exemplo), mas você normalmente não precisa nem saber disso, muito menos se preocupar com isso. Ele foi projetado para funcionar. Qual é o motivo pelo qual você está usando C # e não C.

Em relação ao desempenho do GC em geral, não se preocupe demais, a menos que você saiba (por meio de criação de perfil) que isso está criando um gargalo - isso é apenas em jogos bastante exigentes, em geral. Se for esse o caso, consulte GC.Collect () e suas várias armadilhas, para saber quando é apropriado usá-lo. Eu sugiro pesquisar no Google "force gc xna", há muito material por aí.

Engenheiro
fonte
Portanto, se eu tiver sempre uma lista que estou usando na minha chamada Update (), todos os elementos onde o elemento é "nulo" serão limpos automaticamente E removidos?
Mathias Lykkegaard Lorenzen
@Mathias No. Veja meu comentário sobre sua pergunta. O coletor de lixo não modificará nada que você ainda possa acessar - incluindo sua coleção e seu conteúdo.
Andrew Russell
1
A questão não tem nada a ver com o GC, exceto a escolha infeliz da palavra "lixo".
Nevermind