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.
Thử thách
Trung bìnhViế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.
- Mở: khi
trap.PlayerInReach()đúng, gọitrap.machine.ChangeState(trap.snap);. - Đớp: cộng
Time.deltaTimevàotimer; khi đạt0.5, chuyển sangtrap.chewnếu người chơi vẫn ở trong tầm, nếu không thì chuyển sangtrap.open. - Nhai: đếm ngược
timertừ 3; khi về 0, chuyển sangtrap.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
1Thành phần, không phải hệ thống phân cấp
Thành phần nhỏTập lệnh kẻ địch cơ sởThành phần trừu tượngCác thành phần phối hợp với nhauÔn tập: tháp pháo mô-đunÔn tập: pháo nòng đôi4Dữ liệu tuần tự hóa
Lớp có thể tuần tự hóaDanh sách trong InspectorKiểu liệt kê trong InspectorÔn tập: Bảng vật phẩm rơiÔn tập: Quay vật phẩm7Máy trạng thái
Trạng thái dưới dạng lớpTrạng thái cảnh báoMáy trạng tháiChuyển trạng tháiTrí tuệ của kẻ địchHiệp sĩ tấn côngÔn tập: trạng thái cửa2Giao diện trong Unity
IDamageableGiao diện và TryGetComponentGetComponentsÔn tập: phím tương tácÔn tập: đối tượng gần nhất trong tầm với5ScriptableObjects
Tài sản dữ liệuChỉ số vũ khíCài đặt dùng chungLưu gióSóng dưới dạng dữ liệuÔn tập: Cơ sở dữ liệu vật phẩmÔn tập: Mua hàng từ cửa hàng8Code có thể tái sử dụng
Phương thức mở rộngSingleton tổng quátCác hàm hỗ trợ tổng quátÔn tập: FindNearest3Sự kiện
Sự kiện C# trong UnityĐăng ký trong OnEnableCác trường UnityEventSự kiện điều khiển UIÔn tập: Cửa sổ bật lên thành tíchÔn tập: Bộ hẹn giờ cửa sổ bật lên6Lưu dữ liệu bằng JSON
JsonUtilityLưu trò chơiTải dữ liệu và giá trị mặc địnhÔn tập: các ô lưuChọn và xóa một ô lưu9Tái sử dụng đối tượng bằng pool
Vì sao cần pool đối tượngPool dùng hàng đợiObjectPool của UnityÔn tập: pool đạnTự luyện tập: Playground Unity