Thứ Tư, 30 tháng 4, 2014

COOKBOOK - HIỂN THỊ BIỂU TƯỢNG CÁC MÓN ĐỒ ĐƯỢC NHẶT



Có rất nhiều loại đồ vật mà người chơi có thể nhặt và lưu trữ trong suốt quá trình chơi game như chìa khóa, bình máu, những món đồ tăng điểm.... Một lớp PickUp tổng quát để thực hiện chức năng nhặt những món đồ sẽ rất hữu dụng, và sử dụng GUI để hiển thị biểu tượng của các món đồ có thể được thực hiện thông qua code C# là List < T > để liệt kê các đối tượng.


Chuẩn bị

PickUp.rar


Cách để làm

B1. Vào File | New Project và đặt tên cho project mới là PickUp. Và đánh dấu vào ô Character Controller.





B2. Kéo thả 2 thư mục ảnh vừa tải ở phần Chuẩn bị về vào thẻ Project trong Unity.



B3. Tại thẻ Hierarchy, nhấp chọn nút Create | Terrain và qua thẻ Inspector điều chỉnh Position như hình dưới đây:



B4. Vẫn đang chọn Terrain ở thẻ Hierarchy, qua thẻ Inspector, điều chỉnh như hình sau:



B5. Tại thẻ Project, nhấp chọn nút Create | C# Script và đặt tên là PickUp. Double click vào file này và xóa tất cả đi rồi chèn đoạn code sau vào:

using UnityEngine;
using System.Collections;

public class PickUp : MonoBehaviour
{
    public enum PickUpCategory
    {
        KEY, HEALTH, SCORE
    }
   
    public Texture icon;
    public int points;
    public string fitsLockTag;
    public PickUpCategory catgegory;
   
}


B6. Kéo thả 3rd Person Controller trong thư mục Standard Assets | Character Controllers ở thẻ Project vào thẻ Hierarchy.



B7. Tạo file C# với tên là GeneralInventory và kéo thả file C# này vào 3rd Person Controller ở thẻ Project.

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

public class GeneralInventory : MonoBehaviour
{
    const int ICON_HEIGHT = 32;
    private List<PickUp> inventory = new List<PickUp>();
  
    private void OnGUI()
    {
        // restrict display to left of screen
        Rect r = new Rect(0,0,Screen.width/2, ICON_HEIGHT);
        GUILayout.BeginArea(r);
        GUILayout.BeginHorizontal();
      
        DisplayInventory();
      
        GUILayout.FlexibleSpace();
        GUILayout.EndHorizontal();
        GUILayout.EndArea();
    }
  
    private void DisplayInventory()
    {
        foreach (PickUp item in inventory)
        {
            GUILayout.Label( item.icon );
        }
    }
  
    private void OnTriggerEnter(Collider hitCollider)
    {  
        if( "pickup" == hitCollider.tag )
        {
            PickUp item = hitCollider.GetComponent<PickUp>();
            inventory.Add( item );
            Destroy ( hitCollider.gameObject );
        }
    }
}


B8. Nhấp chọn 3rd Person Controller ở thẻ Hierarchy và qua thẻ Inspector điều chỉnh như sau:



B9. Tại thẻ Hierarchy, nhấp chọn nút Create | Cube và đặt tên là Cube-health. Kéo thả file C# PickUp ở thẻ Project vào Cube-health vừa tạo ở thẻ Hierarchy. Nhấp chọn Cube-health và qua thẻ Inspector điều chỉnh như sau:


B10. Thực hiện tương tự B9 để tạo ra các Cube-key và Cube-health với các icon khác nhau. Nhớ điều chỉnh Categories cho phù hợp nhé.


B11. Kéo thả các file ảnh pickup-items vào từng khối hộp để gán vật liệu cho chúng. VD: Cube-key có icon là key_yellow_icon thì kéo file key_yellow làm vật liệu.


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


Nguyên lí làm việc

Lớp PickUp không chứa hàm nhưng nó bao gồm nhiều biến public hữu dụng. Nó còn có một kiểu dữ liệu liệt kê - enum để phân loại đồ vật thành 3 nhóm là: KEY, HEALTH và SCORE. Mỗi đồ vật được lớp PickUp định nghĩa với một biểu tượng, một điểm số nguyên, một chuỗi (cho mỗi món đồ chìa khóa, tag ), và tùy theo món đồ vật mà nó sẽ được xếp vào mỗi mục phân loại đồ vật khác nhau.

Các đối tượng trong game đều được gắn script PickUp. Trong bài viết này, những khối hộp với các hình ảnh để mô tả phân loại đồ vật của chúng, nhưng các đồ vật người chơi gặp trong thế giới game có thể các món đồ tương tác ba chiều hoặc bất kể là gì. Mỗi món đồ trong game với script PickUp cần điều chỉnh thuộc tính một cách thích hợp. Ví dụ như, chìa khóa màu vàng phải có biểu tượng là chìa khóa màu vàng, được đặt category là KEY và phải đặt Fits Lock Tag là chiếc rương mà nó có thể mở.

Nhân vật của người chơi được gắn script GeneralInventory. Script này sử dụng GUI với 2 hàm chính: đầu tiên là List<T> thu nhận thông tin các món đồ vật được người chơi đang có, và hiển thị ra màn hình các biểu tượng của từng món đồ đó thông qua hàm OnGUI. Hàm chính thứ hai là để kiểm tra va chạm với đồ vật có thể nhặt thông qua hàm OnTriggerEnter, và mỗi đối tượng được đặt tag là pickup khi va chạm sẽ được thêm vào danh sách hành trang.


Còn nữa

Các đồ vật được nhặt nhưng không hiển thị

Với các mục phân loại đồ vật khác nhau, việc xử lý khi va chạm cũng sẽ khác nhau. Như việc người chơi nhặt được món đồ Health thì điểm sẽ được cộng vào máu của người chơi và khi đó món đồ sẽ bị mất đi thay vì được thêm vào hành trang. Việc này cần dùng đến hàm If trong hàm OnTriggerEnter để quyết định sẽ làm gì khi một món đồ được nhặt.

Xóa món đồ từ danh sách nhặt List<>

Khi bạn mở một cánh cửa cần chìa khóa thì chiếc chìa khóa trong hành trang bạn phải mất sau khi đã mở, vì vậy bạn cần phải xóa nó ra khỏi danh sách hành trang. Để thực hiện điều này, bạn sẽ cần dùng đến hàm If bên trong hàm OnTriggerEnter để phát hiện va chạm. Ở đây mình sử dụng chìa khóa vàng để mở cửa và xóa nó ra khỏi danh sách hành trang.






if( "yellowDoor" == hitCollider.tag )
    OpenDoor(hitCollider.gameObject);


Hàm OpenDoor sẽ cần được khai báo món đồ nào trong danh sách có thể mở được cửa và nếu tìm thấy món đồ đó sẽ xóa nó khỏi danh sách hành trang. Sau đó cửa sẽ mở thông qua hàm sau:

     private void OpenDoor(GameObject doorGO)
    {
        // search for key to open the tag of doorGO
        int colorKeyIndex = FindItemIndex(doorGO.tag);
        if( colorKeyIndex > -1 )
        {
            // remove key item
            inventory.RemoveAt( colorKeyIndex );
          
            // now open the door ...
            doorGO.animation.Play ("open");
        }
    }
  
    private int FindItemIndex(string doorTag)
    {
        for (int i = 0; i < inventory.Count; i++)
        {
            PickUp item = inventory[i];
            if( item.fitsLockTag == doorTag )
                return i;
        }
      
        // not found, return -1
        return -1;
    }

Thứ Hai, 28 tháng 4, 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.

Chủ Nhật, 27 tháng 4, 2014

Project RPG BÀI 15. MOB CLASS



Bài viết này mình sẽ sử dụng model mob đã download về từ bài 13 để import kẻ địch cho nhân vật vào scene.



B1. Tại thẻ Project, double click vào scene Level1 nằm trong thư mục Scenes để mở scene này lên.Ở thẻ Hierarchy sẽ bao gồm các đối tượng sau:


B2. Tại thẻ Project, vào thư mục Assets | monsterMob đã tải ở Bài 13 và nhấp chọn monsterMob, qua thẻ Inspector điều chỉnh như sau:

 

B3. Vẫn ở thẻ Project, kéo thả prefab monsterMob vào thẻ Hierarchy. Qua thẻ Inspector, nhấp phải vào Animator và chọn Remove Component. Vào Component | Miscellanious | Animation và điều chỉnh như sau:


B4. Nhấp phải vào thư mục Script ở thẻ Project và chọn Create | C# Script và đặt tên là Mob. Double click vào file C# vừa tạo và xóa tất cả đi chèn đoạn code sau vào:

using UnityEngine;
using System.Collections;

public class Mob : BaseCharacter {

    // Use this for initialization
    void Start () {
        GetPrimaryAttribute ((int)AttributeName.Constituion).BaseValue = 100;
        GetVital((int)VitalName.Health).Update ();
   
    }
   
    // Update is called once per frame
    void Update () {
        Messenger<int, int>.Broadcast("mob health update", 80, 100);
    }
}


B5. Kéo thả file C# Mob vào monsterMob ở thẻ Hierarchy.


B6. Tại thẻ Project, nhấp phải vào thư mục Prefabs và chọn Create | Prefab và đặt tên là mob_Slug.


B7. Kéo thả monsterMob ở thẻ Hierarchy vào mob_Slug ở thẻ Project | Prefabs.


B8. Nhấp chọn monsterMob ở thẻ Hierarchy và nhấn Delete.


B9. Kéo thả mob_Slug ở thẻ Project vào thẻ Hierarchy.


B10. Nhấp chọn mob_Slug ở thẻ Hierarchy, qua thẻ Inspector, nhập tọa độ vào như sau:

 

B11. Tại thẻ Hierarchy, nhấp chọn mob_Slug và ấn Ctrl + D 2 lần và kéo 2 đối tượng vừa tạo sang 2 phía như hình sau:


B12. Ấn Ctrl + S để save scene Level1 lại.

Thứ Bảy, 19 tháng 4, 2014

DEVGAME 1.6 - NHẤP CHỌN ĐỐI TƯỢNG VỚI HÀM ONMOUSEDOWN

 

Khi bạn vừa bắt đầu bước từng bước trong việc viết code ở chuỗi bài viết DevGame, hãy tạm ngưng giây lát để tìm hiểu về sự khác biệt giữa game bắn súng góc nhìn thứ nhất với những game nhấp chuột phiêu lưu đơn giản.

Thông thường, các sự chú ý đều dựa trên các vấn đề: Bạn sử dụng vũ khí để phá cửa và xông vào. Bạn bắt gặp kẻ địch trong tầm ngắm và chúng quay sang tấn công bạn. Hoặc bạn công phá những thùng gỗ với khẩu súng phóng tên lửa, và tên lửa với những yếu tố vật lý phá tan những cái thùng gỗ bất hạnh đó. Trong những thể loại game nhấp chuột đơn giản, thông thường con trỏ chuột sẽ là tâm điểm chú ý, hơn cả việc xảy ra va chạm, các yếu tố vật lý, và hướng sáng.

B1. Mở trình đơn giúp đỡ bằng cách vào Help | Scripting Reference.


B2. Gõ vào khung search dòng chữ "On Mouse Down" và nhấn enter



B3. Nhấp chọn dòng MonoBehavior.OnMouseDown.


Trở lại Unity Editor, chúng ta sẽ tạo thêm một file JavaScript mới.

B4. Nhấp phải vào thư mục My Scripts và chọn Create | JavaScript và đặt tên là MousePick.


B5. Double click vào file Java vừa tạo và xóa hàm Start đi.

#pragma strict

function Update ( ) {

}


B6. Đổi tên hàm Update thành OnMouseDown như ví dụ trong Scripting Reference.

#pragma strict

function OnMouseDown ( ) {

}


B7. Ấn Ctrl + S để save script này lại.

Ví dụ trong mục giúp đỡ Scripting Reference thực hiện chức năng hiển thị một scene mới, nhưng mục đích của chúng ta chỉ là việc hiển thị xem đối tượng có được nhấp hay không thôi. Không giống như hàm Update, hàm OnMouseDown chỉ được gọi mỗi lần nút chuột được nhấp.

Chủ Nhật, 13 tháng 4, 2014

ProjectGame 1.2 - Platform Game Đơn Giản - Tạo nhân vật 2D



Trong bài này, chúng ta sẽ tạo nhân vật 2D và vật liệu từ các file ảnh đã download ở bài viết 1.1. Chúng ta sẽ có nhân vật với 3 kiểu hành động khác nhau: đứng yên, đi bộ và nhảy.



Mục tiêu

1.1 - Tạo Camera và Level
>> 1.2 - Tạo nhân vật 2D
1.3 - Tạo class (lớp) CharacterController và SpriteManager
1.4 - Nhảy và yếu tố vật lý
1.5 - Tạo chìa khóa và mở cửa
1.6 - Chèn âm thanh và nút Replay

Kiểm tra tài nguyên

Tại thẻ Project, các bạn vào thư mục Game Project 1 | Characters sẽ thấy 3 thư mục là Stay, Walk và Jump. Mở từng thư mục và kiểm tra xem đã có đủ các file như hình dưới đây chưa:



Bắt tay vào làm việc

Nhân vật của chúng ta chỉ bao gồm các hình ảnh 2D nên chỉ cần sử dụng plane để tạo nhân vật là đủ.

B1. Tại thẻ Project, vào thư mục Game Project 1 | FBX và kéo thả prefab Plane vào thẻ Hierarchy.


B2. Nhấp chọn Plane ở thẻ Hierarchy, qua thẻ Inspector, nhấp phải vào Animator và chọn Remove Component.



B3. Vẫn ở thẻ Inspector, điều chỉnh các giá trị như hình sau:


B4. Tại thẻ Hierarchy, nhấp phải vào Plane và chọn Rename, gõ tên lại thành Player như hình sau:


B5. Tại thẻ Project, nhấp chọn nút Create | Material và đặt tên là M_Character.


B6. Nhấp chọn M_Character ở thẻ Project, qua thẻ Inspector và điều chỉnh như sau:
  • Shader: Transparent| Cutout| Soft Edge Unlit
  • Base (RGB) Alpha (A)
  • Kéo thả s_set.png trong thư mục Characters | Stay vào texture thumbnail
  • X: Tiling: 0.5, Offset: 0
    ‰Y: Tiling: 1, Offset: 0
  • Base Alpha cutoff: Kéo về tận cùng bên phải


B7. Tại thẻ Project, kéo thả M_Character vào Player ở thẻ Hierarchy.


B8. Nhấp chọn Player ở thẻ Hierarchy, vào Component | Physics | Box Collider, và qua thẻ Inspector điều chỉnh như sau:
  • Size: x = 0.4, y = 0.875, z = 1
  • Center: x = 0, y = -0.06275, z = 0


B9. Tiếp theo, chúng ta sẽ tạo Rigid body để hỗ trợ việc tính toán tốc độ di chuyển, nhảy và phát hiện va chạm với địa hình. Nhấp chọn Player ở thẻ Hierarchy, Component | Physics | RigidBody. Qua thẻ Inspector điều chỉnh như hình sau:



Hoàn thành mục tiêu 1.2

Chúng ta vừa tạo plane để mô phỏng nhân vật 2D cho người chơi. Chúng ta cũng vừa tạo vật liệu cho nhân vật bằng cách sử dụng  Transparent | Cutout | Soft Edge Unlit Shader. Shader này sẽ loại bỏ kênh Alpha và làm nó trong suốt. Trong khi đó, cũng sử dụng tính năng làm mượt đường viền của bức ảnh. Chúng ta có thể điều chỉnh việc phần nào được loại bỏ và độ mượt của đường nét dựa vào tính năng của thanh trượt Base Alpha Cutoff.

Chúng ta cũng đã điều chỉnh Tiling của trục X = 0.5 bởi vì file ảnh chứa 2 khung hình, như chúng ta chỉ cần sử dụng 1 khung hình cho một thời điểm. Chúng ta đã sử dụng Box Collider thay vì Mesh Collider. Cũng đã thêm RigidBody cho nhân vật và điều chỉnh đóng băng các góc xoay, để không sử dụng các tính năng xoay quanh các trục tọa độ mà sử dụng đến Engine Vật lý của Unity. Vì vậy Player của chúng ta không thể xoay tròn được. Rigid Body sẽ cung cấp cho nhân vật các tính chất để kích hoạt Engine Vật lý trong Unity, như lực hút (gravity) hoặc vận tốc (velocity), và các hoạt động vật lý như trong đời thực. Chúng ta sẽ hiểu rõ hơn trong bài viết sau.


Kiến thức bổ sung

Tại sao lại đóng băng các trục xoay và vị trí trong RigidBody?

Chúng ta cần đóng băng góc xoay vì chúng ta sử dụng các sprite để mô phỏng việc di chuyển của nhân vật. Vì vậy chúng ta không cần nhân vật phải xoay hướng nhìn khi di chuyển. Chúng ta cũng đóng băng việc di chuyển trên trục Z bởi nhân vật chỉ di chuyển trên trục X và Y. Trong trường hợp này, chúng ta có thể tiết kiệm được chu trình xử lý của CPU bởi Unity sẽ vô hiệu hóa các tính toán không cần thiết và tập trung vào thực hiện những phần cần thiết khác.

Box Collider và Mesh Collider

Vậy tại sao chúng ta lại dùng Box Collider thay vì Mesh Collider? Cả hai collider này hầu hết đều tương tự nhau. Mỗi bề mặt của vật thể điều có các trục vuông góc đặt ở mỗi đỉnh để kiểm tra có va chạm với các vật thể khác hay không. Với vật thể plane chúng ta dùng để tạo nhân vật thì chỉ có một bề mặt hướng thẳng đến camera. Vì thế nếu chúng ta dùng Mesh Collider, chúng ta sẽ không nhận được giá trị nào phát hiện việc va chạm của các bề mặt trên, dưới, trái, phải và phía sau cả. Đơn giản là bởi vì plane chỉ có bề mặt phía trước mà thôi.

 

Bên cạnh đó, Box Collider sử dụng hình khối để kiểm tra phát hiện va chạm. Kết quả này sẽ nhanh hơn nhiều so với Mesh Collider. Trong trường hợp này, chúng ta kiểm tra hình khối của nhân vật với địa hình để xem phần nào va chạm, phần nào không.

Box Collider sẽ tiết kiệm nhiều bộ nhớ và CPU hơn Mesh Collider.


Kế tiếp, chúng ta sẽ bàn về Tiling của vật liệu, tương tự như các phần mềm 3D khác. Mỗi Texture đều được áp vào vật liệu vì thế chúng sẽ được kéo dãn ra để phủ kín vật thể.


Tiling rất giống với scaling, và cơ bản là việc lặp lại texture theo trục X và Y. Vì vậy nếu chúng ta đặt Tiling X = 0.5 và Y = 1.0, chúng ta sẽ thấy kết quả ở hình bên trên trục X chỉ bằng phân nữa và trục Y vẫn giữ nguyên. Chúng ta sẽ thấy, hình bên phải chỉ hiển thị khung hình thứ nhất (hình đứng yên bên trái trong khối hộp bên trái). Vậy để hiển thị hình bên phải trong khối hộp bên trái thì làm như thế nào? Chúng ta sẽ cần dùng đến tính năng Offset.Offset sẽ cho phép chúng ta điều chỉnh vị trí bắt đầu của texture. Nếu chúng ta điều chỉnh Offset là X = 0, Y = 0 thì đồng nghĩa với việc sẽ bắt đầu hiển thị texture từ góc trên bên trái. Và nếu điều chỉnh X = 0.5 thì texture sẽ hiển thị bắt đầu từ điểm giữa góc trên, đồng nghĩa với việc hiển thị hình ảnh bên phải của khối hộp bên trái. Xem hình dưới đây sẽ rõ:


Chúng ta có thể điều chỉnh Tiling thông qua hàm material.mainTextureScale và sử dụng hàm material.mainTextureOffset để điều chỉnh Offset.

Sau khi đã hiểu về nguyên lý làm việc ở phần này, chúng ta sẽ có thể tiến hành xử lý sprite ảnh của chúng ta bằng cách thay đổi trị số Tiling và Offset của vật liệu ở bài viết tiếp theo.







Thứ Năm, 10 tháng 4, 2014

COOKBOOK - HIỂN THỊ TÊN ĐỒ VẬT ĐƯỢC NHẶT


Hầu hết các game đều có những món đồ vật buộc người chơi phải chạy đến để nhặt. Điển hình như việc nhặt một chiếc chìa khóa để mở một cánh cổng nào đó để đi tiếp chẳng hạn. Bài viết này sẽ hướng dẫn các bạn sử dụng collider để thực hiện điều đó.



Chuẩn bị

TextureImage.rar


Cách để làm

B1. Vào File | New Project và đặt tên cho project mới là Player Inventory. Và đánh dấu vào ô Character Controller.


B2. Kéo thả các file ảnh vừa tải ở phần Chuẩn bị về vào thẻ Project trong Unity.


B3. Tại thẻ Hierarchy, nhấp chọn nút Create | Terrain và qua thẻ Inspector điều chỉnh Position như hình dưới đây:


B4. Vẫn đang chọn Terrain ở thẻ Hierarchy, qua thẻ Inspector, điều chỉnh như hình sau:



B5. Vẫn tại thẻ Hierarchy, nhấp chọn nút Create | Cube đổi tên thành Cube-key và qua thẻ Inspector điều chỉnh như sau:


B6. Kéo thả file ảnh key_yellow vào Cube-key ở thẻ Hierarchy như sau:


B7. Kéo thả 3rd Person Controller trong thư mục Standard Assets | Character Controllers ở thẻ Project vào thẻ Hierarchy.


B8. Nhấp chọn 3rd Person Controller ở thẻ Hierarchy và qua thẻ Inspector điều chỉnh như sau:


B9. Tại thẻ Project, nhấp chọn nút Create | C# Script và đặt tên là PlayerInventory, chèn đoạn code sau vào:
 
using UnityEngine;
using System.Collections;

public class PlayerInventory : MonoBehaviour
{
    private bool isCarryingKey = false;
   
    private void OnGUI()
    {
        string keyMessage = "(bag is empty)";
        if( isCarryingKey )
        {
            keyMessage = "carrying: [ key ]";   
        }
       
        GUILayout.Label ( keyMessage );
    }
   
    private void OnTriggerEnter(Collider hitCollider)
    {
        if( "key" == hitCollider.tag )
        {
            isCarryingKey = true;
            Destroy ( hitCollider.gameObject );
        }
    }
}


B10. Kéo thả file C# PlayerInventory ở thẻ Project vào 3rd Person Controller ở thẻ Hierarchy.


B11. Tại thẻ Hierarchy, nhấp chọn nút Create | Directional Light.


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



Nguyên lý làm việc

Biến luận lý CarryingKey biểu diễn việc người chơi có đang mang chiếc chìa khóa trong người tại bất kỳ thời điểm nào hay không.

Trong hàm OnGUI( ), biến mang chuỗi ký tự thông báo keyMessagestring được hiển thị thông qua hàm GUILayout.Label( ). Giá trị ban đầu của chuỗi này là thông báo việc hành lý của người chơi đang rỗng. Nhưng nếu hàm điều kiện If kiểm tra giá trị của CarryingKey là đúng thì thông báo sẽ được thay đổi rằng người chơi đang mang chiếc chìa khóa trong người.

Hàm OnTriggerEnter( ) sẽ kiểm tra chuỗi tag của bất kỳ vật thể nào mà 3rd chacracter controller va chạm vào nếu đối tượng đã được đánh dấu vào hôp chọn Is Trigger.

Mỗi lần nhân vật chạm vào bất kì vật thể nào, giá trị của Is Trigger sẽ được chuyển thành True, một thông báo sự kiện OnTriggerEnter( ) đều được gửi đến cả 2 vật thể đang va chạm.

Hàm OnTriggerEnter( ) được gán cho nhân vật sẽ kiểm tra tag của đối tượng, nếu tag là key thì hàm điều kiển sẽ phát hiện va chạm với Cube-key và sẽ thực hiện 2 hành động sau:
Điều chỉnh biến luận lý CarryingKey thành True.
Hủy đối tượng vừa va chạm (Trong trường hợp này là Cube-key).


Còn nữa...

So sánh tag của đối tượng bằng hàm chuỗi đặc biệt

So sánh chuỗi tag của đối tượng với một chuỗi là cách thông dụng mà Unity đã cung cấp. Hàm này có tên là CompareTag( ), và mang biến là một chuỗi. Vì vậy chúng ta có thể viết lại hàm điều kiện trong hàm OnTriggerEnter( ) như sau:

if( hitCollider.CompareTag("key") )