Menu
CoddyTech

Cerebros de enemigos

Parte de la sección Arquitectura de juegos en Unity del Journey de Unity de Coddy. Lección 31 de 50.

El cerebro de un enemigo es una máquina de estados con un estado por comportamiento. Este esqueleto camina entre sus puestos, persigue a un jugador que se acerca a menos de 6 unidades y blande su espada una vez por segundo cuando está cerca. Cada estado decide por sí mismo cómo salir, y el esqueleto conserva los ayudantes que comparten:

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; }
    }
}

Los estados se crean una vez y viven tanto como el esqueleto, así que sus campos perduran entre visitas. El next de PatrolState sigue establecido después de una persecución, y el esqueleto continúa caminando hacia el puesto al que se dirigía. Enter reinicia solo lo que debe empezar de nuevo: el de AttackState establece cooldown en 0, así que el primer golpe llega en cuanto el esqueleto alcanza al jugador.

Todo el movimiento pasa por WalkTo, aquí MoveTowards sobre el transform, que atraviesa las paredes. Para mover al esqueleto con un Rigidbody, en cambio, WalkTo establece la velocidad del cuerpo y el esqueleto actualiza la máquina en FixedUpdate, donde Time.deltaTime es el paso de física. Los tres estados no cambian: indican adónde ir, el propietario indica cómo.

challenge icon

Desafío

Intermedio

Un caballero protege el patio del castillo. Las flechas mueven al jugador, cuya Health tiene TakeDamage(int amount) (ambos preparados). Knight.cs tiene los campos que rellena el Inspector (waypoints de W0 a W2, player, playerHealth, speed 3 y los rangos de abajo) y métodos auxiliares que usan su Rigidbody: DistanceTo(point) y PlayerDistance() miden sobre el suelo, WalkTo(target) camina hacia un punto y Stop() se queda quieto. Dale un cerebro:

  • En Knight.cs, crea la máquina y los tres estados (campos patrol, chase y attack), inicia la patrulla y actualiza la máquina en FixedUpdate.
  • En KnightStates.cs, escribe PatrolState, ChaseState y AttackState, cada uno tomando al caballero en su constructor.
  • Patrulla: camina hasta waypoints[next]; cuando esté a menos de 0.3, dirígete al siguiente punto y, después del último, vuelve al primero. Si el jugador está a menos de sightRange (5), persíguelo. Después de una persecución, la patrulla continúa hacia el punto de ruta al que se dirigía.
  • Persecución: camina hacia el jugador. Si está a menos de attackRange (1.5): ataca. Si está a más de loseRange (8): patrulla. Entre 5 y 8, la persecución continúa, para que el caballero no alterne entre las dos acciones en el límite de su campo de visión.
  • Ataque: quédate quieto y golpea al jugador por 1 de inmediato y, después, cada segundo mientras esté dentro de attackRange; cuando esté más lejos, vuelve a perseguirlo.

Pulsa Play, deja que el caballero te vea, recibe un golpe, luego huye y, después, pulsa Check.

Pruébalo tú mismo

using UnityEngine;

// PatrolState, ChaseState y AttackState
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Arquitectura de juegos en Unity