Menu
CoddyTech

Логика врагов

Часть раздела Архитектура игр в Unity путешествия по Unity на Coddy. Урок 39 из 64.

Мозг врага — это конечный автомат, в котором каждому поведению соответствует отдельное состояние. Этот скелет ходит между своими постами, преследует игрока, если тот оказывается ближе чем в 6 единицах, и раз в секунду взмахивает мечом, когда находится рядом. Каждое состояние само решает, как из него выйти, а скелет сохраняет общие для них вспомогательные методы:

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

Состояния создаются один раз и существуют столько же, сколько скелет, поэтому значения их полей сохраняются между переходами в эти состояния. Значение next в PatrolState сохраняется и после погони, и скелет продолжает идти к посту, к которому направлялся. Enter сбрасывает только то, что должно начинаться заново: в AttackState значение cooldown устанавливается в 0, поэтому первый взмах происходит, как только скелет достигает игрока.

Всё перемещение выполняется через WalkTo: здесь используется MoveTowards для transform, и скелет проходит сквозь стены. Чтобы перемещать скелета с помощью Rigidbody, WalkTo задаёт скорость тела, а скелет обновляет автомат состояний в FixedUpdate, где Time.deltaTime — это шаг физического расчёта. Три состояния не меняются: они указывают, куда идти, а владелец определяет, как.

challenge icon

Задание

Средне

Задай рыцарю состояния PatrolState и ChaseState в KnightStates.cs, передав каждому рыцаря.

  1. Патрулирование: иди к waypoints[next]; оказавшись ближе чем на 0.3, переходи к следующей точке, зацикливая маршрут, и сохраняй текущую точку между посещениями. Если игрок ближе, чем sightRange, преследуй его.
  2. Преследование: иди к игроку; если расстояние больше loseRange, патрулируй.
  3. В Knight создай машину состояний и состояния в Awake, запусти патрулирование в Start и вызывай обновление в FixedUpdate.

Нажми «Играть», затем подойди поближе и побегай. Потом нажми «Проверить».

Попробуйте сами

using UnityEngine;

// PatrolState и ChaseState

Все уроки раздела Архитектура игр в Unity

Потренируйтесь самостоятельно: Песочница Unity