À luz do DRY , parece desejável armazenar uma coleção de objetos de jogos relacionados em apenas um contêiner. No entanto, pode ser necessário subconjuntos desses objetos em vários contextos. Pode ser sensato armazenar esses subconjuntos específicos em recipientes específicos e mais adequados. Isso aumenta o esforço para rastrear objetos entre contêineres, por exemplo, quando objetos são removidos do mundo do jogo.
Quais são as possibilidades de simplificar esse design e quais são os trade-offs típicos?
Ilustrar:
Em um jogo de RPG para vários jogadores, o servidor pode conter uma coleção de personagens do jogo em um mapa adequado para pesquisa por ID.
world
map<id, Character> allCharacters
Um personagem também pode residir em um determinado nível de jogo. Para identificar todos os personagens presentes em um nível, pode parecer adequado introduzir um contêiner para cada nível que contenha os personagens atualmente presentes nele. Dessa forma, você pode executar uma lógica comum para todos os caracteres nesse nível.
world
map<id, Character> allCharacters
[levels]
level1
vector<Character> charactersOnPlayfield
level2
vector<Character> charactersOnPlayfield
...
Indo ainda mais longe, quando um personagem interage com as mensagens do mundo só deve ser roteado para caracteres dentro do alcance. Esse gerenciamento de interesses pode ser alcançado dividindo-se cada nível em uma grade de células, cada uma das quais armazenando os caracteres atualmente em pé nela.
world
map<id, Character> allCharacters
[levels]
level1
vector<Character> charactersOnPlayfield
[cells]
cell1
vector<Character> charactersOnCell
cell2
vector<Character> charactersOnCell
...
level2
vector<Character> charactersOnPlayfield
[cells]
cell1
vector<Character> charactersOnCell
cell2
vector<Character> charactersOnCell
...
...
Os objetos Personagem em diferentes níveis de abstração introduzem a necessidade de pensar cuidadosamente sobre a propriedade do objeto e o tempo de vida.
Observe que os objetos Character armazenados nos contêineres seriam naturalmente referências, não cópias. Além disso, estou assumindo que não há coleta de lixo.
fonte
Todo caractere deve, de uma forma ou de outra, conhecer todos os contêineres em que está contido. Isso pode ser explícito (uma lista real dos contêineres que o referenciam), computável (as coordenadas de caracteres são armazenadas, a partir das quais os vetores de células reais podem ser derivados ) ou não ditas ("os caracteres também podem ser armazenados em qualquer uma dessas seis listas e não vou me incomodar em acompanhar quais"). Ou qualquer combinação dos três. O importante é que é fácil e eficiente encontrar todos os locais que olham para um personagem.
Também deve haver um armazenamento canônico que armazene todos os personagens do jogo, como o mapa allCharacters lá em cima.
Um personagem está vivo se e somente se estiver contido em todos os caracteres. Se você deseja removê-lo de allCharacters, remova-o de todos os contêineres que também o contêm (que, conforme o primeiro parágrafo, devem ser razoavelmente eficientes) e está pronto.
Isso pressupõe que você não tem coleta de lixo - se o fizer, poderá deixar o GC cuidar disso. Se você tiver vazamentos, faça um contêiner de referência fraca contendo todas as entidades e use-o para analisar quais entidades estão vazando.
fonte
Ao responder minha pergunta com a experiência que tive até agora, basicamente vejo estas opções:
Extraia sua coleção mais específica de objetos da coleção mais geral sempre que precisar
Armazene sua coleção mais específica em contêineres especializados, conforme fornecido no exemplo desta pergunta
Use cache para determinadas perspectivas / subconjuntos dos objetos do jogo
Os fatores para a escolha de uma dessas opções incluem
O padrão observador pode ajudar a propagar a necessidade de alterar os outros subconjuntos de objetos.
fonte