4/29/2014

AI 2.3 - FSM FRAMEWORK



  2.3 - FSM FRAMEWORK - Khuôn mẫu FSM

Trong bài viết cuối của AI 2, chúng ta sẽ tìm hiểu về 2 FSM khác nhau đã có sẵn trong thư mục Scenes là AdvanceFSM và SimpleFSM để hiểu rõ hơn về cấu trúc cũng như sự khác biệt nhau.


Lớp AdvanceFSM

Lớp AdvanceFSM cơ bản dùng để quản lý tất cả các FSMState đã được cung cấp và giữ quá trình cập nhật các chuyển đổi với trạng thái hiện tại. Vì vậy, việc đầu tiên trước khi sử dụng sườn máy trạng thái có sẵn chính là khai báo các chuyển đổi và trạng thái mà chúng ta thực hiện cho các xe tăng AI.

Code trong file C# AdvancedFSM được viết như sau:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public enum Transition
{
    None = 0,
    SawPlayer,
    ReachPlayer,
    LostPlayer,
    NoHealth,
}

public enum FSMStateID
{
    None = 0,
    Patrolling,
    Chasing,
    Attacking,
    Dead,
}


Ngoài ra còn có danh sách các đối tượng FSMState và 2 biến cục bộ để lưu trữ ID hiện tại của lớp FSMState cũng như trạng thái hiện tại của FSMState:

private List<FSMState> fsmStates;

    //The fsmStates are not changing directly but updated by using transitions
    private FSMStateID currentStateID;
    public FSMStateID CurrentStateID {
        get {
           return currentStateID;
        }
    }


Hàm AddFSMState và DeleteState có công dụng thêm và xóa tình trạng của lớp FSM trong danh sách riêng biệt của chúng ta. Khi hàm PerformTransition được gọi, biến CurrentState sẽ được cập nhật với trạng thái mới tùy theo sự chuyển đổi.


Lớp FSMState

Lớp FSMState quản lý các chuyển đổi sang các trạng thái khác. Nó có từ điển được gọi là map để lưu trữ các cặp giá trị khóa chính của các sự chuyển đổi và các trạng thái. Vi dụ: SawPlayer vạch ra những chuyển đổi đến trạng thái rượt đuổi - Chasing, còn LostPlayer liên kết với trạng thái đang tuần tra - Patrolling và vân vân...

Code trong file C# FSMState như sau:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public abstract class FSMState
{
        protected Dictionary<Transition, FSMStateID> map = new
        Dictionary<Transition, FSMStateID>();
...


Hai hàm AddTransitiona và DeleteTransition thực hiện công việc thêm và xóa các chuyển đổi từ từ điển trạng thái chuyển đổi đối tượng - map. Hàm GetOutputState tìm các đối tượng trong map và trả về trạng thái dựa theo các chuyển đổi được nhận.

Lớp FSMState còn 2 hàm cơ sở để các lớp con cần thực hiện. Các hàm đó như sau:

...
public abstract void Reason(Transform player, Transform npc);
public abstract void Act(Transform player, Transform npc);
...


Hàm Reason kiểm tra khi trạng thái cần phải chuyển đổi đến trạng thái khác. Và hàm Act thực hiện nhiệm vụ thi hành những công việc cho biến currentState như di chuyển tới trước điểm mốc và sau đó rượt và tấn công người chơi. Cả hai hàm yêu cầu di chuyển dữ liệu của người chơi và NPC (Non Player Character - Nhân vật máy điều khiển), tất cả đều được lớp này xử lý.


Các lớp trạng thái

Không giống như trong ví dụ về SimpleFSM, các trạng thái của xe tăng AI được viết thành các lớp (file script) riêng biệt nhau liên kết đến lớp FSMState như AttackState, ChaseState, DeadState, và PatrolState, mỗi cái với sự thực hiện của 2 hàm Reason và Act. Hãy xem qua file C# PatrolState như một ví dụ điển hình.


Lớp PatrolState

Lớp này bao gồm 3 phần chính: Hàm tạo (Constructor), hàm Reason và hàm Act.

Code trong file C# PatrolState như sau:

using UnityEngine;
using System.Collections;

public class PatrolState : FSMState
{
    public PatrolState(Transform[] wp)
    {
        waypoints = wp;
        stateID = FSMStateID.Patrolling;

        curRotSpeed = 1.0f;
        curSpeed = 100.0f;
    }

    public override void Reason(Transform player, Transform npc)
    {
        //Check the distance with player tank
        //When the distance is near, transition to chase state
        if (Vector3.Distance(npc.position, player.position) <= 300.0f)
        {
            Debug.Log("Switch to Chase State");
            npc.GetComponent<NPCTankController>().SetTransition(Transition.SawPlayer);
        }
    }

    public override void Act(Transform player, Transform npc)
    {
        //Find another random patrol point if the current point is reached
       
        if (Vector3.Distance(npc.position, destPos) <= 100.0f)
        {
            Debug.Log("Reached to the destination point\ncalculating the next point");
            FindNextPoint();
        }

        //Rotate to the target point
        Quaternion targetRotation = Quaternion.LookRotation(destPos - npc.position);
        npc.rotation = Quaternion.Slerp(npc.rotation, targetRotation, Time.deltaTime * curRotSpeed);

        //Go Forward
        npc.Translate(Vector3.forward * Time.deltaTime * curSpeed);
    }
}


Hàm tạo lấy giá trị từ mảng waypoints và lưu chúng vào mảng cục bộ và khi đó nó được khởi tạo các thuộc tính như di chuyển và tốc độ xoay. Hàm Reason kiểm tra khoảng cách của xe tăng AI với xe tăng của người chơi. Nếu xe tăng của người chơi trong tầm, nó sẽ thiết lập transition ID thành SawPlayer bằng cách sử dụng hàm SetTransition của lớp NPCTankController theo code trong file C# NPCTankController như sau:

  public void SetTransition(Transition t)
  {
        PerformTransition(t);
  }


Nó được bao bởi hàm PerformTransition của lớp AdvanceFSM. Hàm này sẽ cập nhật biến trạng thái hiện hành - CurrentState, với hàm nhận trách nhiệm chuyển đổi, sử dụng đối tượng Transition, và từ điển trạng thái chuyển đổi đối tượng - map từ lớp FSMState. Hàm Act cập nhật điểm mốc của xe tăng AI, xoay xe tăng và di chuyển thẳng đến điểm mốc đó. Các lớp trạng thái khác cũng dựa theo mẫu này với các reason và act khác nhau. Chúng ta đã được thấy chúng qua bài viết trước Máy trạng thái - FSM đơn giản, vì vậy mình sẽ không nói lại ở bài này.


Lớp NPCTankController

Xe tăng AI, lớp NPCTankController sẽ liên kết đến lớp AdvanceFSM. Đây là cách chúng ta thiết lập các trạng thái cho các xe tăng máy.

  private void ConstructFSM()
  {
        ...


        PatrolState patrol = new PatrolState(waypoints);
        patrol.AddTransition(Transition.SawPlayer, FSMStateID.Chasing);
        patrol.AddTransition(Transition.NoHealth, FSMStateID.Dead);

        ChaseState chase = new ChaseState(waypoints);
        chase.AddTransition(Transition.LostPlayer, FSMStateID.Patrolling);
        chase.AddTransition(Transition.ReachPlayer, FSMStateID.Attacking);
        chase.AddTransition(Transition.NoHealth, FSMStateID.Dead);

        AttackState attack = new AttackState(waypoints);
        attack.AddTransition(Transition.LostPlayer, FSMStateID.Patrolling);
        attack.AddTransition(Transition.SawPlayer, FSMStateID.Chasing);
        attack.AddTransition(Transition.NoHealth, FSMStateID.Dead);

        DeadState dead = new DeadState();
        dead.AddTransition(Transition.NoHealth, FSMStateID.Dead);

        AddFSMState(patrol);
        AddFSMState(chase);
        AddFSMState(attack);
        AddFSMState(dead);
  }


Đây là cái đẹp của việc sử dụng FSM framework. Khi các trạng thái tự quản lý mà không cần các lớp riêng, lớp NPCTankController chỉ cần gọi hàm Reaso và Act của trạng thái hiện tại đang được kích hoạt. Điều này loại trừ việc bạn phải viết một danh sách dài về các câu lệnh điều kiện if / else và switch. Thay vào đó, giờ đây các trạng thái được gói gọn đẹp đẽ vào các lớp riêng biệt, làm cho code dễ quản lý hơn về số lượng trạng thái để thực hiện cũng như các chuyển đổi giữa chúng dù là các project lớn ngày càng phức tạp hơn.

  protected override void FSMFixedUpdate()
  {
        CurrentState.Reason(playerTransform, transform);
        CurrentState.Act(playerTransform, transform);
  }



Và đó là các công việc của khuôn mẫu FSM mà chúng ta vừa tham khảo. Trong phần tổng quát, các bước chính để sử dụng khuôn mẫu này như sau:
  1. Khai báo các chuyển đổi và các trạng thái trong lớp AdvanceFSM.
  2. Viết các lớp trạng thái liên kết đến lớp FSMState, và bổ sung các hàm Reason và Act.
  3. Viết lớp tùy chỉnh NPC AI liên kết đến lớp AdvanceFSM.
  4. Tạo các lớp trạng thái, và thêm các chuyển đổi và cặp trạng thái sử dụng hàm AddTransition của lớp FSMState.
  5. Thêm các trạng thái đã tạo vào danh sách trong lớp AdvanceFSM bằng cách sử dụng hàm AddFSMState.
  6. Gọi hàm Reason và Act của biến CurrentState trong mỗi chu trình chạy game.

Bạn có thể vọc thêm scene AdvancedFSM. Nó sẽ vận hành tương tự như SimpleFSM. Nhưng code và các lớp sẽ có tổ chức và dễ quản lý hơn.


Tổng Kết AI 2.

Trong phần 2 này, chúng ta đã học được cách vận hành của máy trạng thái trong Unity3D dựa vào game bắn xe tăng đơn giản. Chúng ta đã hiểu sơ qua cách làm việc của FSM bằng cách đơn giản nhất, sử dụng hàm switch. Và chúng ta đã học cách để sử dụng khuôn mẫu để làm AI vận hành một cách dễ quản lý và dễ mở rộng hơn.

Trong phần tiếp theo là AI 3., chúng ta sẽ tìm hiểu về xác xuất ngẫu nhiên và cách chúng ta sẽ sử dụng nó để làm cho game của chúng ta trở nên không thể đoán trước được.

No comments:

Post a Comment