Antecedentes
Hago desarrollo de juegos como un hobby, y estoy buscando una mejor manera de diseñarlos. Actualmente, estoy usando un enfoque OOP estándar (he estado haciendo desarrollo empresarial durante 8 años, por lo que viene de manera natural). Tomemos por ejemplo un "malo"
public class Baddie:AnimatedSprite //(or StaticSprite if needed, which inherit Sprite)
{
//sprite base will have things like what texture to use,
//what the current position is, the base Update/Draw/GetInput methods, etc..
//an AnimatedSprite contains helpers to animated the player while
//a StaticSprite is just one that will draw whatever texture is there
}
El problema
Digamos que estoy haciendo un juego de plataformas en 2D, y necesito al malo para poder saltar. Por lo general, lo que hago es agregar el código apropiado en los métodos Update / GetInput. Entonces, si necesito hacer que el jugador se arrastre, se agache, suba, etc., el código irá allí.
Si no tengo cuidado, esos métodos se desordenan, así que termino creando pares de métodos como este
CheckForJumpAction(Input input)
y DoJump()
CheckforDuckAction(Input input)
y DoDuck()
entonces GetInput parece
public void DoInput(Input input)
{
CheckForJumpAction(input);
CheckForDuckAction(input);
}
y la actualización se ve así
public void Update()
{
DoJump();
DoDuck();
}
Si voy y creo otro juego donde el jugador necesita saltar y agacharse, generalmente entro en un juego que tiene la funcionalidad y lo copio. Desordenado, lo sé. Por eso estoy buscando algo mejor.
¿Solución?
Realmente me gusta cómo Blend tiene comportamientos que puedo adjuntar a un elemento. He estado pensando en usar el mismo concepto en mis juegos. Así que veamos los mismos ejemplos.
Crearía un objeto de comportamiento base
public class Behavior
{
public void Update()
Public void GetInput()
}
Y puedo crear comportamientos usando eso. JumpBehavior:Behavior
yDuckBehavior:Behavior
Luego puedo agregar una colección de comportamientos a la base de Sprite y agregar lo que necesito a cada entidad.
public class Baddie:AnimatedSprite
{
public Baddie()
{
this.behaviors = new Behavior[2];
this.behaviors[0] = new JumpBehavior();
//etc...
}
public void Update()
{
//behaviors.update
}
public GetInput()
{
//behaviors.getinput
}
}
Así que ahora, si quisiera usar Jump and Duck en muchos juegos, simplemente puedo traer los comportamientos. Incluso podría hacer una biblioteca para los comunes.
¿Funciona?
Lo que no puedo entender es cómo compartir el estado entre ellos. Mirando a Jump and Duck, ambos afectan no solo la porción actual de la textura que se está dibujando, sino también el estado del jugador. (Jump aplicará una cantidad decreciente de fuerza ascendente con el tiempo, mientras que el pato solo detendrá el movimiento, cambiará la textura y el tamaño de colisión del malo.
¿Cómo puedo unir esto para que funcione? ¿Debo crear propiedades de dependencia entre los comportamientos? ¿Debería tener cada comportamiento saber sobre el padre y modificarlo directamente? Una cosa que pensé fue poder pasar un delegado a cada comportamiento para que se ejecute cuando se activa.
Estoy seguro de que hay más problemas que estoy revisando, pero el propósito es que pueda reutilizar fácilmente estos comportamientos entre juegos y entidades en el mismo juego.
Así que te lo entrego a ti. ¿Le gustaría explicar cómo / si esto se puede hacer? Tienes una mejor idea? Soy todo oídos.
Respuestas:
Echa un vistazo a esta presentación. Suena bastante parecido al tipo de patrón que estás buscando. Este patrón admite comportamientos y propiedades vinculables. No creo que la presentación lo mencione, pero también puede crear eventos adjuntos. Esta idea es similar a las propiedades de dependencia utilizadas en WPF.
fuente
Para mí, esto parece un caso casi de libro de texto para usar el Patrón de estrategia . En este patrón, sus comportamientos genéricos se pueden definir en interfaces que le permitirán intercambiar diferentes implementaciones de comportamientos en tiempo de ejecución (piense en cómo un encendido puede afectar la capacidad de saltar o correr de su personaje).
Por ejemplo (artificial):
Ahora puedes implementar varios tipos de saltos que tu personaje puede usar, sin conocer necesariamente los detalles sobre cada salto específico:
Cualquier personaje que desee tener la capacidad de saltar puede ahora contener una referencia a un IJumpable y puede comenzar a consumir sus diversas implementaciones
Entonces su código puede verse así:
Ahora puede reutilizar fácilmente estos comportamientos, o incluso extenderlos más tarde, ya que desea agregar más tipos de saltos, o crear más juegos construidos en una sola plataforma de desplazamiento lateral.
fuente
Eche un vistazo a la arquitectura DCI, una versión interesante de la programación OO que puede ayudar.
La implementación de una arquitectura de estilo DCI en C # requiere el uso de clases de dominio simples y el uso de objetos de contexto (comportamientos) con métodos de extensión e interfaces para permitir que las clases de dominio colaboren bajo diferentes roles. Las interfaces se utilizan para marcar los roles necesarios para un escenario. Las clases de dominio implementan las interfaces (roles) que se aplican a su comportamiento previsto. La colaboración entre los roles tiene lugar en los objetos de contexto (comportamiento).
Podría crear una biblioteca de comportamientos y roles comunes que se puedan compartir entre objetos de dominio de proyectos separados.
Ver DCI en C # para ejemplos de código en C #.
fuente
¿Qué pasa con pasar un objeto de contexto a un comportamiento que proporciona métodos para cambiar el estado de los objetos / objetos afectados por un comportamiento? Ahora, si un objeto tiene varios comportamientos, cada uno de ellos se llama en un ciclo que les da la oportunidad de cambiar el contexto. Si una determinada modificación de una propiedad descarta / restringe la modificación de otras propiedades, el objeto de contexto puede proporcionar métodos para establecer algún tipo de indicadores para indicar este hecho, o simplemente puede negar modificaciones no deseadas.
fuente