Maquetas de UI para el código


17

Mi diseñador de interfaz de usuario ha hecho un encantador PSD de Photoshop de la interfaz de usuario y todo bonito. El mayor problema que tengo es convertir algunas de las fuentes más elegantes utilizadas en algo renderizable en el juego. ¿Hay alguna forma de convertir estos estilos de fuente en Photoshop a una fuente de mapa de bits de algún tipo?

Necesito poder representar texto como este dentro de mi código:

ingrese la descripción de la imagen aquí



Muchas gracias por la recompensa, pero noté que no la marcó como la respuesta aceptada. ¿No estás satisfecho con la respuesta? ¿Esperaba un tipo diferente de respuesta? Reúno las GUI del juego como parte de mi trabajo diario, por lo que creo que puedo responder sus preguntas. No dude en solicitar aclaraciones o explicaciones más detalladas si lo necesita.
Panda Pijama

Solo un descuido de mi parte. :) He corregido esto ahora. Disfruta y gracias!
Vaughan Hilts

Respuestas:


18

De acuerdo, vas a tener que perdonarme por no darte un código XNA específico, porque no conozco esa plataforma, pero lo que voy a decirte debería funcionar en cualquier motor de juego que te permita dibujar sprites.

Las fuentes no son su único problema, por lo que le daré un consejo y luego responderé a su pregunta. Con estas dos cosas, deberías poder hacer una relación amorosa con tu diseñador de GUI, y ambos podrán hacer juegos muy felices.

Lo primero es que te sentarás con tu diseñador y le pedirás que te dé dos conjuntos de archivos. El primero es un conjunto de archivos transparentes que componen su GUI (óptimamente en formato PSD o DXT). Para cada botón, etiqueta fija, fondo, borde y cuadro de texto, obtendrá un archivo (también puede hacer un atlas de texturas, pero le recomiendo que lo haga después de ensamblar su GUI, y luego ajuste sus coordenadas de origen al pulsar). El texto no estático se debe omitir en este punto (lo volveré a ver más adelante).

Lo segundo que obtendrá es el diseño de la GUI real, esta vez en formato Photoshop. Para este archivo, le pedirá a su diseñador que haga todo el diseño de la GUI, utilizando solo los archivos que le entregó anteriormente.

Luego colocará cada elemento de la GUI en una capa separada, sin ningún efecto. Le dirás que haga este píxel perfecto, porque los lugares donde va a poner todo, es donde todo estará realmente en el juego finalizado.

Una vez que obtenga eso, para cada capa, presionará Ctrl-T, y en el panel de Información (F8), tomará nota de las coordenadas X e Y para cada elemento. Asegúrese de que sus unidades estén configuradas en píxeles (Preferencias-> Unidades y reglas-> Unidades). Estas son las posiciones que usará al dibujar sus sprites.

Ahora, para las fuentes, como puede saber claramente ahora, no podrá hacer que sus fuentes se vean exactamente de la misma manera que las ve en Photoshop utilizando las API de representación de texto. Tendrás que pre-renderizar tus glifos y luego ensamblar tus textos programáticamente. Hay muchas formas de hacer esto, y mencionaré la que uso.

Lo primero es representar todos tus glifos en uno o más archivos. Si solo le importa el inglés, una textura para todos los glifos será suficiente, pero si desea tener un conjunto de caracteres más extendido, puede usar varios archivos. Solo asegúrese de que todos los glifos que desee estén disponibles en la fuente que elija su diseñador.

Por lo tanto, para representar los glifos, puede usar las facilidades de System.Drawingpara obtener las métricas de fuente y dibujar sus 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());

Con esto, dibujó glifos blancos sobre un fondo transparente en un montón de archivos PNG e hizo un archivo de índice que le indica para cada punto de código, en qué archivo se encuentra el glifo, su ubicación y dimensiones. Tenga en cuenta que también pongo dos píxeles adicionales para separar cada glifo (para acomodar más efectos)

Ahora, para cada uno de esos archivos, lo pones en Photoshop y haces todos los filtros que quieras. Puede establecer colores, bordes, sombras, contornos y cualquier otra cosa que desee. Solo asegúrate de que los efectos no hagan que los glifos se superpongan. Si es así, ajuste el espacio, vuelva a renderizar, enjuague y repita. Guarde como PNG o DXT y, junto con el archivo de índice, coloque todo en su proyecto.

Dibujar texto debe ser muy simple. Para cada carácter que desee imprimir, encuentre su ubicación utilizando el índice, dibuje, avance la posición y repita. También puede ajustar el espaciado, el interletraje (complicado), el espaciado vertical e incluso el color. En 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

Y ahi tienes. Repita para cualquier otra fuente (y también el tamaño óptimo)

Editar : cambié el código para usarlo en Graphics.MeasureStringlugar de TextRenderer.MeasureText()porque ambos usan diferentes sistemas de medición y pueden generar inconsistencias entre el glifo medido y el dibujado, especialmente con glifos sobresalientes que se encuentran en algunas fuentes. Más información aquí .


2
¿Por qué recibí un voto negativo sobre esto? Si va a votar a favor, al menos dé algunos comentarios sobre lo que hice mal para que pueda solucionarlo la próxima vez.
Panda Pyjama

Yo no. Esta es una excelente respuesta en profundidad. +1 :-)
Kromster dice que apoya a Mónica el

6

Bueno, como alguien más dijo, en XNA, spritefont hace el trabajo pesado por ti. en el sitio web del club de creadores hay un exportador de fuentes de mapa de bits que exporta en imágenes de fuentes de estilo XNA. ( http://xbox.create.msdn.com/en-US/education/catalog/utility/bitmap_font_maker ) Luego, puede abrirlo en photoshop o lo que sea y hacer que se vea bonito. Desde allí, agrega la textura a su proyecto de contenido y, en el tipo de contenido, selecciona la textura de fuente de sprite. En su código, lo carga como una fuente de sprite normal

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


El problema es que no estoy exactamente seguro de cómo se pueden tomar los estilos de una fuente de Photoshop y aplicarlos a una textura como esta.
Vaughan Hilts

Aquí, haré un video
CobaltHex


BMFont hace lo mismo, pero en mi opinión es un mejor programa. angelcode.com/products/bmfont
Cypher

eh, al exportar un spritefont básico, puedes personalizarlo como quieras en photoshop
CobaltHex

4

La solución es bastante simple y es utilizada por una gran cantidad de juegos. Todo lo que tienes que hacer es tratar tus fuentes como si fueran sprites.

Haga que su diseñador dibuje la gama completa de números y letras que desea usar en su juego. Luego, preséntelos en imágenes estáticas de diferentes tamaños (.png, .bmp, cualquier formato que use). Tendrás algo como esto:

ingrese la descripción de la imagen aquí

Ahora todo lo que tiene que hacer es representar cada letra de su "hoja de fuente" como si fuera un sprite en la pantalla. Ciertamente ayuda a construir una clase auxiliar para traducir entre cadenas y sprites.

Mi implementación es más compleja, pero útil. La hoja de fuentes está construida como la imagen de arriba con múltiples fuentes en un solo archivo .png. Tengo un .iniarchivo que asigna la letra de cada fuente a una posición en la hoja, junto con su ancho y alto. Esto permite que mi diseñador (y yo) nos volvamos locos creando fuentes geniales sin tener que tocar ningún código. Al dibujar una cadena en la pantalla, tengo un método que busca la fuente y el chararchivo .ini para obtener la posición y los límites de la letra de la hoja de fuente, luego simplemente dibujo Texture2Dcon SpriteBatch.Draw()la fuente Rectanglede la letra en cuestión.


2

La IU es un tema vasto y complejo. La representación de fuentes es una parte difícil. Te aconsejo que uses una biblioteca ya existente que te permita mostrar contenido Flash o HTML en el juego, en lugar de rehacer todo tú mismo.

Awesomium parece prometedor y debería funcionar con XNA , por lo que podría intentarlo. Es de uso gratuito para juegos no comerciales, o si no está ganando toneladas de dinero:

Gratis para compañías independientes
    Si su compañía ganó menos de $ 100K el año pasado, ¡usted califica!
Gratis para uso no comercial
Gratis para evaluación y desarrollo


Esa es una idea bastante ingeniosa: supongo que también abstrae parte del código de la interfaz de usuario que tengo que escribir. Lo probaré. ¡Gracias!
Vaughan Hilts

Como es un puerto del motor de Webkit, debería poder hacer la mayoría de los estilos de texto que necesita con CSS, incluidos contornos de trazo, sombras paralelas y degradados.
ChrisC

eso es mucho más complejo que simplemente dibujar fuentes
CobaltHex

1
@CobaltHex He visto esta actitud tantas veces y tanta interfaz de usuario estropeada que se derivó de un "sí, hagamos una interfaz de usuario desde cero, debe ser fácil, reinventaremos la rueda mientras estamos en eso" que crecí Enfermo de eso. Como ejercicio, verifique la imagen de la maqueta OP e imagine que tiene que representar una IU completa con el mismo estilo en varias plataformas, resoluciones de pantalla y proporciones. Ahora la necesidad de una interfaz de usuario adecuada se vuelve clara, por eso me tomé la libertad de publicar esta respuesta, incluso si no es una solución directa al problema dado.
Laurent Couvidou

No estoy diciendo que hagas una IU desde cero, pero si solo quieres dibujar una fuente, insertar un motor de renderizado completo es un poco exagerado
CobaltHex

1

Si está buscando efectos más sofisticados que el simple importador .spritefont, puede intentar buscar un "generador de fuentes de mapa de bits".

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

Algunas otras ideas:

  • Use un generador de fuentes de mapa de bits y luego edite el mapa de bits que produce más en Photoshop, etc.
  • Ponga la mayor cantidad de texto estático posible en sprites predibujados. Luego complete solo cosas como gamertags y califique los totales usando .spritefonts o fuentes generadas a partir de un generador de fuentes de mapa de bits.

Definitivamente lo he visto (y, por cierto, es muy bueno), pero esperaba convertir directamente mis estilos de texto de Photoshop en fuentes de mapa de bits utilizables.
Vaughan Hilts

0

XNAhace todo el trabajo duro por ti. Con Spritefontusted puede convertir fácilmente un archivo de fuente en su máquina en el tipo de hoja de sprite que está solicitando definiendo un archivo XML.

Una vez que agregue el archivo XML a su proyecto de Contenido, cárguelo con ContentManager:

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

Aquí hay un ejemplo de un archivo .spritefont de mi proyecto de contenido:

<?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
El problema es que estas son fuentes TrueType simples. Necesito fuentes de mapa de bits o algo similar con todas mis sombras, brillos y similares aún aplicados.
Vaughan Hilts

Vea mi edición, fuente (s) como estas: i.imgur.com/VaBpQ.png
Vaughan Hilts

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.