Modelos de interface do usuário para o código


17

Meu designer de interface do usuário criou um adorável PSD de Photoshop e tudo bonito. O maior problema que estou tendo é converter algumas das fontes mais elegantes usadas em algo renderizável no jogo. Existe uma maneira de converter esses estilos de fonte no Photoshop em uma fonte de bitmap de algum tipo?

Eu preciso ser capaz de renderizar texto como este dentro do meu código:

insira a descrição da imagem aqui



Muito obrigado pela recompensa, mas notei que você não a marcou como a resposta aceita. Você está insatisfeito com a resposta? Você esperava um tipo diferente de resposta? Eu montei GUIs de jogos como parte do meu trabalho diário, então acho que posso responder às suas perguntas. Sinta-se livre para pedir esclarecimentos ou explicações mais completas, se necessário.
Panda Pyjama

Apenas uma supervisão da minha parte. :) Eu corrigi isso agora. Aproveite e obrigado!
Vaughan Empunhaduras

Respostas:


18

Ok, você terá que me perdoar por não fornecer um código XNA específico, porque não conheço essa plataforma, mas o que vou dizer deve funcionar em qualquer mecanismo de jogo que permita desenhar sprites.

Fontes não é o seu único problema, por isso vou dar um conselho e depois responder sua pergunta. Com essas duas coisas, você poderá estabelecer um relacionamento amoroso com seu designer de GUI, e os dois poderão criar jogos muito felizes.

A primeira coisa é que você vai se sentar com seu designer e pedir a ela que lhe forneça dois conjuntos de arquivos. O primeiro é um conjunto de arquivos transparentes que compõem sua GUI (idealmente no formato PSD ou DXT). Para cada botão, rótulo fixo, plano de fundo, borda e caixa de texto, você receberá um arquivo (você também pode fazer atlas de textura, mas eu recomendo que você faça isso depois de montar sua GUI e, em seguida, ajustar as coordenadas de origem ao fazer a blitagem). O texto não estático deve ser deixado de fora neste momento (revisitarei isso mais tarde).

A segunda coisa que você terá é o design da GUI, desta vez no formato Photoshop. Para esse arquivo, você solicitará ao seu designer que faça o design completo da GUI, usando apenas os arquivos que ele forneceu anteriormente.

Ela então colocará cada elemento da GUI em uma camada separada, sem nenhum efeito. Você vai dizer para ela fazer esse pixel perfeito, porque os locais onde ela colocará tudo é onde tudo estará realmente no jogo finalizado.

Quando você conseguir isso, para cada camada, pressione Ctrl-T e, no painel Informações (F8), você anotará as coordenadas X e Y de cada elemento. Verifique se suas unidades estão definidas para pixels (Preferências-> Unidades e réguas-> Unidades). Essas são as posições que você usará ao desenhar seus sprites.

Agora, para fontes, como você deve saber claramente agora, não será possível fazer com que suas fontes sejam exatamente da mesma maneira que as vê no Photoshop usando APIs de renderização de texto. Você precisará pré-renderizar seus glifos e depois montar programaticamente seus textos. Existem muitas maneiras de fazer isso, e mencionarei a que uso.

A primeira coisa é renderizar todos os seus glifos em um ou mais arquivos. Se você se preocupa apenas com o inglês, uma textura para todos os glifos será suficiente, mas se quiser ter um conjunto de caracteres mais extenso, poderá usar vários arquivos. Apenas verifique se todos os glifos que você deseja estão disponíveis na fonte escolhida pelo designer.

Portanto, para renderizar os glifos, você pode usar os recursos de System.Drawingpara obter as métricas de fonte e desenhar seus glifos:

Color clearColor = Color.Transparent;
Color drawColor = Color.White;
Brush brush = new SolidBrush(drawColor);
TextRenderingHint renderingType = TextRenderingHint.AntiAliasGridFit; // Antialias is fine, but be careful with ClearType, which can blergh your renders when you apply effects
StringFormat stringFormat = StringFormat.GenericTypographic;
string fileNameFormat = "helvetica14_{0}.png";
string mapFileFormat = "helvetica14.txt";
string fontName = "Helvetica";
string fontPath = @"c:\windows\fonts\helvetica.ttf";
float fontSize = 14.3f;
int spacing = 2;

Font font = new Font(fontName, fontSize);
int x = 0;
int y = 0;
int width = 1024; // Force a maximum texture size
int height = 1024;
StringBuilder data = new StringBuilder();
int lineHeight = 0;
int currentPage = 1;
var families = Fonts.GetFontFamilies(fontPath);
List<char> codepoints = new List<char>();
HashSet<char> usedCodepoints = new HashSet<char>();
foreach (FontFamily family in families)
{
    var typefaces = family.GetTypefaces();
    foreach (Typeface typeface in typefaces)
    {
        GlyphTypeface glyph;
        typeface.TryGetGlyphTypeface(out glyph);
        foreach (KeyValuePair<int, ushort> kvp in glyph.CharacterToGlyphMap) // Render all available glyps
        {
            char c = (char)kvp.Key;
            if (!usedCodepoints.Contains(c))
            {
                codepoints.Add(c);
                usedCodepoints.Add(c);
            }
        }
    }
}
Bitmap bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(bitmap);
g.Clear(clearColor);
g.TextRenderingHint = renderingType;

foreach (char c in codepoints)
{
    string thisChar = c.ToString();
    Size s = g.MeasureString(thisChar, font); // Use this instead of MeasureText()
    if (s.Width > 0)
    {
        s.Width += (spacing * 2);
        s.Height += (spacing * 2);
        if (s.Height > lineHeight)
            lineHeight = s.Height;
        if (x + s.Width >= width)
        {
            x = 0;
            y += lineHeight;
            lineHeight = 0;
            if (y + s.Height >= height)
            {
                y = 0;
                g.Dispose();
                bitmap.Save(string.Format(fileNameFormat, currentPage));
                bitmap.Dispose();
                bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
                g = Graphics.FromImage(bitmap);
                g.Clear(clearColor);
                g.TextRenderingHint = renderingType;
                currentPage++;
            }
        }
        g.DrawString(thisChar, font, brush, new PointF((float)x + spacing, (float)y + spacing), stringFormat);
        data.AppendFormat("{0} {1} {2} {3} {4} {5}\n", (int)c, currentPage, x, y, s.Width, s.Height);
        x += s.Width;
    }
}
g.Dispose();
bitmap.Save(string.Format(fileNameFormat, currentPage));
bitmap.Dispose();
File.WriteAllText(mapFileFormat, data.ToString());

Com isso, você desenhou glifos brancos sobre um fundo transparente em vários arquivos PNG e criou um arquivo de índice que informa sobre cada ponto de código, em qual arquivo o glifo está localizado, sua localização e dimensões. Observe que eu também coloquei dois pixels adicionais para separar cada glifo (para acomodar outros efeitos)

Agora, para cada um desses arquivos, você o coloca no photoshop e faz todos os filtros que deseja. Você pode definir cores, bordas, sombras, contornos e qualquer outra coisa que desejar. Apenas verifique se os efeitos não fazem os glifos se sobreporem. Nesse caso, ajuste o espaçamento, renderize novamente, enxágue e repita. Salve como PNG ou DXT e, junto com o arquivo de índice, coloque tudo no seu projeto.

O texto do desenho deve ser muito simples. Para cada caractere que você deseja imprimir, encontre sua localização usando o índice, desenhe-o, avance a posição e repita. Você também pode ajustar o espaçamento, kerning (complicado), espaçamento vertical e até coloração. Em lua:

function load_font(name)
    local font = {}
    font.name = name
    font.height = 0
    font.max_page = 0
    font.glyphs = {}
    font.pages = {}
    font_definition = read_all_text("font/" .. name .. ".txt")

    for codepoint, page, x, y, width, height in string.gmatch(font_definition, "(%d+) (%d+) (%d+) (%d+) (%d+) (%d+)") do
        local page = tonumber(page)
        local height_num = tonumber(height)
        if height_num > font.height then
            font.height = height_num
        end
        font.glyphs[tonumber(codepoint)] = { page=tonumber(page), x=tonumber(x), y=tonumber(y), width=tonumber(width), height=height_num } 
        if font.max_page < page then
            font.max_page = page
        end
    end

    for page = 1, font.max_page do
        font.pages[page] = load_image("font/" .. name .. "_" .. page .. ".png")
    end

    return font
end

function draw_text(font, chars, range, initial_x, initial_y, width, color, spacing)
    local x = initial_x - spacing
    local y = initial_y - spacing
    if range == nil then
        range = { from=1, to=#chars }
    end
    for i = 1, range.to do
        local char = chars[i]
        local glyph = font.glyphs[char]
        if char == 10 then -- line break
            x = initial_x - spacing
            y = y + ((font.height - (spacing * 2)) * 1.4)
        elseif glyph == nil then
            if unavailable_glyphs[char] == nil then
                unavailable_glyphs[char] = true
            end
        else
            if x + glyph.width - spacing > initial_x + width then
                x = initial_x - spacing
                y = y + ((font.height - (spacing * 2)) * 1.4)
            end
            if i >= range.from then
                draw_sprite(font.pages[glyph.page], x, y, glyph.x, glyph.y, glyph.width, glyph.height, color)
            end
            x = x + glyph.width - (spacing * 2)
        end
    end
end

E lá vai você. Repita o procedimento para todas as outras fontes (e também o tamanho ideal)

Editar : eu mudei o código para usar em Graphics.MeasureStringvez de, TextRenderer.MeasureText()porque ambos usam sistemas de medição diferentes e podem levar a inconsistências entre o glifo medido e o desenhado, especialmente com glifos pendentes encontrados em algumas fontes. Mais informações aqui .


2
Por que recebi um voto negativo sobre isso? Se você deseja votar, por favor, pelo menos, faça alguns comentários sobre o que fiz de errado, para que eu possa corrigi-lo da próxima vez.
Panda Pyjama

Eu não. Esta é uma excelente resposta detalhada. +1 :-)
Kromster diz apoio Monica 08/04

6

Bem, como alguém disse, no XNA, o spritefont faz o trabalho pesado para você. no site do clube de criadores, existe um exportador de fontes de bitmap que exporta em imagens de fontes no estilo XNA. ( http://xbox.create.msdn.com/en-US/education/catalog/utility/bitmap_font_maker ) Em seguida, você pode abri-lo no photoshop ou qualquer outra coisa e torná-la bonita. A partir daí, você adiciona a textura ao seu projeto de conteúdo e, no tipo de conteúdo, selecione a textura da fonte do sprite. No seu código, você o carrega como uma fonte sprite normal

(Exemplo: http://www.youtube.com/watch?v=SynGWrWYUQI )


O problema é que não sei exatamente como é possível pegar os estilos de uma fonte do Photoshop e aplicá-los a uma textura como esta.
Vaughan Empunhaduras

Aqui, vou fazer um vídeo
CobaltHex


O BMFont faz a mesma coisa, mas na minha opinião é um programa melhor. angelcode.com/products/bmfont
Cypher

eh, exportando um spritefont básico, você pode personalizá-lo como quiser no photoshop
CobaltHex

4

A solução é bastante simples e é usada por um grande número de jogos. Tudo o que você precisa fazer é tratar suas fontes como se fossem sprites.

Peça ao seu designer que desenhe toda a gama de números e letras que você deseja usar no seu jogo. Em seguida, renderize-as para imagens estáticas de tamanhos variados (.png, .bmp, qualquer que seja o formato que você use). Você terá algo parecido com isto:

insira a descrição da imagem aqui

Agora tudo o que você precisa fazer é renderizar cada letra da sua "folha de fontes" como se fosse um sprite na tela. Certamente ajuda a criar uma classe auxiliar para traduzir entre strings e sprites.

Minha implementação é mais complexa, mas útil. A folha de fontes é construída como a imagem acima, com várias fontes, tudo em um arquivo .png. Eu tenho um .iniarquivo que mapeia a letra de cada fonte para uma posição na folha, juntamente com sua largura e altura. Isso deixa meu designer (e eu) enlouquecendo, criando fontes legais sem precisar tocar em nenhum código. Ao desenhar uma corda para a tela, eu tenho um método que procura o tipo de letra e a charpartir do arquivo .ini para obter a posição e os limites da carta da folha fonte, então eu apenas chamar a Texture2Dcom SpriteBatch.Draw()usando a fonte Rectangleda letra em questão.


2

A interface do usuário é um assunto vasto e complexo. A renderização de fontes é uma parte difícil. Aconselho que você use uma biblioteca já existente que permita exibir conteúdo em Flash ou HTML no jogo, em vez de refazer tudo.

O Awesomium parece promissor e deve funcionar com o XNA , para que você possa experimentá-lo. É gratuito para jogos não comerciais ou se você não estiver ganhando muito dinheiro:

Gratuito para empresas independentes
    Se sua empresa faturou menos de US $ 100 mil no ano passado, você se qualificou!
Grátis para uso não comercial
Grátis para avaliação e desenvolvimento


Essa é uma ideia bastante bacana - acho que ela abstrai alguns dos códigos da interface do usuário que também tenho que escrever. Vou dar uma chance. Obrigado!
Vaughan Hilts

Como é uma porta do mecanismo Webkit, você deve poder executar a maioria dos estilos de texto necessários com CSS, incluindo contornos de traços, sombras projetadas e gradientes.
31412 ChrisC

que é a maneira mais complexificar para fontes simplesmente desenho
CobaltHex

1
@CobaltHex Eu já vi essa atitude tantas vezes e tantas UIs estragadas que derivaram de uma inicial "sim, vamos criar uma UI do zero, deve ser fácil, vamos reinventar a roda enquanto estamos nisso" que eu cresci cansado disso. Como exercício, verifique a imagem da maquete do OP e imagine que você precisa renderizar uma interface do usuário inteira no mesmo estilo em várias plataformas, resoluções de tela e proporções. Agora, a necessidade de uma interface do usuário adequada fica clara, por isso tomei a liberdade de postar esta resposta, mesmo que não seja uma solução direta para o problema em questão.
precisa saber é o seguinte

Não estou dizendo que você faça uma UI a partir do zero, mas se você quiser apenas para desenhar uma fonte, a inserção de um motor de renderização toda é um pouco exagerado
CobaltHex

1

Se você está procurando efeitos mais sofisticados que o importador simples .spritefont, pode tentar procurar um "gerador de fontes de bitmap".

Pessoalmente, prefiro este: http://www.ironstarmedia.co.uk/2010/01/free-game-dev-utility-fancy-bitmap-font-generator/

Algumas outras idéias:

  • Use um gerador de fontes de bitmap e edite o bitmap que ele produz ainda mais no photoshop etc.
  • Coloque o máximo de texto estático possível nos sprites pré-desenhados. Em seguida, preencha apenas itens como etiquetas de jogo e marque totais usando .spritefonts ou fontes geradas a partir de um gerador de fontes de bitmap.

Eu definitivamente vi isso (e é muito legal, a propósito), mas esperava transformar diretamente meus estilos de texto do Photoshop em fontes de bitmap utilizáveis.
Vaughan Hilts

0

XNAfaz todo o trabalho duro para você. Com Spritefontvocê, você pode facilmente converter um arquivo de fonte em sua máquina no tipo de planilha de sprite que você está perguntando, definindo um arquivo XML.

Depois de adicionar o arquivo XML ao seu projeto de conteúdo, carregue-o com o ContentManager:

ContentManager.Load<SpriteFont>(@"MyFont");

Aqui está um exemplo de um arquivo .spritefont do meu projeto de conteúdo:

<?xml version="1.0" encoding="utf-8"?>
<!--
This file contains an xml description of a font, and will be read by the XNA
Framework Content Pipeline. Follow the comments to customize the appearance
of the font in your game, and to change the characters which are available to draw
with.
-->
<XnaContent xmlns:Graphics="Microsoft.Xna.Framework.Content.Pipeline.Graphics">
<Asset Type="Graphics:FontDescription">

    <!--
Modify this string to change the font that will be imported.
//TODO: A different font should be chosen before shipping for licensing reasons
-->
    <FontName>Pericles</FontName>

    <!--
Size is a float value, measured in points. Modify this value to change
the size of the font.
-->
    <Size>8.5</Size>

    <!--
Spacing is a float value, measured in pixels. Modify this value to change
the amount of spacing in between characters.
-->
    <Spacing>0</Spacing>

    <!--
UseKerning controls the layout of the font. If this value is true, kerning information
will be used when placing characters.
-->
    <UseKerning>true</UseKerning>

    <!--
Style controls the style of the font. Valid entries are "Regular", "Bold", "Italic",
and "Bold, Italic", and are case sensitive.
-->
    <Style>Bold</Style>

    <!--
If you uncomment this line, the default character will be substituted if you draw
or measure text that contains characters which were not included in the font.
-->
    <DefaultCharacter>@</DefaultCharacter>

    <!--
CharacterRegions control what letters are available in the font. Every
character from Start to End will be built and made available for drawing. The
default range is from 32, (ASCII space), to 126, ('~'), covering the basic Latin
character set. The characters are ordered according to the Unicode standard.
See the documentation for more information.
-->
    <CharacterRegions>
        <CharacterRegion>
            <Start>&#32;</Start>
            <End>&#126;</End>
        </CharacterRegion>
        <CharacterRegion>
            <Start>&#9;</Start>
            <End>&#9;</End>
        </CharacterRegion>
    </CharacterRegions>
</Asset>
</XnaContent>

1
O problema é que essas são fontes TrueType simples. Eu preciso de fontes de bitmap ou algo parecido com todas as minhas sombras, brilhos e similares ainda aplicadas.
Vaughan Hilts

Veja minha edição, fontes como estas: i.imgur.com/VaBpQ.png
Vaughan Hilts

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.