Estado de Carrera
public abstract class State
{
public bool isStateFinished;
public abstract void StartState();
public virtual void UpdateState()
{
}
public abstract void EndState();
public virtual void FinishState()
{
isStateFinished = true;
}
}
Cambios y Adiciones
Variable isStateFinished:
Variable booleana que indica si el estado ha finalizado su ejecución.
Método FinishState:
Nuevo método virtual que establece isStateFinished
en true
.
Al ser virtual, las clases derivadas pueden sobrescribir este método si necesitan realizar acciones adicionales cuando un estado finaliza.
Razones de estos cambios
Los cambios introducen la variable isStateFinished
para rastrear la finalización del estado y el método FinishState
para estandarizar la señalización de finalización del estado, permitiendo que las clases derivadas agreguen lógica personalizada al completar el estado.
Estado de carrera
public class RunState : State
{
Rigidbody2D rb;
Animator animator;
Transform player;
float speed;
Vector2 scale;
Vector2 velocity;
public RunState(Rigidbody2D rb, Animator animator, Transform p, float speed)
{
this.animator = animator;
player = p;
this.speed = speed;
this.rb = rb;
scale = rb.transform.localScale;
velocity = new Vector2();
}
public override void StartState()
{
isStateFinished = false;
animator.SetBool("run", true);
}
public override void UpdateState(float DeltaTime)
{
scale.x = rb.position.x > player.position.x ? -1 : 1;
rb.transform.localScale = scale;
velocity.x = scale.x * speed;
rb.velocity = velocity;
}
public override void EndState()
{
animator.SetBool("run", false);
}
}
Analicemos la clase RunState
, que hereda de la clase State
. Esta clase se encarga de gestionar el comportamiento de carrera de nuestro enemigo. Nos centraremos en las adiciones y cambios realizados en esta clase.
Constructor:
public RunState(Rigidbody2D rb, Animator animator, Transform p, float speed)
;
Este constructor inicializa el RunState
con los componentes necesarios: un Rigidbody2D
para el movimiento, un Animator
para las animaciones, un Transform
para rastrear al jugador y una speed
que determina la velocidad a la que corre el enemigo;
Método StartState:
public override void StartState()
;
Este método establece isStateFinished
en false
y activa la animación de correr configurando el parámetro booleano "run" en el animator a true
.
Método UpdateState:
public override void UpdateState(float DeltaTime)
;
Este método actualiza la posición y orientación del enemigo en función de la posición del jugador;
Ajusta la escala del enemigo para que mire hacia el jugador, calcula la velocidad según la velocidad establecida y aplica esta velocidad al Rigidbody2D
.
Método EndState:
public override void EndState()
Este método detiene la animación de correr estableciendo el parámetro booleano "run" en el animador a false
.
Funcionamiento para Nuestro Enemigo
-
Movimiento y Animación: Mediante el uso de
Rigidbody2D
yAnimator
, elRunState
garantiza que el enemigo pueda moverse de manera fluida y tenga animaciones correspondientes. Esto hace que el comportamiento sea visual y físicamente realista; -
Seguimiento de la Posición del Jugador: El transform del
player
permite que el enemigo siempre se desplace hacia el jugador, lo cual es esencial para comportamientos de persecución o huida; -
Manejo de Dirección: Ajustar el
scale.x
en función de la posición del jugador asegura que el enemigo mire correctamente hacia el jugador mientras corre, aumentando el realismo; -
Actualizaciones Dinámicas de Estado: El método
UpdateState
se llama en cada frame para ajustar continuamente el movimiento y la dirección del enemigo según la posición del jugador, haciendo que el enemigo sea receptivo y dinámico.
Inicialización del Enemigo
private void Start()
{
idle = new IdleState(animator);
runState = new RunState(GetComponent<Rigidbody2D>(), animator, player, speed);
stateManager = new StateManager(idle);
FarAwayTransition = new Transition(() =>
{
return (ThresholdDistance < Vector2.Distance(transform.position, player.position));
}, new StatePourcentage(runState, 50f), new StatePourcentage(dashState, 50f));
toIdleTransition = new Transition(() =>
{
return stateManager.GetCurrentState().isStateFinished;
}, new StatePourcentage(idle, 100));
finishRunning = new Transition(() =>
{
return (ThresholdDistance >= Vector2.Distance(transform.position, player.position));
}, new StatePourcentage(idle, 100));
stateManager.AddStateTransition(idle, FarAwayTransition);
stateManager.AddStateTransition(dashState, toIdleTransition);
stateManager.AddStateTransition(runState, finishRunning);
}
Explicación
Inicialización del Estado:
Las variables idle
y runState
se inicializan con sus respectivos estados. La variable idle
es una instancia de IdleState
con el animador pasado como parámetro, mientras que runState
es una instancia de RunState
que incluye un Rigidbody2D
, un Animator
, un Transform
para el jugador y un valor de velocidad.
Inicialización de StateManager:
stateManager = new StateManager(idle);
El StateManager
se inicializa con el estado idle
como estado inicial.
Definiciones de Transiciones:
-
FarAwayTransition:
FarAwayTransition = new Transition(() => { return (ThresholdDistance < Vector2.Distance(transform.position, player.position)); }, new StatePourcentage(runState, 50f), new StatePourcentage(dashState, 50f));
; Esta transición verifica si el jugador está más lejos queThresholdDistance
; -
toIdleTransition:
toIdleTransition = new Transition(() => { return stateManager.GetCurrentState().isStateFinished; }, new StatePourcentage(idle, 100));
Esta transición verifica si el estado actual ha finalizado. Si es verdadero, transiciona al estadoidle
con un 100% de probabilidad. -
finishRunning:
finishRunning = new Transition(() => { return (ThresholdDistance >= Vector2.Distance(transform.position, player.position)); }, new StatePourcentage(idle, 100));
Esta transición verifica si el jugador está más cerca queThresholdDistance
. Si es verdadero, transiciona al estadoidle
con un 100% de probabilidad.
Añadiendo transiciones al StateManager:
stateManager.AddStateTransition(idle, FarAwayTransition);
stateManager.AddStateTransition(dashState, toIdleTransition);
stateManager.AddStateTransition(runState, finishRunning);
Estas líneas agregan las transiciones definidas al stateManager
.
Razón de esta implementación
Cambios de estado dinámicos:
Las transiciones permiten que el enemigo cambie su comportamiento de manera dinámica según la posición del jugador y el estado de finalización del estado actual, haciendo que el enemigo sea más receptivo e interactivo dentro del entorno del juego.
Transiciones condicionales:
El uso de condiciones (como verificar la distancia o la finalización del estado) garantiza que las transiciones de estado ocurran de manera lógica y adecuada, mejorando el realismo de la jugabilidad.
Transiciones basadas en probabilidad:
El uso de StatePourcentage
permite transiciones basadas en probabilidad, agregando un elemento de imprevisibilidad y variedad al comportamiento del enemigo.
¡Gracias por tus comentarios!