Devo usar planilhas, devido ou apesar do grande número de imagens?

13

Estou desenvolvendo um jogo 2D e tenho muitos sprites. Eu usei animações e modelos 3D para renderizar em 2D, para dar a eles que "Fallout" ou "Diablo" olhem para eles. Também é mais fácil do que desenhar à mão, lol.

Eu já tive que reduzir a taxa de quadros para 15 fps, que foi a menor que eu pude diminuir sem fazer com que eles tivessem uma aparência instável. No entanto, foi triste devido à aparência incrivelmente suave de 24 quadros.

Há duas razões pelas quais fiz isso:

1) Reduza o espaço no disco rígido. Quanto menos imagens, menor será o meu jogo total.

2) Reduza o consumo de RAM. Quanto menos imagens forem carregadas, maior a probabilidade de evitar problemas que limitam minha limitação de RAM.

No entanto, se houvesse uma maneira de compactar as imagens no espaço do disco rígido e na RAM, eu o faria. Eu testei isso antes, e a maioria não recebe nenhuma alteração na qualidade ao passar de RGBA8888 para RGBA5555 e apenas um pequeno golpe ao converter para RGBA4444 no meu programa TexturePacker. No momento, não faço isso porque o SFML parece usar a mesma quantidade de memória, independentemente do tipo de imagem .PNG. Eu pesquisei sobre como carregá-lo de maneira diferente, mas não consegui encontrar nada sobre o assunto.

Eu li muito sobre como lidar com videogames 2D. O consenso é esmagador: coloque seus Sprites em uma textura maior para obter um ótimo desempenho! Então, eu agrupo meus pequenos sprites em uma planilha muito maior usando o TexturePacker.

No entanto, pretendo ter 10 a 15 animações por personagem, 5 direções para mover e 15 a 40 quadros por animação (provavelmente uma média de 24). Com 15 animações, 5 direções e uma média de 24 quadros por animação; Isso significa 1800 quadros individuais por personagem. Se embalado em uma folha de sprite, são apenas 75 imagens. (Uma folha de sprite por animação, por direção. 15 * 5)

Para o grande personagem chefe do jogo, não posso usar uma planilha e preciso programar uma maneira de simplesmente carregar uma imagem de cada vez. Ainda não sei se posso fazer isso por desempenho.

Para os personagens, eu já os coloco em uma planilha. Para um único personagem andando, isso parece funcionar na maioria das vezes, embora às vezes pare. No entanto, atribuo isso ao meu código mal concebido que troca as texturas em vez de pré-carregar todas as texturas para esse personagem.

Se eu fosse pré-carregar as texturas, faz sentido para folhas de sprite. Imagino que seja uma má ideia pré-carregar 1800 pequenas imagens para cada personagem.

No entanto, imagino que transmiti-los para dentro e para fora da memória, um de cada vez, seria extremamente rápido, portanto, eu só precisaria ter uma única imagem na memória de cada vez. Isso não significaria que, em um determinado momento, cada personagem consumisse apenas alguns KB em vez de 45 + MB?

Eu imagino que isso prejudicaria meu desempenho, pois o streaming precisaria ser incrivelmente rápido (15 imagens entrando e saindo da memória e renderizando por segundo) e, embora as imagens fossem muito pequenas, seria uma idéia melhor carregar planilhas de caracteres na memória. Mas terei que codificar um sistema de renderização semelhante a um fluxo de imagem única para meu personagem maior, de qualquer maneira.

Eu tenho experimentado, mas não é um processo simples. Especialmente porque estou trabalhando em outras partes do mecanismo de jogo que não lidam com gráficos no momento.

Carter81
fonte
1. Você não especificou suas restrições de RAM ou HDD. Quantos caracteres precisam ter acesso rápido? 2. Existem várias perguntas ao longo do texto, talvez você possa focalizá-las em negrito ou até dividir as perguntas em partes?
Kromster diz apoio Monica
Oh, me desculpe. Nao muitos. Eu imaginaria que o número máximo de caracteres individuais na tela a qualquer momento seria de cerca de 40. Se as pessoas fizeram questão de tentar travar seus clientes, então ... 130 é o máximo absoluto. Normalmente, seria necessário apenas 10 para o máximo típico e o máximo absoluto não seria maior que <40. Qualquer coisa acima de 40 seria uma raridade extrema, extrema, com os usuários propositadamente tentando amontoar os personagens por nenhuma outra razão que não seja uma captura de tela ou para a diversão de amontoar nos personagens. Qualquer coisa acima de 10 é rara, e qualquer coisa sobre 40 é extremamente, extremamente rara.
precisa saber é o seguinte
O jogo é um rpg 2D apenas para PC (sem dispositivos móveis), no entanto, eu não gostaria de descartar uma plataforma móvel, a menos que simplesmente não seja viável. Eu imagino que o espaço de RAM que eu tenho seja limitado a qualquer RAM que esteja no PC do usuário e na VRAM do usuário. Eu duvido muito que seja muito grande o HDD. É sempre verdade que, para o consumo de HDD, quanto menor, melhor.
precisa saber é o seguinte
Quantos caracteres ÚNICOS você precisa ter na tela?
Kromster diz apoio Monica
Não mais que 20. Qualquer coisa acima disso seria quase impossível, a menos que trapaceie. Tipicamente 5-10.
precisa saber é o seguinte

Respostas:

16

Temos um caso semelhante com o nosso RTS Remake. Todas as unidades e casas são sprites. Temos 18.000 sprites para unidades e casas e terrenos, além de outros ~ 6.000 para cores de equipe (aplicadas como máscaras). Além disso, também temos cerca de 30.000 caracteres usados ​​em fontes.

Portanto, a principal razão por trás dos atlas é:

  • RAM menos desperdiçada (nos dias anteriores, quando você carrega o NPOT na GPU, ele estica / acolchoa no POT, eu li que ainda é o mesmo no iOS e em algumas estruturas. É melhor verificar a gama de hardware que você deseja)
  • menos opções de textura
  • carregamento mais rápido de tudo em menos pedaços maiores

O que não funcionou para nós:

  • texturas paletizadas. O recurso existia apenas no OpenGL 1.x 2.xe mesmo assim foi descartado principalmente pelos fabricantes de GPU. No entanto, se você mira no OpenGL + Shaders, pode fazê-lo no código shaders.
  • Em texturas NPOT, tivemos problemas com bordas erradas e sprites borrados, o que é inaceitável na arte pixel. O uso de RAM também foi muito maior.

Agora temos tudo em várias dezenas de atlas 1024x1024 (as GPUs modernas suportam dimensões ainda maiores) e isso funciona muito bem, consumindo apenas ~ 300mb de memória, o que é bastante bom para um jogo de PC. Algumas otimizações que tivemos:

  • adicione a opção de usuário para usar RGB5_A1 em vez de RGBA8 (sombras do tabuleiro de damas)
  • Evite Alpha de 8 bits quando possível e use o formato RGB5_A1
  • compactar sprites firmemente em atlas (consulte Algoritmos de embalagem de bin)
  • armazene e carregue tudo em um pedaço do HDD (os arquivos de recursos devem ser gerados offline)
  • você também pode tentar formatos de compactação de hardware (DXT, S3TC etc.)

Ao considerar seriamente a possibilidade de mudar para dispositivos móveis, você se preocupará com restrições. Por enquanto, basta começar o jogo e atrair jogadores! ;)

Kromster diz apoio Monica
fonte
Esta foi a melhor solução de longe! Meus sprites nem parecem diferentes em RGB5_A1 ou RGBA4444, mas economizam memória. Uma sugestão sua no chat para pré-carregar todos os meus ativos na RAM e VRAM é perfeita. Ainda mais, você sugeriu ter níveis gráficos opcionais, como cliente de alta definição para quem tem RAM, ou uma opção para reduzir a taxa de quadros pela metade, etc. Ótimas sugestões ao redor e exatamente o que eu precisava!
Carter81
5

Eu tenho uma resposta tangencialmente relacionada em aqui , mas a ideia geral é que, se você estiver carregando e desenhando texturas em momentos diferentes (você não está carregando texturas adicionais enquanto está renderizando), então existem dois lugares onde você afetará seu desempenho:

Tempo de carregamento:

Este é o momento em que você carrega suas texturas na memória. a inteira quantidade de dados que você está enviando para VRAM é o que a maioria irá definir quanto tempo o seu tempo de carregamento será. Tornar suas texturas formatos menores, como RGBA4444, tornará isso mais rápido. No entanto, a menos que você esteja carregando texturas nas centenas de megabytes para a VRAM, provavelmente não terá um gargalo aqui. Se o fizer, uma boa tela de carregamento pode facilitar a espera.

Unir suas texturas aos atlas terá pouco efeito, pois toda a quantidade de informações que você está enviando para o VRAM será a mesma. De fato, se você está atlasando suas texturas e precisa deixar espaços vazios em seus atlas, estará enviando mais dados para o VRAM e, portanto, essa parte será mais lenta!

Desempenho de renderização:

Quando todas as suas texturas estiverem em VRAM, a quantidade de texturas que você possui não afetará o desempenho da renderização. Existem quatro elementos que afetam o desempenho da renderização:

  1. Alterações de estado de renderização : sempre que você alterar a imagem da qual deseja renderizar, aumentará seriamente o tempo necessário para renderizá-la. Em geral, você deseja minimizar a quantidade de alterações de estado e pode reduzir a quantidade de alterações de estado agrupando várias imagens que serão desenhadas consecutivamente em um atlas de textura.

    Apenas atlas não é suficiente. Você precisa atlas de uma maneira em que as alterações de estado sejam reduzidas, a fim de obter ganhos de desempenho. Por exemplo, pode-se pensar que ter seu personagem principal em uma folha de sprite obterá um ganho de desempenho, mas se você estiver apenas desenhando um sprite dessa folha de sprite por quadro, não obterá nenhum ganho de desempenho em comparação a ter cada sprite em um arquivo separado.

    Atlasing adequado não é trivial, mas em geral você pode agrupar sprites com segurança da mesma camada. Por exemplo, ter todos os itens da GUI em uma planilha de sprites é uma idéia muito promissora, enquanto agrupar monstros em ordem alfabética pode não.

  2. Chamadas de empate: em geral, convém manter suas chamadas de empate no mínimo. Uma boa regra geral é que, se não houver alterações no estado de renderização entre duas chamadas de draw, você poderá juntá-las em uma única chamada de draw. Para obter ganhos de desempenho mais avançados, você pode usar, por exemplo, 8 amostradores de textura e chamadas em grupo para cada 8 texturas; portanto, você só precisa alterar as texturas a cada 8 texturas.

  3. Contagem de triângulos: Na verdade, quanto mais triângulos você desenhar, mais tempo levará para desenhá-los. No entanto, nos computadores modernos e na maioria dos jogos 2D, você estará muito longe de maximizar isso. Você pode desenhar com segurança centenas de milhares de sprites por quadro e ainda obter taxas de quadros incrivelmente boas. Você provavelmente estará mais ligado à CPU se estiver desenhando quantidades extremas de sprites antes de ter problemas com sua GPU.

  4. Configurações da API: se você estiver fazendo tudo certo e continuar com taxas de quadros estranhamente baixas, verifique as configurações com as quais está desenhando seus sprites. Não conheço SFML, mas, por exemplo, no Direct3D 9, a criação de um buffer de vértice com D3DUSAGE_DYNAMIC, ou D3DPOOL_MANAGEDpode aumentar facilmente o tempo de renderização em dez vezes. Obviamente, o uso do vSync limitará a taxa de quadros na taxa de atualização do seu monitor. Além disso, o uso de FVFs não alinhados pode diminuir o desempenho em algumas GPUs. Isso também é para o Direct3D 9.

    No seu caso, verifique a documentação da API que você está usando.

Se você tiver apenas uma quantidade baixa a moderada de texturas (menos de 1 GB) e estiver desenhando pequenas quantidades de sprites (menos de um milhão por quadro), a primeira coisa que gostaria de ver é alterar as configurações da API e reduzindo a quantidade de estados de renderização e fazer chamadas.

Panda Pajama
fonte
Se eu não me importo com o tempo de carregamento, devo assumir que, a menos que eu fique sem RAM ou VRAM, devo carregar tudo na memória antes?
precisa saber é o seguinte
Estou preocupado apenas com tudo, porque tenho medo de ficar com pouca memória RAM / VRAM. Não sei por que, mas me petrifica que os usuários que jogam meu jogo falhem sempre que tentam carregar em uma área que tenha muitos sprites únicos ou que falhem sempre que muitos personagens entram na tela. Se não me engano e cada sprite individual consumir 96 KB, se cada caractere único tiver 15 animações, 5 direções e uma média de 24 quadros por animação - cada caractere totalmente carregado será 173 MB. Pode haver 10, talvez até mais, caracteres únicos na tela ao mesmo tempo.
precisa saber é o seguinte
@KromStern: se você criar um atlas e precisar deixar espaços vazios (preenchimento), os dados serão maiores e, portanto, o tempo de carregamento será maior. Está claro que a causa de tempos de carregamento mais longos é o espaço vazio e que o tempo total de carregamento está relacionado à quantidade total de dados e não à quantidade de texturas. Não vejo nada de enganoso por lá, e acho que uma pessoa com conhecimento suficiente para entender a pergunta original será capaz de juntar os pontos e tirar suas próprias conclusões para todos os casos em que as texturas e atlas são PoT e nPoT.
Panda Pyjama
1
@ Carter81: Você precisa escolher a configuração de hardware de destino, como "i5, 1 GB de RAM, NVidia GT260, disco rígido de 400 mb" e trabalhar com isso. Sempre haverá PCs mais fracos e com menos RAM.
Kromster diz apoio Monica
Obviamente, eles não precisam de todas as 15 animações a qualquer momento. No entanto, e se todos os 10 personagens únicos entrarem em "Modo de combate" ao mesmo tempo e exigirem que 5 conjuntos de animações (caminhada, corrida, inatividade, sem combate etc.) sejam trocados por mais 5 (caminhada de combate, combate ocioso, combate etc.)? Estou com medo de trocar texturas porque, quando tentei fazer isso com o SFML, ele criou um congelamento ou pausa perceptível do cliente ao alternar os atlas de textura. Não sei qual seria meu gargalo com as várias estratégias para lidar com tantos sprites.
precisa saber é o seguinte