Inteligências dos Inimigos
Parte da seção Arquitetura de jogos no Unity do Journey de Unity da Coddy. Lição 31 de 50.
O cérebro de um inimigo é uma máquina de estados com um estado para cada comportamento. Este esqueleto caminha entre seus postos, persegue um jogador que se aproxima a até 6 unidades e balança a espada uma vez por segundo quando está perto. Cada estado decide sua própria saída, e o esqueleto mantém os auxiliares que eles compartilham:
public class Skeleton : MonoBehaviour
{
public Transform player;
public Transform[] posts;
public float speed = 2f;
public StateMachine machine = new StateMachine();
public IState patrol, chase, attack;
void Start()
{
patrol = new PatrolState(this);
chase = new ChaseState(this);
attack = new AttackState(this);
machine.ChangeState(patrol);
}
void Update() { machine.Tick(); }
public float PlayerDistance() { return Vector3.Distance(transform.position, player.position); }
public void WalkTo(Vector3 target)
{
target.y = transform.position.y;
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
public class PatrolState : IState
{
private Skeleton skel;
private int next;
public PatrolState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() < 6f) { skel.machine.ChangeState(skel.chase); return; }
Vector3 post = skel.posts[next].position;
post.y = skel.transform.position.y;
skel.WalkTo(post);
if (skel.transform.position == post) next = (next + 1) % skel.posts.Length;
}
}
public class ChaseState : IState
{
private Skeleton skel;
public ChaseState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
float d = skel.PlayerDistance();
if (d > 6f) { skel.machine.ChangeState(skel.patrol); return; }
if (d < 1.5f) { skel.machine.ChangeState(skel.attack); return; }
skel.WalkTo(skel.player.position);
}
}
public class AttackState : IState
{
private Skeleton skel;
private float cooldown;
public AttackState(Skeleton skel) { this.skel = skel; }
public void Enter() { cooldown = 0f; }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() > 1.5f) { skel.machine.ChangeState(skel.chase); return; }
cooldown -= Time.deltaTime;
if (cooldown <= 0f) { Debug.Log("Swing!"); cooldown = 1f; }
}
}Os estados são criados uma vez e duram tanto quanto o esqueleto, então seus campos persistem entre as visitas. O next de PatrolState continua definido após uma perseguição, e o esqueleto segue caminhando até o posto para o qual estava indo. Enter redefine apenas o que deve recomeçar: o cooldown de AttackState é definido como 0, então o primeiro golpe acontece assim que o esqueleto alcança o jogador.
Todo o movimento passa por WalkTo, que aqui usa MoveTowards no transform e atravessa paredes. Para mover o esqueleto com um Rigidbody, em vez disso, WalkTo define a velocidade do corpo, e o esqueleto atualiza a máquina em FixedUpdate, onde Time.deltaTime é o passo da física. Os três estados não mudam: eles dizem para onde ir; o proprietário diz como.
Desafio
MédioUm cavaleiro guarda o pátio do castelo. As setas movem o Player, cuja Health tem TakeDamage(int amount) (ambos já estão prontos). Knight.cs tem os campos preenchidos pelo Inspector (waypoints de W0 a W2, player, playerHealth, speed 3 e os intervalos abaixo) e métodos auxiliares que usam seu Rigidbody: DistanceTo(point) e PlayerDistance() medem a distância pelo chão, WalkTo(target) caminha em direção a um ponto, Stop() fica parado. Dê a ele um cérebro:
- Em
Knight.cs, crie a máquina e os três estados (campospatrol,chaseeattack), inicie a patrulha e atualize a máquina em FixedUpdate. - Em
KnightStates.cs, escrevaPatrolState,ChaseStateeAttackState, cada um recebendo o cavaleiro em seu construtor. - Patrulha: caminhe até
waypoints[next]; quando estiver a menos de 0.3 dele, siga para o próximo e, depois do último, volte ao primeiro. Se o jogador estiver mais perto do quesightRange(5), persiga-o. Depois da perseguição, a patrulha continua em direção ao waypoint para o qual estava indo. - Perseguição: caminhe até o jogador. Se estiver mais perto do que
attackRange(1.5): ataque. Se estiver mais longe do queloseRange(8): patrulhe. Entre 5 e 8, a perseguição continua, para que o cavaleiro não alterne entre os dois estados no limite do campo de visão. - Ataque: fique parado e acerte o jogador causando 1 de dano imediatamente e, depois, a cada segundo, enquanto ele estiver dentro de
attackRange; quando estiver mais longe, volte a persegui-lo.
Pressione Play, deixe o cavaleiro avistá-lo, leve um golpe, depois corra e então pressione Check.
Experimente você mesmo
using UnityEngine;
// PatrolState, ChaseState e AttackState
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Arquitetura de jogos no Unity
1Componentes, não hierarquias
Componentes pequenosUm script-base para inimigosComponentes abstratosComponentes trabalhando juntosRevisão: Torre modular4Dados Serializados
Classes SerializáveisListas no InspectorEnums no InspectorRevisão: Tabela de Loot7Máquinas de Estados
Estados como ClassesUma Máquina de EstadosTransiçõesInteligências dos InimigosRevisão: Estados da Porta10Projeto: Sobrevivente da Arena
O HeróiTipos de Inimigos5ScriptableObjects
Assets de dadosAtributos das armasConfigurações compartilhadasOndas como dadosRevisão: banco de dados de itens