Menu
CoddyTech

Переходы

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

Кто решает, когда меняется состояние? Если поручить это владельцу, все правила снова окажутся в одном 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

Задание

Средне

Запиши переходы мухоловки в каждом вызове Tick в FlytrapStates.cs.

  1. Open: когда выполняется trap.PlayerInReach(), вызови trap.machine.ChangeState(trap.snap);.
  2. Snap: прибавляй Time.deltaTime к timer; через 0.5 перейди в trap.chew, если игрок всё ещё в зоне досягаемости, иначе — в trap.open.
  3. Chew: отсчитывай timer от 3 до нуля; при достижении 0 перейди в trap.open.

Нажми «Играть», подойди к ловушке, а затем увернись от неё. После этого нажми «Проверить».

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

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()
    {
    }
}

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

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