Manera eficiente de dibujar contornos alrededor de sprites

21

Estoy usando XNA para programar un juego, y he estado experimentando con varias formas de lograr un efecto 'seleccionado' en mis sprites. El problema que tengo es que cada clic que se dibuja en el spritebatch se dibuja con más de un sprite (cada objeto puede estar compuesto por hasta 6 sprites).

Agradecería que alguien me aconsejara sobre cómo puedo lograr agregar un contorno a mis sprites de X píxeles (por lo que el ancho del contorno puede ser de varios números de píxeles enteros).

Gracias por adelantado,

  • Greg
Greg
fuente

Respuestas:

20

Con mucho, la forma más fácil de hacer esto (así que probablemente la mejor manera, a menos que esté realmente limitado para el rendimiento) es tener dos copias de sus sprites.

  • La versión regular
  • Una versión "gorda", sin color, básicamente una versión blanca de su sprite X, muchos píxeles "más gordos" que el original.

Dibuja todo tu objeto usando la versión "gorda", luego dibuja la versión normal en la parte superior.

Al hacer que la versión "gorda" sea blanca, puede usar el tinte de color incorporado de SpriteBatch para cambiar el color de selección dinámicamente.

Para generar su versión "gorda", le recomiendo escribir una extensión de Content Pipeline que pueda tomar automáticamente sus sprites originales, leer su canal alfa, crear un nuevo canal alfa muestreando el canal alfa máximo en la imagen original X-muchos píxeles alrededor de cada píxel, y ajuste RGB = (1,1,1).

Tendrá que asegurarse de que todos sus sprites tengan un borde transparente suficiente para agregar el contorno (puede verificar esto en el procesador de contenido e incluso hacer espacio si es necesario).

Si solo tiene algunos sprites, puede usar un buen editor de imágenes (GIMP, Photoshop) y hacerlo a mano: canal alfa a selección, expandir selección, selección a alfa, rellenar canales de color blanco.

Andrew Russell
fuente
4

Supongo que primero debes dibujar todas las piezas que se oponen a un solo sprite. Entonces creo que tendrías que escribir un sombreador para detectar los bordes del sprite y dibujar un píxel donde sea que encuentre un borde. Espero que ya haya algunos sombreadores para hacer esto, que podría usar o portar.

Iain
fuente
11
Este tipo de sombreador es sorprendentemente molesto de escribir, he oído (lo usamos en uno de nuestros juegos 3D y seguimos corriendo en casos de borde antiestéticos). Una cosa que quizás desee considerar es codificar el contorno directamente en la textura del sprite con un color específico como (0, 255, 255, 0), y solo hacer que el sombreador transforme ese color cuando se selecciona el sprite. Luego, el sombreador es un cambio de color trivial, y tiene un alto nivel de control del artista sobre el contorno y los detalles que desee.
4

Dependiendo de los requisitos, lo que también podría ser efectivo es crear un esquema a pedido para el sprite. Supongo que sus sprites tienen transparencia y tienen una forma irregular en lugar de solo ser rectángulos (aunque esto funcionaría bien para eso, los rectángulos de contorno deberían ser triviales).

when selected:
   outline = new sprite canvas of appropriate size
   for sprite in object:
      # use larger numbers for thicker outlines
      for x in (-1, 0, 1) and y in (-1, 0, 1):
         render sprite mask into canvas at x,y with desired color

Tenga en cuenta que no necesita hacer esto en cada sorteo (aunque supongo que podría hacerlo), solo cree el nuevo sprite de contorno cuando cambie los sprites.

dash-tom-bang
fuente
Sí, esto es lo que también quería sugerir. Representa el sprite enmascarado 1 píxel a la izquierda, derecha, arriba y abajo del sprite original, debajo del sprite existente. Muchos juegos utilizan este método para delinear texto renderizado, o con solo 2 de las 4 posiciones para crear una sombra paralela.
Kaj
1

El enfoque más simple de la fuerza bruta es construir dos copias de cada sprite, una normal y otra resaltada. Luego simplemente cámbielos cuando esté resaltado.

Si tiene memoria de sobra, no hay necesidad de complicarse más que eso. Además, los artistas tienen control total sobre el aspecto cuando se resaltan para que pueda hacer un esquema o cualquier otra cosa que desee.

Wkerslake
fuente
Creo que parte del problema es que el OP dice que cada objeto puede estar hecho de múltiples sprites. Entonces, supongo que el contorno debe ser el contorno del objeto combinado, en lugar de tener un contorno separado alrededor de cada sprite componente.
Chris Howe
1
Chris, no tiene que ser el contorno del objeto combinado, y funcionará bien para un objeto hecho de múltiples sprites. Simplemente haga exactamente lo que dijo wkerslake, pero asegúrese de dibujar los sprites resaltados detrás de todos los sprites normales para ese objeto. De esa manera, no importa de cuántos sprites esté hecho un objeto, el resaltado solo usará un poco más del doble del tiempo de dibujo para ese objeto, que debería ser mucho menor que generar los contornos en el momento del renderizado con los sprites combinados.
AttackingHobo
1

¿Qué tal para cada sprite? También tenga otro sprite que sea un contorno del sprite base. Al dibujar un objeto contorneado, dibuje los sprites base, luego haga una máscara de la representación combinada, luego dibuje los sprites de contorno excluyendo la máscara.

gris
fuente
2
¿Por qué no dibujar primero los sprites de contorno y dibujar los sprites regulares sobre ellos?
Chris Howe
Una razón para no hacer esto (o la sugerencia de Chris) es porque usa el doble de memoria de textura; Otra razón es que el flujo de trabajo del artista es una porquería porque necesita actualizar dos archivos cada vez que cambia un sprite.
2
Bueno, sí, idealmente no tendrías dos activos reales de sprites. Si está utilizando pruebas alfa para sus sprites, podría poner el contorno en el sprite y darle un alfa ligeramente más alto que sus áreas transparentes. Luego, controlando el valor de referencia de la prueba alfa, puede controlar si el contorno es visible o no. Todo lo que estaba diciendo es que si tiene 2 versiones de los sprites (ya sean 2 activos o 2 estados del mismo activo), primero debe dibujar la versión del esquema y luego la versión normal. De esta manera, no necesita sombreadores, máscaras o lo que sea.
Chris Howe
1
La idea de Chris tiene mérito; Además, se puede evitar que el artista actualice 2 archivos (o incluso el mismo activo) si crea una herramienta para generar el alfa para los sprites de esquema que se ejecutan como parte de su canalización de activos / contenido. La memoria de textura puede o no ser un problema, pero los sprites de contorno separados deben ser altamente compresibles DXT; posiblemente 1/6 del tamaño de la textura original en la memoria de video y sin pérdida de fidelidad.
jpaver
1

Algunas soluciones diferentes con diferentes compensaciones.

Más fácil: renderice el objeto usando un color plano varias veces y fluctúe la posición (desplazamiento hacia la izquierda, arriba, abajo, derecha, etc.), esto creará una versión resumida de lo que renderice encima, pero tiene costos de rendimiento y no Permitir bordes gruesos sin muchos renders adicionales. Un borde de uno o dos píxeles puede estar bien con 4x.

Más rápido: preprocese la textura y tenga una copia que ya esté bordeada, o sea solo el borde, o sea una máscara de 8 bits en escala de grises plana que puede colorear en un sombreador. Esto probablemente será rápido a costa de la memoria.

Lo mejor: mi opinión, pero generar una representación de campo de distancia firmado (SDF) de su objeto probablemente sea la mejor solución. Estas texturas pueden ser mucho más pequeñas que la textura de origen y aún capturar datos útiles. Esencialmente, cada píxel codifica qué tan lejos está del objeto utilizado para generarlo. Con estos datos en la mano, puede escribir todo tipo de efectos desde resplandores hasta contornos. El borde podría cambiar en tamaño y color, etc., y sigue siendo un sombreador relativamente barato y solo un dibujo adicional. Lo malo es el herramental y el preprocesamiento.

Aku
fuente
0

No estoy seguro de la eficiencia, pero la forma más fácil que puedo ver sería dibujar una versión más grande del sprite en el color que desea seleccionar primero. Dibuja el sprite encima de eso. Solo verá el borde del primer sprite, dando el efecto de selección.

EDITAR: Sin embargo, como puede ver en los comentarios, esta no es una buena idea.

El pato comunista
fuente
1
Simplemente escalar un sprite no da un esquema verdadero
Iain
2
No creo que lo sea, pero sería fácil.
El pato comunista
2
Muchas cosas son fáciles pero no resuelven el problema. Para sprites con cualquier tipo de silueta interesante, una ampliación producirá basura absoluta.
La ampliación solo se verá bien para objetos cuadrados o circulares sólidos. Si la forma es realmente ancha, alta o tiene huecos, se verá muy mal.
AttackingHobo
3
La ampliación de escala dará mejores resultados que no hacer nada y también sería un paso útil en el camino hacia la "perfección". Si bien los resultados gráficos no serán perfectos, si se tratara de una herramienta interna, podría ser lo suficientemente bueno.
dash-tom-bang
0

Estoy de acuerdo con ampliar el sprite. De lejos, la ruta más fácil, y puede aplicarla para seleccionar CUALQUIER sprite sin tener que crear sprites adicionales especialmente para este propósito.

  • es decir, spriteOutline.Scale = spriteOriginal.Scale * 0.1f; spriteOutline.Color = nuevo color (255, 0, 0, 255);

fuente
0

Reemplace el color del sprite original con el color del contorno (o incluso tinte si lo desea). Renderice este sprite de color plano o teñido cuatro veces con un desplazamiento de 1 píxel: en x, y = (- 1, -1), luego (+ 1, -1), luego (-1, + 1) y luego (+1 , +1). Repita para todos los sprites que componen el objeto.

Después de eso, renderiza los sprites originales en el orden correcto en la parte superior a (0,0).

Ilya Bossov
fuente