Menu
CoddyTech

Chuyển trạng thái

Thuộc phần Kiến trúc trò chơi trong Unity trong hành trình Unity của Coddy. Bài học 38 trên 64.

Ai quyết định khi nào trạng thái thay đổi? Để đối tượng sở hữu quyết định sẽ dồn mọi quy tắc trở lại một hàm Update, như cách chuyển trạng thái đã làm. Cách thường dùng là để mỗi trạng thái tự biết cách thoát: trong Tick, trạng thái kiểm tra điều kiện rồi yêu cầu máy chuyển sang trạng thái tiếp theo. Quả bom này chờ đến khi người chơi đến gần, sau đó đốt ngòi nổ và quay lại trạng thái chờ nếu người chơi chạy đi:

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

Mỗi trạng thái nhận quả bom trong hàm khởi tạo (constructor) và thông qua nó truy cập máy trạng thái, kiểm tra người chơi và các trạng thái khác. Quả bom tạo mỗi trạng thái một lần trong Start, vì vậy một trạng thái không tạo trạng thái khác bằng new: nếu hai trạng thái tạo lẫn nhau trong hàm khởi tạo, chúng sẽ lặp mãi cho đến khi ngăn xếp bị tràn. Hàm Update của quả bom chỉ gọi Tick cho máy trạng thái, còn các quy tắc nằm trong trạng thái tương ứng: ngòi nổ 2 giây chỉ thuộc trách nhiệm của FuseState.

ChangeState chạy Exit của FuseState và Enter của IdleState ngay lập tức, nhưng phần còn lại trong Tick của FuseState vẫn sẽ chạy sau lời gọi đó. return sẽ dừng hàm, nếu không ngòi nổ sẽ cháy thêm một khung hình dù trạng thái đã kết thúc. Destroy được viết là Object.Destroy: MonoBehaviour kế thừa phương thức này từ lớp Object của Unity, còn lớp thông thường phải ghi rõ tên lớp.

challenge icon

Thử thách

Trung bình

Viết các bước chuyển trạng thái của flytrap trong mỗi lần gọi Tick của FlytrapStates.cs.

  1. Mở: khi trap.PlayerInReach() đúng, gọi trap.machine.ChangeState(trap.snap);.
  2. Đớp: cộng Time.deltaTime vào timer; khi đạt 0.5, chuyển sang trap.chew nếu người chơi vẫn ở trong tầm, nếu không thì chuyển sang trap.open.
  3. Nhai: đếm ngược timer từ 3; khi về 0, chuyển sang trap.open.

Nhấn Chơi, đi vào bẫy rồi né nó. Sau đó nhấn Kiểm tra.

Tự làm thử

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()
    {
        // Khép bẫy khi người chơi vào tầm với
    }

    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()
    {
        // Sau 0.5 giây: nhai nếu người chơi vẫn trong tầm với, nếu không thì mở lại
    }

    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()
    {
        // Sau 3 giây: mở lại
    }

    public void Exit()
    {
    }
}

Tất cả bài học trong Kiến trúc trò chơi trong Unity

Tự luyện tập: Playground Unity