Usando o XNA ContentPipeline para exportar um arquivo em uma máquina sem o XNA GS completo

9

Meu jogo usa o Pipeline de conteúdo para carregar o spriteSheet em tempo de execução. O artista do jogo me envia a planilha modificada e eu faço uma compilação na minha máquina e envio a ele um projeto atualizado. Então, estou procurando uma maneira de gerar os arquivos xnb em sua máquina (esta é a saída do pipeline de conteúdo) sem que ele precise instalar o estúdio XNA Game completo.

1) Não quero que meu artista instale o VS + Xna (eu sei que há uma versão gratuita do VS, mas isso não será dimensionado quando adicionarmos mais pessoas à equipe). 2) Não estou interessado em executar este editor / ferramenta no Xbox, portanto, uma solução apenas para Windows funciona. 3) Estou ciente das opções do MSBuild, mas elas exigem XNA completo

Eu pesquisei no blog de Shawn e encontrei a opção de usar o Msbuild Sample ou uma nova opção no XNA 4.0 que parecia promissora aqui, mas parece ter a mesma restrição: É necessário instalar o XNA GS completo porque o ContentPipeline não faz parte do redna do XNA.

Então, alguém encontrou uma solução alternativa para isso?

krolth
fonte

Respostas:

11

Parece que a resposta certa para isso é pular o ContentPipeline e usar Texture2D.FromStream para carregar as texturas em tempo de execução. Esse método funciona bem em um PC e, mesmo com um pequeno impacto no desempenho, isso é algo que eu posso otimizar quando estiver mais próximo da data de lançamento. Por enquanto, ter a capacidade de modificar dinamicamente o conteúdo do editor e do jogo é exatamente o que eu preciso. Depois que o conteúdo estiver congelado, eu posso otimizar isso retornando ao ContentPipeline.

Como você escolheu essa rota, devo avisá-lo de que não é tão simples quanto usar apenas Texture2D.FromStreampor dois motivos:

Problema # 1 - Falta de suporte alfa pré-multiplicado

O XNA4 agora lida com texturas com cores no formato alfa pré-multiplicado por padrão. Quando você carrega uma textura através do pipeline de conteúdo, esse processamento é feito automaticamente para você. Infelizmente Texture2D.FromStream, não faz o mesmo, portanto, qualquer textura que exija algum grau de transparência será carregada e renderizada incorretamente. Abaixo está uma captura de tela para ilustrar o problema:

insira a descrição da imagem aqui

Portanto, para obter os resultados corretos, você mesmo precisa fazer o processamento. O método que mostrarei usa a GPU para fazer o processamento, então é bem rápido. Foi baseado neste ótimo artigo . Claro que você também pode instruir SpriteBatchpara renderizar no antigo modo NonPremultiplyAlpha, mas eu realmente não recomendo fazer isso.

Problema # 2 - Formatos não suportados

O pipeline de conteúdo suporta mais formatos que Texture2D.FromStream. Em particular, Texture2D.FromStreamsuporta apenas png, jpg e gif. Por outro lado, o pipeline de conteúdo suporta bmp, dds, dib, hdr, jpg, pfm, png, ppm e tga. Se você tentar carregar um formato usuportado, Texture2D.FromStreamreceberá um InvalidOperationExceptionpouco de informações adicionais.

Eu realmente precisava de suporte bmp no meu mecanismo, então, para esse caso em particular, encontrei uma solução alternativa que parece funcionar bem. Não conheço nenhum dos outros formatos. O problema com o meu método é que você precisa adicionar uma referência ao System.Drawingassembly ao seu projeto, porque ele usa GDIs Image.FromStreamque suportam mais formatos do que Texture2D.FromStream.

Se você não se importa com o suporte a bmp, pode facilmente largar essa parte da minha solução e fazer o processamento alfa pré-multiplicado.

Solução - Versão Simples (Mais Lenta)

Primeiro de tudo, aqui está a solução mais simples , se você não se importa com o suporte a bmps. Neste exemplo, o estágio de processamento é realizado inteiramente na CPU. É um pouco mais lento que a alternativa mostrada abaixo (fiz benchmark de ambas as soluções), mas é mais fácil de entender:

public static Texture2D FromStream(GraphicsDevice graphicsDevice, Stream stream)
{
    Texture2D texture = Texture2D.FromStream(graphicsDevice, stream);
    Color[] data = new Color[texture.Width * texture.Height];
    texture.GetData(data);
    for (int i = 0; i != data.Length; ++i)
        data[i] = Color.FromNonPremultiplied(data[i].ToVector4());
    texture.SetData(data);
    return texture;
}

Se você se preocupa com bmps, o que você precisa fazer é carregar a imagem com o GDI primeiro e depois converter em PNG internamente antes de passá-la para Texture2D.FromStream. Aqui está o código que faz isso:

// Load image using GDI because Texture2D.FromStream doesn't support BMP
using (Image image = Image.FromStream(stream))
{
    // Now create a MemoryStream which will be passed to Texture2D after converting to PNG internally
    using (MemoryStream ms = new MemoryStream())
    {
        image.Save(ms, System.Drawing.Imaging.ImageFormat.Png);
        ms.Seek(0, SeekOrigin.Begin);
        texture = Texture2D.FromStream(_graphicsDevice, ms);
    }
}

Solução - Versão complexa (mais rápida)

Por fim, a abordagem usada em meus projetos é usar a GPU para fazer o processamento. Neste método, você precisa criar um destino de renderização, configurar corretamente alguns estados de mesclagem e desenhar a imagem duas vezes com um SpriteBatch. No final, reviso todo o RenderTarget2D e clono o conteúdo em um objeto Texture2D separado porque o RenderTarget2D é volátil e não sobrevive a coisas como alterar o tamanho do backbuffer para que seja mais seguro fazer uma cópia.

O engraçado é que, mesmo com tudo isso, nos meus testes essa abordagem foi executada cerca de três vezes mais rápido que a da CPU. Portanto, é definitivamente mais rápido do que passar por cima de cada pixel e calcular a cor você mesmo. O código é um pouco longo, então eu o coloquei em um pastebin:

http://pastie.org/3651642

Basta adicionar essa classe ao seu projeto e usá-la da maneira mais simples:

TextureLoader textureLoader = new TextureLoader(GraphicsDevice);
Texture2D texture = textureLoader.FromFile("Content/texture.png");

Nota: Você só precisa criar uma TextureLoaderinstância para o jogo inteiro. Também estou usando a correção BMP, mas você pode removê-la se não precisar e obter um monte de desempenho ou apenas deixar o needsBmpparâmetro como falso.

David Gouveia
fonte
uau, isso é ótimo! Isso vai me ajudar muito :) Muito obrigado David, eu aprecio isso.
krolth
+1 Na verdade, eu estava usando FromStreamum fluxo de memória contendo um bitmap de 32 bits (salvo em png), assim como no outro exemplo, mas esse método não criou uma textura pré-multiplicada. A pré-multiplicação explícita de cada cor fez o truque, obrigado.
Groo
3

Eu acho que a maioria das equipes confirmará as alterações do xnb (bem, todas as mudanças realmente, inclusive o xnb) no servidor svn (que pode ser configurado gratuitamente) e permitirá que outras pessoas (o artista etc.) atualizem suas próprias cópias de trabalho.

De fato, esse seria um bom mecanismo para o artista controlar a versão original (pre-xnb) art. Ele iria confirmar as alterações, você atualizaria sua cópia de trabalho, construiria (tornando-o um xnb no processo), confirmará suas alterações, ele atualiza a cópia de trabalho do seu trabalho e todo mundo tem todas as alterações. (Você tem a última arte bruta, ele tem o xnb (s).

Isso escala muito bem também.

Steve H
fonte
Então você está dizendo que acha que isso não pode ser feito? Sua sugestão é adicionar controle de versão ao XNB e sprites, já estamos fazendo isso. Mas não gosto porque me torno o gargalo para eles. Eu já escrevi uma ferramenta para eles editarem as animações e eles podem experimentá-las no jogo. Mas se eles fizerem alterações na planilha, precisam esperar até que eu a construa para que possam vê-la. Como você pode imaginar, se eles cometerem um erro, precisam fazê-lo novamente.
krolth
@krolth É muito importante ter seus artistas com o VS Express e o XNA como parte da configuração para trabalhar em um projeto? Acho que, nesse ponto, a desvantagem de ter que escrever um guia e ajudar as pessoas a superá-lo superará em muito a produtividade que você está perdendo agora, já que os artistas não conseguem ver seu trabalho no mecanismo. Para otimizar o processo, forneça a eles um arquivo .BAT no qual eles possam clicar duas vezes para recompilar tudo sem precisar abrir o IDE. E se eles estão executando apenas o OS X, bem, merda difícil. Bem-vindo ao desenvolvedor do jogo. Eles podem confirmar seus sprites e aguardar os próximos XNBs comprometidos.
michael.bartnett
não é grande coisa, apenas uma dor. Mas acho que terá que fazer. Obrigado a todos por suas respostas / comentários!
krolth
1

Continuei investigando isso e publicarei isso em benefício de alguém que tenha a mesma pergunta.

Parece que a resposta certa para isso é pular o ContentPipeline e usar Texture2D.FromStream para carregar as texturas em tempo de execução. Esse método funciona bem em um PC e, mesmo com um pequeno impacto no desempenho, isso é algo que eu posso otimizar quando estiver mais próximo da data de lançamento.

Por enquanto, ter a capacidade de modificar dinamicamente o conteúdo do editor e do jogo é exatamente o que eu preciso. Depois que o conteúdo estiver congelado, eu posso otimizar isso retornando ao ContentPipeline.

krolth
fonte
Você testou isso corretamente? Da minha experiência Texture2D.FromStreampor si só não é suficiente. A razão para isso é que, uma vez que a versão 4 do XNA trabalha com texturas alfa pré-multiplicadas, e enquanto o pipeline de conteúdo cuida desse processamento automaticamente para você, Texture2D.FromStreamisso não ocorre , portanto você provavelmente terá problemas ao desenhar sprites com transparência. Posso postar uma solução funcional, se você quiser.
David Gouveia
Além disso, Texture2D.FromStreamnão oferece suporte ao carregamento de .BMParquivos enquanto o pipeline de conteúdo suporta. Isso é algo que provavelmente o afastaria se você usasse algum .BMPativo anteriormente e depois o trocasse Texture2D.FromStream. Eu também tenho uma solução alternativa para essa limitação. Vou apenas postar.
David Gouveia
0

Confira este projeto .

Isso permitirá que seu artista crie XNB a partir de praticamente qualquer coisa que o pipeline de conteúdo XNA padrão suporte. A estrutura XNA redistribuível ainda é necessária, embora seu artista não precise do Visual Studio.

ClassicThunder
fonte
Obrigado pelo ponteiro. Olhando para o código, parece uma modificação no exemplo da Microsoft que eu fiz referência. Depende da instalação completa do XNA Game Studio (consulte ContentBuilder.cs). Por que você acha que não?
krolth
Eu acho que não. Se você quiser usar o pipeline de conteúdo, precisará do estúdio completo do jogo. No entanto, o projeto impede que seus artistas tenham que usar o Visual Studio. As únicas outras alternativas são reescrever o pipeline de conteúdo.
ClassicThunder