Al crear juegos, a menudo crea el siguiente objeto de juego del que heredan todas las entidades:
public class GameObject{
abstract void Update(...);
abstract void Draw(...);
}
Entonces, al actualizar el ciclo, itera sobre todos los objetos del juego y les da la oportunidad de cambiar de estado, luego, en el siguiente ciclo de dibujo, itera nuevamente sobre todos los objetos del juego y les da la oportunidad de dibujar ellos mismos.
Aunque esto funciona bastante bien en un juego simple con un renderizador avanzado simple, a menudo conduce a algunos objetos gigantes del juego que necesitan almacenar sus modelos, texturas múltiples y, lo peor de todo, un método de dibujo gordo que crea un acoplamiento estrecho entre el objeto del juego, la estrategia de renderizado actual y cualquier clase de renderizado relacionada
Si tuviera que cambiar la estrategia de renderizado de adelante a diferido, tendría que actualizar muchos objetos del juego. Y los objetos del juego que hago no son tan reutilizables como podrían ser. Por supuesto, la herencia y / o la composición pueden ayudarme a combatir la duplicación de código y hacer que sea un poco más fácil cambiar la implementación, pero aún me parece que falta.
Una mejor manera, tal vez, sería eliminar el método Draw de la clase GameObject por completo y crear una clase Renderer. GameObject aún necesitaría contener algunos datos sobre sus imágenes, como con qué modelo representarlo y qué texturas se deben pintar en el modelo, pero cómo se hace esto se dejaría al renderizador. Sin embargo, a menudo hay muchos casos de borde en el renderizado, por lo que, aunque esto eliminaría el acoplamiento estrecho del GameObject con el Renderer, el Renderer aún tendría que estar al tanto de todos los objetos del juego que lo engordarían, todo el conocimiento y estrechamente acoplado. Esto violaría algunas buenas prácticas. Tal vez el diseño orientado a datos podría hacer el truco. Los objetos del juego sin duda serían datos, pero ¿cómo sería impulsado el renderizador por esto? No estoy seguro.
Así que estoy perdido y no puedo pensar en una buena solución. Intenté usar los principios de MVC y en el pasado tuve algunas ideas sobre cómo usar eso en los juegos, pero recientemente no parece tan aplicable como pensaba. Me encantaría saber cómo abordan este problema.
De todos modos, vamos a recapitular, estoy interesado en cómo se pueden lograr los siguientes objetivos de diseño.
- Sin lógica de renderizado en el objeto del juego
- Acoplamiento flojo entre los objetos del juego y el motor de render
- No todo el renders conocedor
- Preferiblemente, el tiempo de ejecución cambia entre motores de render
La configuración ideal del proyecto sería una 'lógica de juego' separada y un proyecto de lógica de renderizado que no necesitan referenciarse entre sí.
Todo este tren de pensamiento comenzó cuando escuché a John Carmack decir en Twitter que tiene un sistema tan flexible que puede cambiar los motores de render en tiempo de ejecución e incluso puede decirle a su sistema que use ambos renderizadores (un procesador de software y un procesador acelerado por hardware) al mismo tiempo para que pueda inspeccionar las diferencias. Los sistemas que he programado hasta ahora ni siquiera son tan flexibles
fuente
Lo que hice para mi propio motor es agrupar todo en módulos. Entonces tengo mi
GameObject
clase y tiene un control para:Entonces tengo una
Player
clase y unaBullet
clase. Ambos derivanGameObject
y se agregan aScene
. PeroPlayer
tiene los siguientes módulos:Y
Bullet
tiene estos módulos:Esta forma de organizar las cosas evita el "Diamante de la Muerte" donde tienes un
Vehicle
, aVehicleLand
y aVehicleWater
y ahora quieres unVehicleAmphibious
. En cambio, tienes unVehicle
y puede tener unModuleWater
y unModuleLand
.Bonificación adicional: puede crear objetos utilizando un conjunto de propiedades. Todo lo que tiene que saber es el tipo de base (Player, Enemy, Bullet, etc.) y luego crear identificadores para los módulos que necesita para ese tipo.
En mi escena, hago lo siguiente:
Update
para todas lasGameObject
manijas.ModuleCollision
identificador.UpdatePost
para todas lasGameObject
manijas para informar sobre su posición final después de la física.m_ObjectsCreated
lista a lam_Objects
lista.Y podría organizarlo más: por módulos en lugar de por objeto. Luego renderizaría una lista de
ModuleSprite
, actualizaría un montónModuleScriptingBase
y colisionaría con una lista deModuleCollision
.fuente
GameObject
(por ejemplo, una forma de representar una "serpiente" de Sprites), deberá crear un hijoModuleSprite
para esa funcionalidad específica (ModuleSpriteSnake
) o agregar un módulo nuevo por completo (ModuleSnake
) Afortunadamente, solo son punteros, pero he visto código dondeGameObject
literalmente hizo todo lo que un objeto podía hacer.