Menu
CoddyTech

Переходы

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

Кто решает, когда состояние меняется? Если поручить это владельцу, все правила снова окажутся в одном Update, как при использовании switch. Обычно каждое состояние само знает, как из него выйти: в своём Tick оно проверяет условие и запрашивает у машины переход в следующее состояние. Эта бомба ждёт, пока игрок не подойдёт близко, затем поджигает фитиль и возвращается в режим ожидания, если игрок убегает:

public class Bomb : MonoBehaviour
{
    public Transform player;
    public StateMachine machine = new StateMachine();
    public IState idle;
    public IState fuse;

    void Start()
    {
        idle = new IdleState(this);
        fuse = new FuseState(this);
        machine.ChangeState(idle);
    }

    void Update() { machine.Tick(); }

    public bool PlayerNear()
    {
        return Vector3.Distance(transform.position, player.position) < 3f;
    }
}

public class IdleState : IState
{
    private Bomb bomb;
    public IdleState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { }
    public void Exit() { }

    public void Tick()
    {
        if (bomb.PlayerNear()) bomb.machine.ChangeState(bomb.fuse);
    }
}

public class FuseState : IState
{
    private Bomb bomb;
    private float timer;
    public FuseState(Bomb bomb) { this.bomb = bomb; }

    public void Enter() { timer = 2f; }
    public void Exit() { }

    public void Tick()
    {
        if (!bomb.PlayerNear())
        {
            bomb.machine.ChangeState(bomb.idle);
            return;
        }
        timer -= Time.deltaTime;
        if (timer <= 0f)
        {
            Debug.Log("Boom!");
            Object.Destroy(bomb.gameObject);
        }
    }
}

Каждое состояние получает бомбу в конструкторе и через неё обращается к машине, проверке близости игрока и другим состояниям. Бомба создаёт каждое состояние один раз, в Start, поэтому состояние никогда не создаёт другое с помощью new: два состояния, создающие друг друга в своих конструкторах, зациклились бы, пока стек не переполнится. В Update бомбы только вызывается Tick машины, а правила находятся в соответствующих состояниях: фитиль с таймером на 2 секунды — забота только FuseState.

ChangeState сразу вызывает Exit у FuseState и Enter у IdleState, но остальная часть Tick состояния FuseState всё равно продолжила бы выполняться после вызова. return останавливает её, иначе фитиль горел бы ещё один кадр для состояния, которое уже завершилось. Destroy записан как Object.Destroy: MonoBehaviour наследует его от класса Object в Unity, а в обычном классе нужно указать имя класса.

challenge icon

Задание

Средне

Мухоловка щёлкает челюстями на любого, кто подходит к ней. Flytrap.cs готов: он создаёт машину состояний и три состояния (публичные поля open, snap и chew), запускает состояние Open и обновляет машину каждый кадр, а PlayerInReach() сообщает, находится ли игрок ближе чем в 2 единицах по земле. В FlytrapStates.cs написаны все методы Enter (цвета, таймеры, сообщение в журнале Chomp!). Напиши переходы в методе Tick каждого состояния:

  • Open: когда игрок находится в пределах досягаемости, мухоловка захлопывается.
  • Snap длится 0.5 секунды. Затем мухоловка начинает жевать, если игрок всё ещё в пределах досягаемости, и снова открывается, если ему удалось уйти.
  • Chew длится 3 секунды, независимо от того, кто находится поблизости, после чего мухоловка снова открывается.

Стрелки перемещают Player. Нажми Play, подойди к мухоловке, затем попробуй увернуться от неё, после чего нажми Check.

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

using UnityEngine;

public class OpenState : IState
{
    private Flytrap trap;

    public OpenState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        trap.Paint(new Color(0.3f, 0.8f, 0.2f));
    }

    public void Tick()
    {
        // Захлопнуться, когда игрок окажется в зоне досягаемости
    }

    public void Exit()
    {
    }
}

public class SnapState : IState
{
    private Flytrap trap;
    private float timer;

    public SnapState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 0f;
        trap.Paint(new Color(1f, 0.55f, 0.1f));
    }

    public void Tick()
    {
        // Через 0.5 секунды: жевать, если игрок всё ещё в зоне досягаемости; иначе снова открыться
    }

    public void Exit()
    {
    }
}

public class ChewState : IState
{
    private Flytrap trap;
    private float timer;

    public ChewState(Flytrap trap)
    {
        this.trap = trap;
    }

    public void Enter()
    {
        timer = 3f;
        trap.Paint(new Color(0.5f, 0.05f, 0.1f));
        Debug.Log("Chomp!");
    }

    public void Tick()
    {
        // Через 3 секунды: снова открыться
    }

    public void Exit()
    {
    }
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

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