Por ejemplo, tengo una clase de juego y mantiene una int
que rastrea la vida del jugador. Tengo un condicional
if ( mLives < 1 ) {
// Do some work.
}
Sin embargo, esta condición sigue disparando y el trabajo se realiza repetidamente. Por ejemplo, quiero configurar un temporizador para salir del juego en 5 segundos. Actualmente, seguirá configurándolo en 5 segundos cada fotograma y el juego nunca termina.
Este es solo un ejemplo, y tengo el mismo problema en varias áreas de mi juego. Quiero verificar alguna condición y luego hacer algo una vez y solo una vez, y luego no verificar o hacer el código en la declaración if nuevamente. Algunas posibles soluciones que vienen a la mente son tener una bool
para cada condición y establecer bool
cuándo se dispara la condición. Sin embargo, en la práctica, esto se vuelve muy complicado al manejar muchos bools
, ya que deben almacenarse como campos de clase o estáticamente dentro del método mismo.
¿Cuál es la solución adecuada para esto (no para mi ejemplo, sino para el dominio del problema)? ¿Cómo harías esto en un juego?
Respuestas:
Creo que puede resolver este problema simplemente ejerciendo un control más cuidadoso sobre sus posibles rutas de código. Por ejemplo, en el caso de que esté verificando si el número de vidas del jugador ha caído por debajo de uno, ¿por qué no verificar solo cuando el jugador pierde una vida, en lugar de cada cuadro?
Esto, a su vez, supone que
subtractPlayerLife
solo se llamaría en ocasiones específicas, lo que tal vez resultaría de condiciones en las que debe verificar cada cuadro (colisiones, tal vez).Al controlar cuidadosamente cómo se ejecuta su código, puede evitar soluciones desordenadas como booleanos estáticos y también reducir bit por bit la cantidad de código que se ejecuta en un solo cuadro.
Si hay algo que parece imposible de refactorizar, y realmente necesita verificar solo una vez, entonces el estado (como un
enum
) o los booleanos estáticos son el camino a seguir. Para evitar declarar las estadísticas usted mismo, puede usar el siguiente truco:que haría el siguiente código:
imprimir
something
ysomething else
solo una vez. No produce el código más atractivo, pero funciona. También estoy bastante seguro de que hay formas de mejorarlo, tal vez asegurando mejor nombres de variables únicos. Sin#define
embargo, esto solo funcionará una vez durante el tiempo de ejecución. No hay forma de restablecerlo, y no hay forma de guardarlo.A pesar del truco, recomiendo encarecidamente controlar mejor el flujo de su código primero y usarlo
#define
como último recurso, o solo con fines de depuración.fuente
Esto suena como el caso clásico de usar el patrón de estado. En un estado, verifica su condición de vidas <1. Si esa condición se cumple, pasa a un estado de retraso. Este estado de retraso espera la duración especificada y luego pasa a su estado de salida.
En el enfoque más trivial:
Podría considerar usar una clase de estado junto con un StateManager / StateMachine para manejar el cambio / empuje / transición entre estados en lugar de una declaración de cambio.
Puede hacer que su solución de administración de estado sea tan sofisticada como desee, incluidos varios estados activos, un solo estado padre activo con muchos estados hijos activos que usen alguna jerarquía, etc. Por supuesto, utilice lo que tiene sentido para usted, pero realmente creo que La solución de patrón de estado hará que su código sea mucho más reutilizable y más fácil de mantener y seguir.
fuente
Si le preocupa el rendimiento, el rendimiento de las comprobaciones múltiples no suele ser significativo; lo mismo para tener condiciones bool.
Si está interesado en algo más, puede usar algo similar al patrón de diseño nulo para resolver este problema.
En este caso, supongamos que desea llamar a un método
foo
si al jugador no le quedan vidas. Implementaría esto como dos clases, una que llamafoo
y otra que no hace nada. Vamos a llamarlosDoNothing
yCallFoo
así:Este es un poco un ejemplo "en bruto"; Los puntos clave son:
FooClass
que puede serDoNothing
oCallFoo
. Puede ser una clase base, una clase abstracta o una interfaz.execute
método de esa clase.DoNothing
instancia).CallFoo
instancia).Esto es esencialmente como una combinación de estrategia y patrones nulos.
fuente
if
cheque a laFooClass
propia instancia, por lo que solo se ejecuta una vez, y lo mismo para crear instanciasCallFoo
.