Chủ Nhật, 2 tháng 3, 2014

AI 2.1 - MÁY TRẠNG THÁI - FINITE STATE MACHINE


  2.1 - Máy trạng thái - Finite State Machine

Trong chương này, chúng ta sẽ học cách sử dụng FSM trong Unity3D qua một trò xe tăng đơn giản. Chúng ta sẽ tìm hiểu code cùng các thành phần liên quan.


Download Source Code
VIETUNITY AI C02.rar


Trong game của chúng ta, player có thể điều khiển được một chiếc xe tăng. Các xe tăng địch sẽ di chuyển xung quanh trong scene bằng bốn điểm waypoint. Khi xe tăng của player bước vào tầm nhìn của địch, bọn chúng sẽ truy đuổi theo; và khi xe tăng địch đã đến đủ gần để tấn công, bọn chúng sẽ bắt đầu bắn vào xe tăng của player. Điều này có đủ đơn giản chứ? Chúng ta sẽ tích hợp FSM vào để điều khiển các xe tăng địch AI của chúng ta. Đầu tiên, chúng ta sẽ chỉ dùng các trạng thái chuyển đổi đơn giản để đưa vào các trạng thái AI của xe tăng, và rồi khi chúng ta dùng một framework FSM, nền tảng tương thích trong C# FSM framework, và có thể được tìm thấy ở trang web sau đây :

http://wiki.unity3d.com/index.php?title=Finite_State_Machine

Xe tăng của Player

Trước khi viết script cho xe tăng player, hãy xem cách chúng ta cài đặt đối tượng Player Tank. Đối tượng Tank dựa trên một Mesh đơn giản với một thành phần Rigidbody, và một thành phần Box Collider. Đối tượng Tank không chỉ đơn giản là một Mesh, mà là hai Mesh tách biệt, Tank và Turret. Chúng ta thiết lập Turret là đối tượng con của Tank. Nó cho phép đối tượng Turret xoay chiều độc lập khi dùng chuột di chuyển. Và cùng lúc, khi mà nó là con của đối tượng Tank, nó sẽ đi theo bất kỳ đâu Tank đi. Vì vậy tạo ra một empty object để làm việc chuyển đổi SpawnPoint của chúng ta. Nó sẽ được dùng như một vị trí điểm khởi tạo lúc bắn một viên đạn. Cũng như chúng ta cần ấn định liên kết của Player vào đối tượng Tank của chúng ta. Đó là cách thực thể Tank của chúng ta được thiết lập. Nào chúng ta hãy nhìn vào các lớp điều khiển.

Thực thể Tank
Lớp PlayerTankController

Lớp này mang ý nghĩa chính bởi player điều khiển đối tượng Tank sẽ dùng đến nó. Chúng ta sẽ dùng các phím W, A, S và D để di chuyển và lái xe tăng, và chuột trái dùng để nhắm và bắn dành cho đối tượng Turret.

Khởi tạo

Đặc tính của lớp TankController như sau. Đầu tiên chúng ta sẽ thiết lập hàm Start và Update.
Code trong file PlayerTankController.cs như sau :

using UnityEngine;
using System.Collections;
 

public class PlayerTankController : MonoBehaviour
{
    public GameObject Bullet;
    

   private Transform Turret;
    private Transform bulletSpawnPoint;
    private float curSpeed, targetSpeed, rotSpeed;
    private float turretRotSpeed = 10.0f;
    private float maxForwardSpeed = 300.0f;
    private float maxBackwardSpeed = -300.0f;
  

    //Bullet shooting rate
    protected float shootRate = 0.5f;
    protected float elapsedTime;
    

    void Start()
    {
      //Tank Settings
      rotSpeed = 150.0f;
      

      //Get the turret of the tank
      Turret = gameObject.transform.GetChild(0).transform;
      bulletSpawnPoint = Turret.GetChild(0).transform;
    }

    
    void Update()
    {
      UpdateWeapon();
      UpdateControl();
    }


Đối tượng con đầu tiên trong thực thể Tank của chúng ta chính là đối tượng Turret, và đối tượng con đầu tiên của đối tượng Turret chính là điểm khởi tạo đạn SpawnPoint. Hàm Start tìm các đối tượng này, và rồi ấn định chúng cho các biến. Chúng ta sẽ ấn định biến Bullet sau, sau khi chúng ta tạo ra đối tượng Bullet. Cũng như chúng ta thêm vào trong hàm Update những hàm như UpdateControl và UpdateWeapon, chúng ta sẽ tạo chúng sớm thôi.

Bắn đạn

SpawnPoint
Bất kể khi nào player nhấp chuột trái, chúng ta kiểm tra thời gian tổng cộng từ lần bắn trước đó đã mất. Sau đó, chúng ta tạo ra một đối tượng Bullet mới ở vị trí biến SpawnPoint. Bằng cách này, chúng ta có thể tránh việc bắn liên tục mà không có giới hạn nào cả.

void UpdateWeapon()
    {
      if (Input.GetMouseButtonDown(0))
      {
        elapsedTime += Time.deltaTime;
        if (elapsedTime >= shootRate)
        {
          //Reset the time
          elapsedTime = 0.0f;
       
          //Instantiate the bullet
          Instantiate(Bullet, bulletSpawnPoint.position, bulletSpawnPoint.rotation);
        }
      }
    }

Điều khiển xe tăng

Turret
Player sẽ xoay đối tượng Turret bằng cách dùng chuột. Vì thế, phần này có hơi chua. Camera của chúng ta sẽ hướng xuống chiến trường. Từ đó, chúng ta sẽ dùng tia sáng để xác định hướng xoay, dựa trên đối tượng mousePosition trên chiến trường.

    void UpdateControl()
    {
      //AIMING WITH THE MOUSE
      //Generate a plane that intersects the transform's
      //position with an upwards normal.
      Plane playerPlane = new Plane(Vector3.up,transform.position + new Vector3(0, 0, 0));

      // Generate a ray from the cursor position
      Ray RayCast = Camera.main.ScreenPointToRay(Input.mousePosition);

      //Determine the point where the cursor ray intersects
      //the plane.
      float HitDist = 0;

      // If the ray is parallel to the plane, Raycast will
      //return false.
      if (playerPlane.Raycast(RayCast, out HitDist))
      {
        //Get the point along the ray that hits the
        //calculated distance.
        Vector3 RayHitPoint = RayCast.GetPoint(HitDist);

        Quaternion targetRotation = Quaternion.LookRotation(RayHitPoint - transform.position);

        Turret.transform.rotation = Quaternion.Slerp(Turret.transform.rotation, targetRotation, Time.deltaTime * turretRotSpeed);
      }


Tia sáng để nhắm bằng chuột

Đây là cách nó hoạt động :

1.     Thiết lập một mặt phẳng giao với xe tăng người chơi bằng một pháp tuyến hướng lên.

2.     Bắn ra một tia từ khoảng không màn hình với vị trí chuột (ở hình minh họa trước, cho thấy chúng ta đang nhìn xuống xe tăng).

3.     Tìm điểm tia sáng giao với mặt phẳng.

4.     Cuối cùng, tìm góc xoay từ vị trí hiện hành đến điểm giao nhau.

Rồi chúng ta kiểm tra việc nhập phím, và rồi di chuyển/xoay xe tăng theo đó.

      if (Input.GetKey(KeyCode.W))
      {
        targetSpeed = maxForwardSpeed;
      }
      else if (Input.GetKey(KeyCode.S))
      {
        targetSpeed = maxBackwardSpeed;
      }
      else
      {
        targetSpeed = 0;
       }
      if (Input.GetKey(KeyCode.A))
      {
        transform.Rotate(0, -rotSpeed * Time.deltaTime, 0.0f);
      }
      else if (Input.GetKey(KeyCode.D))
      {
        transform.Rotate(0, rotSpeed * Time.deltaTime, 0.0f);
      }

      //Determine current speed
      curSpeed = Mathf.Lerp(curSpeed, targetSpeed, 7.0f * Time.deltaTime);

      transform.Translate(Vector3.forward * Time.deltaTime * curSpeed);
    }
}


Lớp Bullet

Prefab Bullet
Tiếp theo prefab Bullet được thiết lập với hai mặt phẳng trực giao bằng việc dùng laser – như một vật liệu, và một Particles/Additive trong trường Shader.

Code trong file Bullet.cs như sau :

using UnityEngine;
using System.Collections;

public class Bullet : MonoBehaviour
{
    //Explosion Effect
    public GameObject Explosion;
    public float Speed = 600.0f;
    public float LifeTime = 3.0f;
    public int damage = 50;

    void Start()
   {
      Destroy(gameObject, LifeTime);
    }

    void Update()
    {
      transform.position += transform.forward * Speed * Time.deltaTime;
    }

    void OnCollisionEnter(Collision collision)
    {
      ContactPoint contact = collision.contacts[0];
      Instantiate(Explosion, contact.point, Quaternion.identity);
      Destroy(gameObject);
    }
}

Chúng ta có ba đặc tính: damage, Speed và Lifetime cho Bullet, vì thế Bullet sẽ tự động bị huỷ sau khi hết lifetime của nó.

Bạn có thể thấy đặc tính Explosition của Bullet được liên kết với prefab ParticleExplosion, chúng ta sẽ không nói chi tiết về nó. Có một prefab được gọi là ParticleExplosion bên dưới thư mục ParticleEffects. Chúng ta chỉ cần kéo thả prefab vào trong khu vực này. Hiệu ứng riêng biệt này được dùng khi Bullet đâm vào cái gì đó như miêu tả trong phương pháp OnCollisionEnter. Prefab ParticleExplosion dùng một script gọi là AutoDestruct để huỷ đối tượng Explosion một cách tự động sau một khoảng thời gian nhất định.

Không có nhận xét nào:

Đăng nhận xét