¿Debería presentarse un objeto en un juego 2D?


16

Estoy haciendo un juego de luchador callejero en 2D que no está basado en fichas. Por lo general, las personas recomiendan que las entidades se entreguen a un renderizador que las represente, no a ellas mismas, pero parece que lo inverso es mejor,

¿Por qué es uno mejor que el otro?

Gracias


¿Por qué crees que lo inverso es mejor?

1
@Martin porque el objeto indicará qué mapa de bits usar de todos modos, entonces, ¿por qué no simplemente hacer object-> render ();
jmasterx

Respuestas:


11

Un par de consideraciones:

  • Como mencionó, cada sprite tendría que "insinuar" qué mapa de bits usar, pero si la entidad tiene que representarse a sí misma. ¿Cuál sería esa 'pista'? Si se trata de una referencia a un mapa de bits, hoja de sprites, etc. diferente para cada sprite, entonces podría terminar usando más memoria de la necesaria o tener problemas para administrar esa memoria. Una ventaja de un renderizador separado es que solo tiene una clase responsable de cualquier gestión de activos. Dicho esto, en un juego de lucha tipo SF2, es posible que solo tengas dos sprites;)

  • Como se mencionó en otra parte, cada vez que desee cambiar su API gráfica, debe cambiar el código de todos sus sprites.

  • el renderizado rara vez se realiza sin una referencia a algún contexto gráfico. Entonces, hay una variable global que representa este concepto, o cada sprite tiene una interfaz con render (GraphicalContext ctx). Esto mezcla la API gráfica y la lógica de su juego (que algunas personas encontrarán poco elegante), y puede causar problemas de compilación.

  • Personalmente, encuentro que separar el renderizado de las entidades individuales es un primer paso interesante en la dirección de ver tu juego como un sistema que no necesariamente necesita gráficos en absoluto. Lo que quiero decir es que cuando quitas el renderizado, te das cuenta de que gran parte del juego ocurre en un "mundo no gráfico" donde las coordenadas de las entidades, sus estados internos, etc. es lo que importa. Esto abre la puerta a pruebas automatizadas, sistemas más desacoplados, etc.

En general, tiendo a preferir los sistemas en los que el renderizado lo realiza una clase separada. Eso no significa que sus sprites no puedan tener algunos atributos que están "relacionados gráficamente" (nombre de animación, cuadro de animación, altura x ancho, id de sprite, etc.), si eso hace que el renderizador sea más fácil de escribir o más eficiente.

Y no sé si eso se aplicaría a 3D (donde la noción de mallas y la variable de coordenadas que usaría podrían estar vinculadas a su API 3D; mientras que x, y, h, w es bastante independiente de cualquier 2D API).

Esperando que esto ayude.


11

Desea que el sistema de representación controle lo que se dibuja cuando. Si, en cambio, los sprites tienen el control de la representación, pierdes muchas ganancias de eficiencia y flexibilidad. Soy de la opinión de que tener el sistema de renderizado en control da como resultado un código más limpio.

Algunas ventajas del renderizado centralizado:

  • ordenamiento z:
    si los objetos del juego son responsables de la representación, tendrá que asegurarse de llamarlos en el orden correcto. De lo contrario, los objetos de fondo se pueden dibujar sobre los objetos de primer plano.
    Con el sistema de renderizado en control, puede elegir ordenar todos los objetos de renderizado, detectar superposiciones en el momento del renderizado y simplemente renderizarlos, o simplemente renunciar a ordenar todos juntos. El punto es que la decisión se puede tomar fácilmente ahora.
  • procesamiento por lotes:
    la otra ventaja obvia de permitir que el sistema de procesamiento esté bajo control es el procesamiento por lotes. Una vez más, el sistema de renderizado tiene la opción de procesar por lotes los sprites y compartir la textura. Puede usar el corte de triángulos para representar todo con una sola llamada. Es posible que pueda almacenar en caché algunos cálculos de renderizado. O simplemente podría renderizar cada sprite a su vez sin ninguna de esas cosas elegantes. (Nota: es posible un lote cuando cada objeto se representa a sí mismo, pero el problema es menos eficiente y más complejo).

La forma en que implemento esto en mis juegos es para que los objetos del juego registren los sprites que desean dibujar con el sistema de renderizado. Cuando el objeto ya no quiere que se dibuje, anula el registro del sprite o lo marca como inactivo.

Todo eso dicho. Si es más fácil hacer que los objetos de tu juego se representen por sí mismos, hazlo de esa manera. Es mucho más importante avanzar y obtener algo / cualquier cosa que tener una arquitectura perfecta.


¿Sobre el orden z si los objetos se dibujan a sí mismos no puede un sistema decidir sobre el orden para llamar a su método de dibujo? Me refiero a centralizado vs no centralizado parece no hacer ninguna diferencia sobre el ordenamiento z.
GorillaApe

3

Descargo de responsabilidad: su pregunta no da muchos detalles, por lo que estoy respondiendo con un principio general. Disculpe si entendí mal su uso o 'render'.

Generalmente uso un objeto externo para representar varios actores en una escena como una forma de encapsular propiedades y métodos a nivel de escena fuera de los 'objetos de actor' individuales. Los objetos en la escena solo deben contener métodos y propiedades internos; solo deberían saber qué son ellos mismos y qué hacen. Presumiblemente, serán influenciados por otros objetos en el juego, así como por la entrada del usuario. Esto afectará cómo / si se representan en la pantalla. Un 'objeto director' puede, por ejemplo, traducir la tecla 'w' para saltar, luego decirle al objeto actor .jump (). Tal lógica de nivel de director también puede decirles a los actores que entren o salgan completamente de la escena.

Saludos, David


Pero en ese sentido, ¿no podría el director decir simplemente acton-> setVisible (falso); ?
jmasterx

Incluso en el caso setVisible (falso), es una entidad externa que realiza el renderizado al verificar la variable visible del actor y renderizarla solo si es verdadera.
Nav

Hacer invisible a un actor no lo elimina de la escena. También debe dejar de participar en colisiones, etc.
finnw

3

¿Qué pasa si algún día quieres portar tu juego a una resolución diferente (es decir, iPhone y amigos)? Por lo tanto, una propiedad global sobre los cambios de representación, ¿cómo actualiza fácilmente su código?


3

Lo que usé fue un diseño basado en el observador. Cuando creé una instancia de una clase que quería renderizar, se almacenó un puntero en la clase central de Renderer. Cuando llama RenderFrame(), el renderizador ya tiene todos los objetos existentes que necesita para renderizar y accedió a sus propiedades para hacerlo. Las clases mismas no tenían idea de que iban a ser impartidas en absoluto. Esta API fue agradable, limpia y fácil de usar.


1
+1 Interesante. He usado este enfoque para el sonido mientras uso el patrón de visitante para gráficos. Pensé que esto tenía más sentido para el sonido porque mientras los gráficos y la IA se ejecutan en el mismo reloj, el mezclador de audio se ejecuta en otro, por lo que un modelo de evento es más fácil. Además, no es crítico si un evento de movimiento (que hace que cambie la panorámica / reverberación de un canal de audio) llega unos milisegundos tarde, pero es crítico si un sprite se dibuja en el estado incorrecto.
finnw

2

En general, siempre se trata de lo fácil que es mantener y expandir su código. Mañana te das cuenta de que no te gusta la API de gráficos que estás usando actualmente y quieres cambiar. ¿Tendrá que pasar ahora por todas sus clases de objetos y cambiar todo, o todavía necesita cambiar su código en un punto central del proyecto?

Depende de lo que estén haciendo realmente sus objetos cuando llame a render (). Siempre y cuando solo envuelvan llamadas de método alrededor de su motor de gráficos, está completamente bien, ya que la lógica <-> distinción de gráficos aún se dará.

Por ejemplo, si sus métodos render () son básicamente métodos de conveniencia y se parecen a esto:

void MyClass::render(const Graphics &g)
{
    g.draw(this);
}

o

void MyClass::render()
{
   mySprite->render();
}

o

void MyClass::render()
{
    mySprite->UseShader(thatshader);
    mySprite->render();
}

o cerca de eso, no creo que sea un problema en absoluto.

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.