Trí tuệ của kẻ địch
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 39 trên 64.
Bộ não của kẻ địch là một máy trạng thái, mỗi trạng thái tương ứng với một hành vi. Bộ xương này đi tuần giữa các vị trí của mình, đuổi theo người chơi khi họ đến trong phạm vi 6 đơn vị và vung kiếm mỗi giây một lần khi ở gần. Mỗi trạng thái tự quyết định cách thoát ra, còn bộ xương giữ các hàm trợ giúp mà chúng dùng chung:
public class Skeleton : MonoBehaviour
{
public Transform player;
public Transform[] posts;
public float speed = 2f;
public StateMachine machine = new StateMachine();
public IState patrol, chase, attack;
void Start()
{
patrol = new PatrolState(this);
chase = new ChaseState(this);
attack = new AttackState(this);
machine.ChangeState(patrol);
}
void Update() { machine.Tick(); }
public float PlayerDistance() { return Vector3.Distance(transform.position, player.position); }
public void WalkTo(Vector3 target)
{
target.y = transform.position.y;
transform.position = Vector3.MoveTowards(transform.position, target, speed * Time.deltaTime);
}
}
public class PatrolState : IState
{
private Skeleton skel;
private int next;
public PatrolState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() < 6f) { skel.machine.ChangeState(skel.chase); return; }
Vector3 post = skel.posts[next].position;
post.y = skel.transform.position.y;
skel.WalkTo(post);
if (skel.transform.position == post) next = (next + 1) % skel.posts.Length;
}
}
public class ChaseState : IState
{
private Skeleton skel;
public ChaseState(Skeleton skel) { this.skel = skel; }
public void Enter() { }
public void Exit() { }
public void Tick()
{
float d = skel.PlayerDistance();
if (d > 6f) { skel.machine.ChangeState(skel.patrol); return; }
if (d < 1.5f) { skel.machine.ChangeState(skel.attack); return; }
skel.WalkTo(skel.player.position);
}
}
public class AttackState : IState
{
private Skeleton skel;
private float cooldown;
public AttackState(Skeleton skel) { this.skel = skel; }
public void Enter() { cooldown = 0f; }
public void Exit() { }
public void Tick()
{
if (skel.PlayerDistance() > 1.5f) { skel.machine.ChangeState(skel.chase); return; }
cooldown -= Time.deltaTime;
if (cooldown <= 0f) { Debug.Log("Swing!"); cooldown = 1f; }
}
}Các trạng thái được tạo một lần và tồn tại suốt vòng đời của bộ xương, vì vậy các trường của chúng giữ nguyên giá trị giữa những lần được kích hoạt. next của PatrolState vẫn giữ nguyên sau khi đuổi theo người chơi, và bộ xương tiếp tục đi đến vị trí mà nó đang hướng tới. Enter chỉ đặt lại những gì cần bắt đầu lại: Enter của AttackState đặt cooldown về 0, nên bộ xương vung kiếm ngay khi đến chỗ người chơi.
Mọi chuyển động đều đi qua WalkTo, ở đây dùng MoveTowards trên transform, khiến bộ xương đi xuyên qua tường. Nếu muốn di chuyển bộ xương bằng Rigidbody, WalkTo sẽ đặt vận tốc của vật thể, còn bộ xương sẽ cập nhật máy trạng thái trong FixedUpdate, nơi Time.deltaTime là bước thời gian vật lý. Ba trạng thái không thay đổi: chúng chỉ định nơi cần đến, còn đối tượng sở hữu quyết định cách di chuyển.
Thử thách
Trung bìnhCài đặt cho hiệp sĩ các trạng thái PatrolState và ChaseState trong KnightStates.cs, mỗi trạng thái nhận hiệp sĩ làm đối số.
- Tuần tra: đi đến
waypoints[next]; khi cách điểm đó dưới0.3, chuyểnnextsang điểm kế tiếp theo vòng lặp và giữ nguyên giá trị này giữa các lần ghé thăm. Khi gần hơnsightRange, đuổi theo. - Đuổi theo: đi đến người chơi; khi ở xa hơn
loseRange, tuần tra. - Trong
Knight, khởi tạo máy trạng thái và các trạng thái trong Awake, bắt đầu tuần tra trong Start, gọi Tick trong FixedUpdate.
Nhấn Chơi, sau đó đến gần và chạy. Rồi nhấn Kiểm tra.
Tự làm thử
using UnityEngine;
// PatrolState và ChaseState
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