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

AI 2.2 - MÁY TRẠNG THÁI - FSM ĐƠN GIẢN

  2.2 - Máy trạng thái - FSM đơn giản

Bài viết này, chúng ta sẽ cùng nghiên cứu các phương thức hoạt động cũng như tính năng chiến đấu của các xe tăng AI trong scene SimpleFSM. Bạn có thể tìm thấy scene này trong source code đã cung cấp ở bài viết AI 2.1.

Thiết lập waypoint

Tiếp theo, chúng ta sẽ đặt bốn object Cube vào các vị trí ngẫu nhiên, đó chính là các điểm mốc mà xe của AI sẽ chạy đến theo thứ tự ngẫu nhiên, tên của những điểm này được gom chung lại thành WandarPoints.

WanderPoints
Đây là thuộc tính của đối tượng WanderPoint :

WanderPoint properties
Một điều cần lưu ý ở đây, chính là việc cần đặt tag cho những điểm đó với tên là WandarPoint. Chúng ta sẽ liên kết các tag này lại khi chúng ta thiết lập các điểm mốc cho xe tăng AI. Như bạn có thể thấy các thuộc tính của nó, một điểm mốc ở đây chỉ là một Cube Object đã bị vô hiệu hóa Mesh Renderer, và đối tượng Box Collider bị loại bỏ. Thậm chí chúng ta có thể dùng một empty object cũng được, tất cả những gì chúng ta cần từ một điểm mốc chính là vị trí cùng dữ liệu về sự biến đổi của nó. Nhưng chúng ta đang dùng Cube objects ở đây, để chúng ta có thể dễ nhìn thấy các điểm mốc khi làm việc.

Lớp FSM ảo

Tiếp theo, chúng ta sẽ thực thi một lớp ảo, định nghĩa các phương thức xe tăng AI của địch phải thực thi.

Code trong file FSM.cs như sau :

using UnityEngine;
using System.Collections;
public class FSM : MonoBehaviour
{
    //Player Transform
    protected Transform playerTransform;
    //Next destination position of the NPC Tank
    protected Vector3 destPos;
    //List of points for patrolling
    protected GameObject[] pointList;
    //Bullet shooting rate
    protected float shootRate;
    protected float elapsedTime;
    //Tank Turret
    public Transform turret { get; set; }
    public Transform bulletSpawnPoint { get; set; }
    protected virtual void Initialize() { }
    protected virtual void FSMUpdate() { }
    protected virtual void FSMFixedUpdate() { }
    // Use this for initialization
    void Start ()
    {
      Initialize();
    }
   
    // Update is called once per frame
    void Update ()
    {
      FSMUpdate();
    }
    void FixedUpdate()
    {
      FSMFixedUpdate();
    }
}

Tất cả các xe tăng của địch cần biết vị trí xe tăng của người chơi, điểm đến tiếp theo của chúng, và danh sách của các điểm mốc để chọn lựa hướng đi, khi chúng đang tuần tra. Một khi xe tăng của người chơi bước vào phạm vi, chúng sẽ xoay đối tượng turret (tháp pháo) và rồi bắt đầu bắn từ điểm viên đạn xuất phát với tốc độ bắn của chúng.

Các lớp kế thừa cũng cần thực thi ba hàm : Initialize, FSMUpdate, và FSMFixedUpdate. Đó chính là các lớp ảo, mà xe tăng AI của chúng ta đang thực thi.

Xe tăng AI của địch

Bây giờ hãy nhìn vào đoạn code thực sự cho các xe tăng AI của chúng ta. Lớp SimpleFSM kế thừa từ lớp ảo FSM.

Đoạn code nằm trong file SimpleFSM.cs như sau:

using UnityEngine;
using System.Collections;
public class SimpleFSM : FSM
{
    public enum FSMState
    {
      None,
      Patrol,
      Chase,
      Attack,
      Dead,
    }
    //Current state that the NPC is reaching
    public FSMState curState;
    //Speed of the tank
    private float curSpeed;
    //Tank Rotation Speed
    private float curRotSpeed;
    //Bullet
    public GameObject Bullet;
    //Whether the NPC is destroyed or not
    private bool bDead;
    private int health;

Ở đây, chúng ta đang khai báo vài biến mới. Xe tăng AI của chúng ta sẽ có bốn trạng thái khác nhau : Patrol, Chase, Attack, và Dead. Cơ bản chúng ta đang thực thi FSM được miêu tả như trong ví dụ ở AI 1.1 - Giới thiệu về AI.


FSM của xe tăng AI của địch

Trong hàm Initialize, chúng ta thiết lập các thuộc tính của xe tăng AI bằng các giá trị mặc định. Rồi chúng ta lưu lại các vị trí của các điểm mốc trong các biến cục bộ. Chúng ta lấy các điểm mốc đó từ scene bằng cách dùng hàm FindGameObjectsWithTag, cố tìm các đối tượng đó bằng tag WandarPoint.

    //Initialize the Finite state machine for the NPC tank
    protected override void Initialize ()
    {
      curState = FSMState.Patrol;
      curSpeed = 150.0f;
      curRotSpeed = 2.0f;
      bDead = false;
      elapsedTime = 0.0f;
      shootRate = 3.0f;
      health = 100;
      //Get the list of points
      pointList =
      GameObject.FindGameObjectsWithTag("WandarPoint");
      //Set Random destination point first
      FindNextPoint();
      //Get the target enemy(Player)
      GameObject objPlayer =
      GameObject.FindGameObjectWithTag("Player");
      playerTransform = objPlayer.transform;
      if (!playerTransform)
        print("Player doesn't exist.. Please add one "+
        "with Tag named 'Player'");
        //Get the turret of the tank
        turret = gameObject.transform.GetChild(0).transform;
        bulletSpawnPoint = turret.GetChild(0).transform;
      }

Hàm update của chúng ta được thực thi mỗi khung hình như sau :

    //Update each frame
    protected override void FSMUpdate()
    {
      switch (curState)
      {
        case FSMState.Patrol: UpdatePatrolState(); break;
        case FSMState.Chase: UpdateChaseState(); break;
        case FSMState.Attack: UpdateAttackState(); break;
        case FSMState.Dead: UpdateDeadState(); break;
      }
     //Update the time
     elapsedTime += Time.deltaTime;
     //Go to dead state is no health left
     if (health <= 0)
      curState = FSMState.Dead;
    }

Chúng ta kiểm tra tình trạng hiện hành, và rồi gọi hàm trạng thái thích hợp. Một khi đối tượng health trở về 0 hoặc âm, chúng ta cho xe tăng trở về trạng thái Dead.

Trạng thái tuần tra (Patrol)


Khi xe tăng của chúng ta đang ở trạng thái Patrol, chúng ta kiểm tra nó có hay không đã đến được điểm đích. Nếu đúng, nó sẽ tìm ra điểm đích tiếp theo. Cơ bản phương thức FindNextPoint chọn điểm đích tiếp theo một cách ngẫu nhiên từ các điểm mốc xác định. Nếu vẫn nằm trên con đường đến điểm đích hiện tại, nó sẽ kiểm tra khoảng cách đến xe tăng của người chơi. Nếu xe tăng người chơi vẫn còn ở trong phạm vị (ở đây là 300), nó sẽ đổi sang trạng thái Chase. Các đoạn code còn lại chỉ là việc chuyển hướng và di chuyển về trước của xe tăng.

    protected void UpdatePatrolState()
    {
      //Find another random patrol point if the current
      //point is reached
      if (Vector3.Distance(transform.position, destPos) <=
      100.0f)
      {
        print("Reached to the destination point\n"+
        "calculating the next point");
        FindNextPoint();
      }
      //Check the distance with player tank
      //When the distance is near, transition to chase state
      else if (Vector3.Distance(transform.position,
      playerTransform.position) <= 300.0f)
      {
        print("Switch to Chase Position");
        curState = FSMState.Chase;
      }
      //Rotate to the target point
      Quaternion targetRotation =
      Quaternion.LookRotation(destPos
      - transform.position);
      transform.rotation =
      Quaternion.Slerp(transform.rotation,
      targetRotation, Time.deltaTime * curRotSpeed);
      //Go Forward
      transform.Translate(Vector3.forward * Time.deltaTime *
      curSpeed);
    }
    protected void FindNextPoint()
    {
      print("Finding next point");
      int rndIndex = Random.Range(0, pointList.Length);
      float rndRadius = 10.0f;
      Vector3 rndPosition = Vector3.zero;
      destPos = pointList[rndIndex].transform.position +
      rndPosition;
      //Check Range to decide the random point
      //as the same as before
      if (IsInCurrentRange(destPos))
      {
        rndPosition = new Vector3(Random.Range(-rndRadius,
        rndRadius), 0.0f, Random.Range(-rndRadius,
        rndRadius));
        destPos = pointList[rndIndex].transform.position +
        rndPosition;
      }
    }
    protected bool IsInCurrentRange(Vector3 pos)
    {
      float xPos = Mathf.Abs(pos.x - transform.position.x);
      float zPos = Mathf.Abs(pos.z - transform.position.z);
      if (xPos <= 50 && zPos <= 50)
        return true;
        return false;
    }

Trạng thái rượt đuổi (Chase)


Cũng tương tự, đầu tiên xe AI sẽ kiểm tra khoảng cách đến xe tăng người chơi. Nếu đủ gần, nó sẽ chuyển sang trạng thái Attack. Nếu xe tăng người chơi đã đi xa quá, nó sẽ trở lại trạng thái Patrol.

    protected void UpdateChaseState()
    {
      //Set the target position as the player position
      destPos = playerTransform.position;
      //Check the distance with player tank When
      //the distance is near, transition to attack state
      float dist = Vector3.Distance(transform.position,
      playerTransform.position);
      if (dist <= 200.0f)
      {
        curState = FSMState.Attack;
      }
      //Go back to patrol is it become too far
      else if (dist >= 300.0f)
      {
        curState = FSMState.Patrol;
      }
      //Go Forward
      transform.Translate(Vector3.forward * Time.deltaTime *
      curSpeed);
    }

Trạng thái tấn công (Attack)


Nếu xe tăng người chơi đủ gần để tấn công xe tăng AI của chúng ta, chúng sẽ xoay đối tượng tháp pháo turret về phía xe tăng của người chơi, và rồi nổ súng. Nó sẽ trở lại trạng thái Patrol, nếu xe tăng người chơi đã ra khỏi phạm vi.

    protected void UpdateAttackState()
    {
      //Set the target position as the player position
      destPos = playerTransform.position;
      //Check the distance with the player tank
      float dist = Vector3.Distance(transform.position,
      playerTransform.position);
      if (dist >= 200.0f && dist < 300.0f)
      {
        //Rotate to the target point
        Quaternion targetRotation =
        Quaternion.LookRotation(destPos -
        transform.position);
        transform.rotation = Quaternion.Slerp(
        transform.rotation, targetRotation,
        Time.deltaTime * curRotSpeed); 
        //Go Forward
        transform.Translate(Vector3.forward *
        Time.deltaTime * curSpeed);
        curState = FSMState.Attack;
      }
      //Transition to patrol is the tank become too far
      else if (dist >= 300.0f)
      {
        curState = FSMState.Patrol;
      }
      //Always Turn the turret towards the player
      Quaternion turretRotation =
      Quaternion.LookRotation(destPos
      - turret.position);
      turret.rotation =
      Quaternion.Slerp(turret.rotation, turretRotation,
      Time.deltaTime * curRotSpeed);
      //Shoot the bullets
      ShootBullet();
    }
    private void ShootBullet()
    {
      if (elapsedTime >= shootRate)
      {
        //Shoot the bullet
        Instantiate(Bullet, bulletSpawnPoint.position,
        bulletSpawnPoint.rotation);
        elapsedTime = 0.0f;
      }
    }

Trạng thái chết (Dead)

Nếu xe tăng đã đến trạng thái Dead, chúng ta sẽ bật trạng thái chết và làm nó nổ tung.

    protected void UpdateDeadState()
    {
      //Show the dead animation with some physics effects
      if (!bDead)
      {
        bDead = true;
        Explode();
      }
    }

Nổ tung (Explode)



Đây là một hàm sẽ mang đến một hiệu ứng nổ đẹp. Chúng ta chỉ áp dụng một ExplosionForce vào thành phần rigidbody với các hướng ngẫu nhiên, có trong đoạn code sau đây:

    protected void Explode()
    {
      float rndX = Random.Range(10.0f, 30.0f);
      float rndZ = Random.Range(10.0f, 30.0f);
      for (int i = 0; i < 3; i++)
      {
        rigidbody.AddExplosionForce(10000.0f,
        transform.position - new Vector3(rndX, 10.0f,
        rndZ), 40.0f, 10.0f);
        rigidbody.velocity = transform.TransformDirection(
        new Vector3(rndX, 20.0f, rndZ));
      }
      Destroy(gameObject, 1.5f);

    }

Thứ Năm, 6 tháng 3, 2014

Project RPG BÀI 13. SỬ DỤNG GUI TEXTURE CHO THANH MÁU

 


Ở bài viết đầu tiên, chúng ta đã tạo thanh máu cho nhân vật bằng GUI Box, bây giờ, chúng ta sẽ sử dụng GUI Texture để hiển thị thanh máu với sự hỗ trợ của C# Messenger Extended đã tạo ở bài 12.



B1. Double click vào scene Level1 để làm việc với màn này. Tại thẻ Project, nhấp phải vào thư mục Prefab và chọn Create | Prefabs và đặt tên là Player Health bar Prefab.



B2. Tải file guiTexture.rar này về (Tệp / Lưu Ctrl + S) giải nén và kéo thả ảnh redBar.png vào thư mục Skin, kéo thả thư mục monsterMob vào thư mục Assets.

B3. Vào GameObject | Create Other | GUI Texture. Tại thẻ Project, kéo thả file ảnh redBar vào Texture của UnityWatermark-small ở thẻ Inspector vừa tạo ở bước trên và điều chỉnh như sau:


B4. Kéo thả file C# VitalBar ở thẻ Project vào UnityWatermark-small ở thẻ Hierarchy.


B5. Kéo thả UnityWatermark-small vào Player Health bar Prefab ở thẻ Project.


B6. Nhấp chọn UnityWatermark-small ở thẻ Hierarchy và ấn Delete.


B7. Kéo thả Player Health bar Prefab ở thẻ Project vào thẻ Hierarchy.


B8. Tại thẻ Project, nhấp chọn Player Health bar Prefab, ấn Ctrl + D để nhân đôi đối tượng này lên và đổi tên đối tượng mới thành Mob Health bar Prefab.


B9. Kéo thả  Mob Health bar Prefab ở thẻ Project vào thẻ Hierarchy.


B10. Nhấp chọn Mob Health bar Prefab ở thẻ Hierarchy, qua thẻ Inspector, điều chỉnh các thông số như hình sau:


B11. Double click vào file C# VitalBar, xóa tất cả đi và chèn lại đoạn code sau vào:

 /// <summary>
/// VitalBar.cs
///
/// This class is responsble for displaying a vita fpr the player character or a mob...
/// </summary>
using UnityEngine;
using System.Collections;

public class VitalBar : MonoBehaviour {
    public bool _isPlayerHealthBar;

    private int _maxBarLength;
    private int _curBarLength;
    private GUITexture _display;

    // Use this for initialization
    void Start () {
        //_isPlayerHealthBar = true;

        _display = gameObject.GetComponent<GUITexture>();

        _maxBarLength = (int)_display.pixelInset.width;
        OnEnable();
    }
  
    // Update is called once per frame
    void Update () {
  
    }

    //This method is called when the gameobject is enabled
    public void OnEnable(){
        if(_isPlayerHealthBar)
            Messenger<int, int>.AddListener("player health update", OnChangeHealthBarSize);
        else
            Messenger<int, int>.AddListener("mob health update", OnChangeHealthBarSize);
    }

    //this method is called when the gameobject is disabled
    public void OnDisable(){
        if(_isPlayerHealthBar)
            Messenger<int, int>.RemoveListener("player health update", OnChangeHealthBarSize);
        else
            Messenger<int, int>.RemoveListener("mob health update", OnChangeHealthBarSize);

    }

    //This method will calculate the total size of the healthbar in relation to the % of health the target has left
    public void OnChangeHealthBarSize(int curHealth, int maxHealth){
        //Debug.Log("We heard an event: curHealth = " + curHealth + " - maxHealth = " + maxHealth);
        _curBarLength = (int)((curHealth / (float)maxHealth) * _maxBarLength);    //this calculate the current bar length based on the player health %
        _display.pixelInset = new Rect(_display.pixelInset.x, _display.pixelInset.y, _curBarLength, _display.pixelInset.height);
  
    }

    //setting the health bar to the player or mob
    public void SetPlayerHealth(bool b){
        _isPlayerHealthBar = b;
    }
}


B12. Double click vào file C# PlayerCharacter, xóa tất cả đi và chèn lại đoạn code sau vào:

 public class PlayerCharacter : BaseCharacter {
    void Update(){
        Messenger<int, int>.Broadcast("player health update", 80, 100);
    }
}


B13. Tại thẻ Hierarchy, nhấp chọn Mob Health bar Prefab, qua thẻ Inspector, bỏ dấu stick ở mục Is Player Health Bar đi.


B14. Tại thẻ Hierarchy, nhấp chọn Player Health bar Prefab, qua thẻ Inspector, đánh dấu stick vào mục Is Player Health Bar.


B15. Nhấn Ctrl + S để save scene Level1 này lại. Nhấp nút Play để kiểm tra thành quả.


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.

DEVGAME 1.5 - CẤU TRÚC CỦA MỘT TỆP MÃ - BIẾN (Phần 1)

 

Biến là nơi lưu trữ những trạng thái, giá trị, văn bản và nhiều thứ khác nữa, là kho lưu trữ thông tin trong máy. Trong Unity, các biến sẽ được hiển thị trên thẻ Inspector, vì thế, bạn không cần phải mở trình biên tập code để chỉnh sửa bất kì thứ gì về biến. Điều này còn cho phép bạn giữ lại đặc tính riêng của từng đối tượng khác nhau, cho nên bạn có thể dùng cùng một tệp mã (script) cho nhiều đối tượng với các thiết lập khác nhau.


Bây giờ, để thay đổi tốc độ xoay của khối hộp, chúng ta cần thay đổi một vài thứ trong hàm Rotate. Chúng ta áp dụng biến vào hàm này.

  • Mẹo: Tên biến nên đặt thành một từ có nghĩa, phải ngắn gọn. Nếu dài quá có thể viết tắt tên biến và thêm chú thích vào phía sau.
  • Luật: Tên biến không được bắt đầu bởi chữ số, kí tự đặc biệt hoặc có chứa khoảng trắng và có nhiều tên không được đặt. Nên đặt tên biến là kí tự không in hoa, khi tên biến được viết hoa, sẽ xuất hiện khoảng trắng phía trước từ được viết in hoa đó trong thẻ Inspector.

1. Double click vào file java SimpleRotate và bổ sung các phần được tô xanh:

#pragma strict

var myDegrees = 50;

function Update () {
    transform.Rotate(0,myDegrees * Time.deltaTime,0);
}


2. Ấn Ctrl + S để lưu file script lại.

3. Tại thẻ Hierarchy, nhấp vào nút tam giác phía trước Cube Parent để hiển thị đối tượng con Cube.

 

4. Nhấp chọn Cube và nhìn qua thẻ Inspector sẽ thấy hiển thị biến mới.


Chú ý sẽ thấy tên biến giờ đây được viết hoa kí tự đầu và ngăn cách nhau bởi dấu khoảng trắng. Vì khi đặt tên biến, trước kí tự in hoa sẽ được thêm dấu cách và tất cả các từ đều sẽ được in hoa chữ đầu dù rằng trong file script chúng ta viết thường.

5. Nhấp nút Play và bạn sẽ không thấy gì thay đổi cả.

6. Double click vào file java SimpleMove và đổi giá trị 2 thành 0.5 để làm khối hộp di chuyển chậm lại, kéo dài thời gian khối hộp di chuyển ra khỏi màn hình.

7. Lưu file script lại và nhấp chuột chọn Cube ở thẻ Hierarchy.

8. Nhấp nút Play.

9. Tại thẻ Inspector, đổi giá trị của biến My Degrees từ 50 thành 200.




Khối hộp lúc này quay nhanh hơn.

10. Nhấp nút Play lần nữa để ngừng game.

Và lúc này, biến giá trị bị khôi phục về giá trị ban đầu là 50.


  • Luật: Trong Unity, các điều chỉnh trong quá trình game đang chạy (Sau khi nhấn nút Play để chạy game) đều bị mất khi dừng game. Điều này giúp bạn kiểm tra quá trình thực thi mà không làm ảnh hưởng đến các thứ khác. Nếu bạn muốn điều chỉnh thực sự, bạn phải ngừng quá trình chạy game thì mới thay đổi thực sự được.

11. Double click vào file java SimpleRotate và thay đổi giá trị biến thành 100 và lưu file script lại.


Lúc này, giá trị biến ở thẻ Inspector vẫn được giữ nguyên là 50 và không hề bị thay đổi.


12. Nhấp nút Play.

Tốc độ quay vẫn chậm hơn so với giá trị đã được thay đổi trong file script. Bởi giá trị trong thẻ Inspector luôn luôn được sử dụng và không bị thay đổi bởi việc điều chỉnh trong file script.

  • Luật: Khi thay đổi giá trị biến trong thẻ Inspector, chương trình sẽ luôn sử dụng giá trị biến này và bỏ qua giá trị trong file script.

13. Nhấp nút Play lần nữa để dừng.

14. Tại thẻ Inspector, thay đổi giá trị biến thành 200.


15. Nhấp nút Play để kiểm tra thành quả.

  • Chú ý: Nếu bạn đặt tốc độ quay quá nhanh, nó có thể nhấp nháy hoặc quay chậm hơn hoặc bị quay lùi trên các máy tính chạy chậm bởi không thể xử lý được các khung hình có tốc độ nhanh.