Переходы
Часть раздела Архитектура игр в 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, а в обычном классе нужно указать имя класса.
Задание
СреднеЗапиши переходы мухоловки в каждом вызове Tick в FlytrapStates.cs.
- Open: когда выполняется
trap.PlayerInReach(), вызовиtrap.machine.ChangeState(trap.snap);. - Snap: прибавляй
Time.deltaTimeкtimer; через0.5перейди вtrap.chew, если игрок всё ещё в зоне досягаемости, иначе — вtrap.open. - 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
1Компоненты, а не иерархии
Небольшие компонентыБазовый скрипт врагаАбстрактные компонентыКомпоненты работают вместеПовторение: модульная турельПовторение: спаренная пушка4Сериализованные данные
Сериализуемые классыСписки в инспектореПеречисления в инспектореПовторение: таблица добычиПовторение: определяем добычу2Интерфейсы в Unity
IDamageableИнтерфейсы и TryGetComponentGetComponentsПовторение: клавиша взаимодействияПовторение: ближайший в пределах досягаемости5ScriptableObjects
Ресурсы данныхХарактеристики оружияОбщие настройкиСохраняем ветерВолны как данныеПовторение: база предметовПовторение: покупка в магазинеПотренируйтесь самостоятельно: Песочница Unity