Questão
Quando você tem um jogo 2D que usa imagens muito grandes (maiores do que o seu hardware pode suportar), o que você faz? Talvez haja alguma solução interessante para esse problema que nunca me ocorreu antes.
Estou basicamente trabalhando em uma espécie de criador de jogos de aventura gráfica. Meu público-alvo são pessoas com pouca ou nenhuma experiência em desenvolvimento (e programação) de jogos. Se você estivesse trabalhando com esse aplicativo, não preferiria que ele resolvesse o problema internamente, em vez de pedir para "dividir suas imagens"?
Contexto
É sabido que as placas gráficas impõem um conjunto de restrições ao tamanho das texturas que podem ser usadas. Por exemplo, ao trabalhar com o XNA, você fica restrito a um tamanho máximo de textura de 2048 ou 4096 pixels, dependendo do perfil escolhido.
Normalmente, isso não representa um problema nos jogos 2D, porque a maioria deles possui níveis que podem ser construídos a partir de peças individuais menores (por exemplo, peças ou sprites transformados).
Mas pense em alguns jogos clássicos de aventura gráfica point'n'click (por exemplo, Monkey Island). As salas de jogos gráficos de aventura geralmente são projetadas como um todo, com poucas ou nenhuma seção repetível, como um pintor trabalhando na paisagem. Ou talvez um jogo como o Final Fantasy 7, que usa grandes fundos pré-renderizados.
Algumas dessas salas podem ficar bem grandes, abrangendo frequentemente três ou mais telas de largura. Agora, se considerarmos esses planos de fundo no contexto de um jogo moderno de alta definição, eles facilmente excederão o tamanho máximo de textura suportado.
Agora, a solução óbvia para isso é dividir o plano de fundo em seções menores que se encaixam nos requisitos e desenhá-las lado a lado. Mas você (ou seu artista) deve ser o único a dividir todas as suas texturas?
Se você estiver trabalhando com uma API de alto nível, talvez não deva estar preparado para lidar com essa situação e fazer a divisão e a montagem das texturas internamente (como um pré-processo, como o Content Pipeline do XNA ou em tempo de execução)?
Editar
Aqui está o que eu estava pensando, do ponto de vista da implementação do XNA.
Meu mecanismo 2D requer apenas um subconjunto muito pequeno da funcionalidade do Texture2D. Na verdade, posso reduzi-lo a essa interface:
public interface ITexture
{
int Height { get; }
int Width { get; }
void Draw(SpriteBatch spriteBatch, Vector2 position, Rectangle? source, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth);
}
O único método externo que eu uso que depende de Texture2D é SpriteBatch.Draw (), para que eu pudesse criar uma ponte entre eles adicionando um método de extensão:
public static void Draw(this SpriteBatch spriteBatch, ITexture texture, Vector2 position, Rectangle? sourceRectangle, Color color, float rotation, Vector2 origin, Vector2 scale, SpriteEffects effects, float layerDepth)
{
texture.Draw(spriteBatch, position, sourceRectangle, color, rotation, origin, scale, effects, layerDepth);
}
Isso me permitiria usar uma ITexture de forma intercambiável sempre que eu usasse um Texture2D antes.
Então, eu poderia criar duas implementações diferentes do ITexture, por exemplo, RegularTexture e LargeTexture, em que RegularTexture simplesmente envolvia um Texture2D comum.
Por outro lado, LargeTexture manteria uma lista de Texture2D correspondendo a cada uma das partes divididas da imagem completa. A parte mais importante é que chamar Draw () na LargeTexture deve poder aplicar todas as transformações e desenhar todas as peças como se fossem apenas uma imagem contígua.
Por fim, para tornar todo o processo transparente, eu criaria uma classe unificada de carregador de textura que atuaria como um Método de Fábrica. Ele pegaria o caminho da textura como parâmetro e retornaria uma ITexture que seria uma RegularTexture ou uma LargeTexture, dependendo do tamanho da imagem que excede ou não o limite. Mas o usuário não precisava saber qual era.
Um pouco exagerado ou soa bem?
Respostas:
Eu acho que você está no caminho certo. Você precisa dividir as imagens em pedaços menores. Desde que você criou um game maker, não pode usar o pipeline de conteúdo do XNA. A menos que seu criador seja mais como um mecanismo que ainda exigirá que os desenvolvedores usem o Visual Studio. Então você precisa criar suas próprias rotinas que farão a divisão. Você deve até considerar transmitir as peças um pouco antes que se tornem visíveis, para que você ainda não limite a quantidade de memória de vídeo que os players devem ter.
Existem alguns truques que você pode fazer especificamente para jogos de aventura. Eu imagino que, como todos os jogos clássicos de aventura, você terá poucas camadas dessas texturas para que ... seu personagem possa andar atrás da árvore ou do prédio ...
Se você deseja que essas camadas tenham um efeito paralaxante, ao dividir suas peças, pule toda a translúcida uma vez. Aqui você precisa pensar no tamanho dos ladrilhos. Os blocos menores adicionarão o gerenciamento de despesas gerais e as chamadas de chamadas, mas haverá menos dados, onde os blocos maiores serão mais amigáveis à GPU, mas terão muita translucidez.
Se você não tiver um efeito paralaxante, em vez de cobrir a cena inteira com 2 a 4 imagens gigantes com profundidade de 32 bits, você poderá criar apenas uma profundidade de 32 bits. E você pode ter uma camada de stencil ou profundidade que será de apenas 8 bits por pixel.
Eu sei que isso parece difícil de gerenciar ativos por não desenvolvedores de jogos, mas na verdade não precisa ser. Por exemplo, se você tem uma rua com 3 árvores e o jogador fica atrás de 2 delas, na frente da terceira árvore e no restante do plano de fundo ... Faça o layout da cena da maneira que desejar no seu editor e depois em um Para criar uma etapa do seu criador de jogos, você pode assar essas imagens gigantes (bem pequenas peças de uma imagem grande).
Sobre como os jogos clássicos estavam fazendo isso. Não sei se eles provavelmente fizeram truques semelhantes, mas você deve se lembrar de que na época a tela tinha 320x240, os jogos tinham 16 cores que, ao usar texturas palatizadas, permitem codificar 2 pixels em um byte. Mas não é assim que as arquiteturas atuais funcionam: D
Boa sorte
fonte
Corte-os para que eles não sejam mais arbitrariamente grandes, como você diz. Não há mágica. Esses jogos 2D antigos fizeram isso ou foram renderizados em software; nesse caso, não havia restrições de hardware além do tamanho da RAM. É digno de nota que muitos desses jogos 2D realmente não tinham grandes origens, apenas rolavam lentamente para se sentirem enormes.
O que você está procurando fazer em seu criador de jogos de aventura gráfica é pré-processar essas imagens grandes durante algum tipo de etapa de "compilação" ou "integração" antes que o jogo seja empacotado para execução.
Se você quiser usar uma API e uma biblioteca existentes, em vez de escrever a sua própria, sugiro que você converta essas imagens grandes em blocos durante essa "construção" e use um mecanismo de bloco 2D, dos quais existem muitos.
Se você quiser usar suas próprias técnicas em 3D, poderá dividir-se em blocos e usar uma API 2D bem conhecida e bem projetada para escrever sua própria biblioteca em 3D, dessa forma, é garantido que você estará começando pelo menos com um bom design de API e menos design necessário de sua parte.
fonte
Se você estiver familiarizado com a aparência de um quadtree na prática visual, usei um método que corta imagens grandes e detalhadas em imagens menores, relativamente semelhantes à aparência de uma prova / visualização de um quadtree. No entanto, as minhas foram feitas dessa maneira para cortar áreas especificamente que poderiam ser codificadas ou compactadas de maneira diferente com base na cor. Você pode usar esse método e fazer o que descrevi, pois também é útil.
Em tempo de execução, cortá-los exigiria mais memória temporariamente e reduziria o tempo de carregamento. Eu diria que isso depende se você se preocupa mais com o armazenamento físico ou o tempo de carregamento de um jogo de usuários feito com seu editor.
Carga / tempo de execução: A maneira que eu usaria o carregamento é apenas cortá-lo em pedaços de tamanho proporcional. No entanto, considere um pixel na parte mais à direita, se for um número ímpar de pixels, ninguém gosta que suas imagens sejam cortadas, especialmente usuários inexperientes que talvez não saibam que isso não depende inteiramente deles. Torne-os com metade da largura OU metade da altura (ou 3º, 4º, o que for), o motivo não para quadrados ou 4 seções (2 linhas e 2 colunas) é porque isso tornaria a memória lado a lado menor, pois você não se preocuparia com xey ao mesmo tempo (e dependendo de quantas imagens são grandes, pode ser muito importante)
Antes da Mão / Automaticamente: Nesse caso, gosto da idéia de oferecer ao usuário a opção de armazená-lo em uma imagem ou em partes separadas (uma imagem deve ser o padrão). Armazenar a imagem como um gif funcionaria muito bem. A imagem estaria em um arquivo e, em vez de cada quadro ser uma animação, seria mais como um arquivo compactado da mesma imagem (que é essencialmente o que é um .gif). Isso permitiria facilidade de transporte físico de arquivos, facilidade de carregamento:
Ah, e se você usar o método .gif, altere a extensão do arquivo para outra coisa (.gif -> .kittens) para evitar menos confusão quando o seu .gif for animado como se fosse um arquivo quebrado. (não se preocupe com a legalidade disso, .gif é um formato aberto)
fonte
Isso vai parecer óbvio, mas, por que não dividir seu quarto em pedaços menores? Escolha um tamanho arbitrário que não esbarrar nas placas gráficas (como 1024x1024, ou talvez maior) e apenas dividir suas salas em várias partes.
Sei que isso evita o problema e, honestamente, esse é um problema muito interessante de resolver. De qualquer forma, essa é uma solução trivial, que pode funcionar algumas vezes para você.
fonte