Conteúdo do Curso
Jogo de Luta no Unity
Jogo de Luta no Unity
Estado de Execução
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;
}
}
Alterações e Adições
Variável isStateFinished:
Esta é uma variável booleana que indica se o estado terminou sua execução.
Método FinishState:
Este é um novo método virtual que define isStateFinished
como true
.
Ser virtual significa que classes derivadas podem sobrescrever este método se precisarem realizar ações adicionais quando um estado termina.
Por Que Essas Alterações Foram Feitas
As alterações introduzem a variável isStateFinished
para rastrear a conclusão do estado e o método FinishState
para padronizar o sinal de conclusão do estado, permitindo que classes derivadas adicionem lógica personalizada ao concluir o estado.
Estado de Corrida
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);
}
}
Vamos olhar para a classe RunState
, que herda da classe State
. Esta classe é responsável por lidar com o comportamento de corrida do nosso inimigo. Vamos nos concentrar nas adições e alterações feitas nesta classe.
Construtor:
public RunState(Rigidbody2D rb, Animator animator, Transform p, float speed)
;
Este construtor inicializa o RunState
com os componentes necessários: um Rigidbody2D
para movimento, um Animator
para animações, um Transform
para rastrear o jogador e uma speed
para determinar a velocidade com que o inimigo corre;
Método StartState:
public override void StartState()
;
Este método define isStateFinished
como false
e aciona a animação de corrida ao definir o parâmetro booleano "run" no animator como true
.
Método UpdateState:
public override void UpdateState(float DeltaTime)
;
Este método atualiza a posição e orientação do inimigo com base na posição do jogador;
Ajusta a escala do inimigo para encarar o jogador, calcula a velocidade com base na velocidade, e aplica essa velocidade ao Rigidbody2D
.
Método EndState:
public override void EndState()
Este método para a animação de corrida ao definir o parâmetro booleano "run" no animator como false
.
Como Funciona para Nosso Inimigo
- Movimento e Animação:
Usando Rigidbody2D
e Animator
, o RunState
garante que o inimigo possa se mover suavemente e ter animações correspondentes. Isso torna o comportamento visual e fisicamente realista;
- Rastreamento da Posição do Jogador:
O player
transform permite que o inimigo sempre se mova em direção ao jogador, o que é essencial para comportamentos de perseguição ou fuga;
- Manipulação de Direção:
Ajustar o scale.x
com base na posição do jogador garante que o inimigo encare o jogador corretamente enquanto corre, adicionando realismo;
- Atualizações Dinâmicas de Estado:
O método UpdateState
é chamado a cada quadro para ajustar continuamente o movimento e a direção do inimigo com base na posição do jogador, tornando o inimigo responsivo e dinâmico.
Inicialização do Inimigo
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);
}
Explicação
Inicialização de Estado:
As variáveis idle
e runState
são inicializadas com seus respectivos estados. A variável idle
é uma instância de IdleState
com o animator passado, enquanto runState
é uma instância de RunState
que inclui um Rigidbody2D
, Animator
, Transform
para o jogador e um valor de velocidade.
Inicialização do StateManager:
stateManager = new StateManager(idle);
O StateManager
é inicializado com o estado idle
como estado inicial.
Definições de Transição:
- FarAwayTransition:
FarAwayTransition = new Transition(() => { return (ThresholdDistance < Vector2.Distance(transform.position, player.position)); }, new StatePourcentage(runState, 50f), new StatePourcentage(dashState, 50f));
;
Esta transição verifica se o jogador está mais distante que ThresholdDistance
;
- toIdleTransition:
toIdleTransition = new Transition(() => { return stateManager.GetCurrentState().isStateFinished; }, new StatePourcentage(idle, 100));
Esta transição verifica se o estado atual está concluído. Se verdadeiro, transita para o estadoidle
com 100% de probabilidade. - finishRunning:
finishRunning = new Transition(() => { return (ThresholdDistance >= Vector2.Distance(transform.position, player.position)); }, new StatePourcentage(idle, 100));
Esta transição verifica se o jogador está mais próximo queThresholdDistance
. Se verdadeiro, transita para o estadoidle
com 100% de probabilidade.
Adicionando Transições ao StateManager:
stateManager.AddStateTransition(idle, FarAwayTransition);
stateManager.AddStateTransition(dashState, toIdleTransition);
stateManager.AddStateTransition(runState, finishRunning);
Essas linhas adicionam as transições definidas ao stateManager
.
Por Que Fizemos Assim
Mudanças Dinâmicas de Estado:
As transições permitem que o inimigo mude dinamicamente seu comportamento com base na posição do jogador e no status de conclusão do estado atual, tornando o inimigo mais responsivo e interativo dentro do ambiente do jogo.
Transições Condicionais:
O uso de condições (como verificar distância ou conclusão de estado) garante que as transições de estado ocorram de forma lógica e apropriada, melhorando o realismo do jogo.
Transições Baseadas em Probabilidade:
Usar StatePourcentage
permite transições baseadas em probabilidade, adicionando um elemento de imprevisibilidade e variedade ao comportamento do inimigo.
Obrigado pelo seu feedback!