Soy muy nuevo en el desarrollo de juegos, pero no en la programación.
Estoy (de nuevo) jugando con un juego de tipo Pong usando el canvas
elemento de JavaScript .
He creado un Paddle
objeto que tiene las siguientes propiedades ...
width
height
x
y
colour
También tengo un Pong
objeto que tiene propiedades como ...
width
height
backgroundColour
draw()
.
El draw()
método actualmente está reiniciando canvas
y ahí es donde surgió una pregunta.
Si el Paddle
objeto tiene un draw()
método responsable de su elaboración, o si la draw()
del Pong
objeto se encargará de elaborar sus actores (supongo que es el término correcto, por favor, corríjanme si estoy incorrecto).
Pensé que sería ventajoso Paddle
que se dibujara, ya que instanciaba dos objetos, Player
y Enemy
. Si no estuviera en los Pong
's draw()
, necesitaría escribir un código similar dos veces.
¿Cuál es la mejor práctica aquí?
Gracias.
fuente
Respuestas:
Hacer que los actores se dibujen a sí mismos no es un buen diseño, por dos razones principales:
1) viola el principio de responsabilidad única , ya que esos actores presumiblemente tenían otro trabajo que hacer antes de que ingresaran el código de representación en ellos.
2) dificulta la extensión; Si cada tipo de actor implementa su propio dibujo, y necesita cambiar la forma en que dibuja en general , es posible que deba modificar mucho código. Evitar el uso excesivo de la herencia puede aliviar esto hasta cierto punto, pero no por completo.
Es mejor para su renderizador manejar el dibujo. Después de todo, eso es lo que significa ser un renderizador. El método de dibujo del renderizador debe tomar un objeto de "descripción de render", que contiene todo lo que necesita para renderizar una cosa. Referencias a datos de geometría (probablemente compartidos), transformaciones específicas de instancia o propiedades de materiales como el color, etc. Luego dibuja eso, y no le importa qué se supone que es esa descripción de renderizado.
Sus actores pueden conservar una descripción de render que ellos mismos crean. Dado que los actores son típicamente tipos de procesamiento lógico, pueden enviar cambios de estado a la descripción del render según sea necesario; por ejemplo, cuando un actor sufre daños, podría establecer el color de su descripción de render en rojo para indicar esto.
Luego, simplemente puede iterar cada actor visible, incluir sus descripciones de renderizado en el renderizador y dejar que haga lo suyo (básicamente; podría generalizar esto aún más).
fuente
actor.draw(renderer,x,y)
querenderer.draw(actor.getAttribs(),x,y)
.struct RenderDetail { glVAO* vao; int shader; int texture; Matrix4f* transform; }
más o menos para mi renderizador. De esta forma, al procesador no le importa lo que representan los datos, solo los procesa. Según esta información, también puedo decidir si ordenar el orden de las llamadas de extracción para reducir las llamadas generales de la GPU.El patrón de visitante puede ser útil aquí.
Lo que puede hacer es tener una interfaz de Renderer que sepa cómo dibujar cada objeto y un método de "dibujar usted mismo" en cada actor que determina a qué método de renderizador (específico) llamar, por ejemplo
De esta manera, todavía es fácil portar a otra biblioteca gráfica o marco (una vez porté un juego de Swing / Java2D a LWJGL y me alegré mucho de haber usado este patrón en lugar de pasarlo
Graphics2D
).Hay otra ventaja: el renderizador se puede probar por separado de cualquier código de actor
fuente
En su ejemplo, desearía que los objetos Pong implementen draw () y se rendericen.
Aunque no notarás ganancias significativas en un proyecto de este tamaño, separar la lógica del juego y su representación visual (representación) es una actividad que vale la pena.
Con esto quiero decir que tendrías tus objetos de juego que se pueden actualizar () 'pero no tienen idea de cómo se representan, solo se preocupan por la simulación.
Entonces tendrías una clase PongRenderer () que tiene una referencia a un objeto Pong, y luego se encarga de representar la clase Pong (), esto podría implicar renderizar los Paddles, o tener una clase PaddleRenderer para cuidarlo.
La separación de preocupaciones en este caso es bastante natural, lo que significa que sus clases pueden estar menos hinchadas, y es más fácil modificar cómo se representan las cosas, ya no tiene que seguir la jerarquía que hace su simulación.
fuente
Si lo hiciera en el
Pong
's draw (), no necesitaría duplicar el código, simplemente llame a la funciónPaddle
' draw () para cada una de sus paletas. Así que en tuPong
sorteo () tendríasfuente
Cada objeto debe contener su propia función de dibujo que debe ser invocada por el código de actualización del juego o el código de dibujo. Me gusta
Este no es un código, sino solo para mostrar cómo se deben hacer los objetos de dibujo.
fuente