Isso pode ser fácil para jogos com escopo bem definido, mas a questão é sobre jogos em área restrita, onde o jogador pode criar e construir qualquer coisa .
Possíveis técnicas:
- Use conjuntos de memória com limite superior.
- Exclua objetos que não são mais necessários periodicamente.
- Aloque uma quantidade extra de memória no início para que possa ser liberada posteriormente como um mecanismo de recuperação. Eu diria cerca de 2-4 MBs.
É mais provável que isso aconteça em plataformas móveis / console, onde a memória geralmente é limitada, diferentemente do seu PC de 16 GB. Suponho que você tenha controle total sobre a alocação / desalocação de memória e nenhuma coleta de lixo envolvida. É por isso que estou marcando isso como C ++.
Observe que não estou falando do item 7 do C ++ eficaz "Esteja preparado para condições de falta de memória" , mesmo que seja relevante, gostaria de ver uma resposta mais relacionada ao desenvolvimento de jogos, onde você geralmente tem mais controle sobre o que é acontecendo.
Para resumir a pergunta, como você se prepara para condições de falta de memória para jogos em sandbox, quando está direcionando uma plataforma com console / dispositivo de memória limitado?
fonte
Respostas:
Geralmente, você não lida com falta de memória. A única opção sensata em software tão grande e complexo quanto um jogo é simplesmente travar / afirmar / encerrar no seu alocador de memória o mais rápido possível (especialmente em compilações de depuração). As condições de falta de memória são testadas e manipuladas em alguns softwares do sistema principal ou em servidores em alguns casos, mas geralmente não em outros lugares.
Quando você tem um limite de memória superior, apenas garante que nunca precisará de mais do que essa quantidade de memória. Você pode manter um número máximo de NPCs permitidos por vez, por exemplo, e simplesmente parar de gerar novos NPCs não essenciais assim que o limite for atingido. Para NPCs essenciais, você pode substituí-los por outros não essenciais ou ter um pool / limite separado para NPCs essenciais que seus designers sabem projetar (por exemplo, se você pode ter apenas 3 NPCsa essenciais, os designers não colocarão mais do que 3 em uma área / parte - boas ferramentas ajudarão os designers a fazer isso corretamente e o teste é essencial, é claro).
Um sistema de streaming realmente bom também é importante, especialmente para jogos em sandbox. Você não precisa manter todos os NPCs e itens na memória. À medida que você avança por partes do mundo, novas partes serão inseridas e partes antigas serão exibidas. Isso geralmente inclui NPCs e itens, além de terrenos. Os limites de projeto e engenharia nos limites de itens precisam ser definidos com esse sistema em mente, sabendo que no máximo X trechos antigos serão mantidos ao redor e carregados de forma proativa Y serão carregados novos trechos, para que o jogo precise ter espaço para manter tudo os dados de pedaços X + Y + 1 na memória.
Alguns jogos tentam lidar com situações de falta de memória com uma abordagem de duas passagens. Lembre-se de que a maioria dos jogos possui muitos dados em cache tecnicamente desnecessários (por exemplo, os blocos antigos mencionados acima) e uma alocação de memória pode fazer algo como:
Esta é uma medida de última parada para lidar com situações inesperadas no lançamento, mas durante a depuração e teste, você provavelmente deve travar imediatamente. Você não precisa depender desse tipo de coisa (especialmente porque o descarte dos caches pode ter sérias conseqüências de desempenho).
Você também pode considerar despejar cópias de alta resolução de alguns dados, por exemplo, pode despejar os níveis de texturas mipmap de maior resolução se estiver com pouca memória GPU (ou qualquer memória em uma arquitetura de memória compartilhada). Isso geralmente requer muito trabalho arquitetônico para fazer valer a pena, no entanto.
Observe que alguns jogos sandbox muito ilimitados podem ser facilmente travados, mesmo no PC (lembre-se de que os aplicativos comuns de 32 bits têm um limite de 2-3 GB de espaço de endereço, mesmo se você tiver um PC com 128 GB de RAM; O SO e o hardware de bit permite que mais aplicativos de 32 bits sejam executados simultaneamente, mas não podem fazer nada para fazer com que um binário de 32 bits tenha um espaço de endereço maior). No final, você tem um mundo de jogo muito flexível que precisará de espaço de memória ilimitado para rodar em todos os casos ou um mundo muito limitado e controlado que sempre funciona perfeitamente na memória limitada (ou algo no meio).
fonte
O aplicativo geralmente é testado na plataforma de destino com os piores cenários e você sempre estará preparado para a plataforma de destino. Idealmente, o aplicativo nunca deve falhar, mas, além da otimização para dispositivos específicos, há poucas opções quando você recebe um aviso de pouca memória.
A melhor prática é ter pools pré-alocados e o jogo usa desde o início toda a memória necessária. Se o seu jogo tiver no máximo 100 unidades, você terá um pool para 100 unidades e pronto. Se 100 unidades excederem os requisitos de memória para um dispositivo de destino, você poderá otimizar a unidade para usar menos memória ou alterar o design para no máximo 90 unidades. Não deve haver caso em que você possa construir coisas ilimitadas, sempre deve haver um limite. Seria muito ruim para um jogo de sandbox usar
new
para cada instância, porque você nunca pode prever o uso de mem e uma falha é muito pior do que uma limitação.Além disso, o design do jogo deve sempre ter em mente os dispositivos mais baixos, porque se você basear seu design com itens "ilimitados", será muito mais difícil resolver os problemas de memória ou alterar o design posteriormente.
fonte
Bem, você pode alocar cerca de 16 MiB (apenas para ter 100% de certeza) na inicialização ou mesmo em
.bss
tempo de compilação e usar um "alocador seguro", com uma assinatura comoinline __attribute__((force_inline)) void* alloc(size_t size)
(__attribute__((force_inline))
é ummingw-w64
atributo / GCC que força o alinhamento de seções críticas de código mesmo que as otimizações estejam desativadas, mesmo que devam estar ativadas para jogos), em vez demalloc
tentar,void* result = malloc(size)
e se falhar, elimine caches, libere a memória sobressalente (ou diga a outro código para usar a.bss
coisa, mas isso está fora do escopo desta resposta) e libere dados não salvos (salve o mundo no disco, se você usar um conceito de blocos do tipo Minecraft, chame algo assimsaveAllModifiedChunks()
). Então, semalloc(16777216)
(alocar esses 16 MiB novamente) falhar (novamente, substitua por analógico por.bss
), encerre o jogo e mostreMessageBox(NULL, "*game name* couldn't continue because of lack of free memory, but your world was safely saved. Try closing background applications and restarting the game", "*Game name*: out of memory", MB_ICONERROR)
ou uma alternativa específica da plataforma. Juntando tudo:Você pode usar uma solução semelhante com
std::set_new_handler(myHandler)
ondemyHandler
évoid myHandler(void)
chamado quandonew
falha:fonte