Hiển thị các bài đăng có nhãn Tài liệu. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Tài liệu. Hiển thị tất cả bài đăng

Thứ Ba, 6 tháng 5, 2014

DEVGAME 1.7 - IN THÔNG BÁO LÊN CONSOLE



Một cách cơ bản nhất để kiểm tra hàm có thực hiện hay không là in thông báo lên console để kiểm tra.


Trở lại cửa sổ web Script Reference, gõ vào khung search dòng chữ "print to console". Khi đó bạn sẽ thấy trong kết quả tìm kiếm có dòng "Debug.Log - Logs message to the Unity Console" ( Thông báo ghi chú lên Console ). Nhấp chọn vào kết quả này để xem cách thức sử dụng của hàm này. Ngoài ra, lần sau, bạn cũng có thể giành chút thời gian để xem qua hàm MonoBehaviour.print cũng tương tự như Debug.Log và dễ nhớ hơn.

B1. Chèn dòng code tô đỏ dưới đây vào hàm OnMouseDown( ) như sau:

#pragma strict

function OnMouseDown ( ) {

    Debug.Log("This object was picked");

}


B2. Lưu file script lại.

Phần hướng dẫn trên web có để là "Hàm được gọi mỗi khi nhấp chuột lên button của GUI Element hoặc Collider". Ở đây chúng ta không sử dụng GUI nên chúng ta phải tạo collider.

B3. Trở lại Unity Editor, nhấp chọn Cube ở thẻ Hierarchy và qua thẻ Inspector bạn sẽ thấy nó đã có collider sẵn:



B4. Để dễ nhấp trúng khối hợp hơn bạn nên tắt phần dịch chuyển đi. Tại thẻ Hierarchy, nhấp chọn Cube Parent và qua thẻ Inspector điều chỉnh như sau:


B5. Kéo thả file Js (JavaScript) MousePick vào Cube ở thẻ Hierarchy như sau:
 

B6. Nhấn nút Play và thử nhấp vào Cube.

B7. Nhấn Ctrl + Shift + C để xem thông báo trong hộp thoại Console.
 

Lưu ý: Nếu không thấy thông báo xuất hiện, chắc rằng bạn đã kéo thả file Js PickUp vào Cube, không phải Cube Parent nhé. Vì Cube Parent không có collider.

Vật thể không cần phải hiện hình hoặc được render mới có thể nhấp chọn. Điều này giúp tăng tính sáng tạo thêm trong các môi trường game.

B8. Nhấp chọn Cube ở thẻ Hierarchy và nhớ vị trí của nó.

B9. Qua thẻ Inspector, bỏ chọn Mesh Render để ẩn khối hộp đi.


Khối hộp đã được ẩn khỏi khung nhìn Game. Trong khung nhìn Scene, mặc dù nó vẫn còn thấy các cạnh màu xanh của khối hộp khi nhấp vào Box Collider ở thẻ Inspector.


B10. Nhấp nút Play và rà chuột nhấp chọn vào chỗ của khối hộp đã được ẩn.

Bạn sẽ thấy dù khối hộp không hiển thị lên màn hình nhưng khi nhấp chuột thông báo vẫn xuất hiện trên Console.

B11. Nhấp nút Play để thoát khỏi quá trình chạy game. Nhấp chọn Cube ở thẻ Hierarchy và qua thẻ Inspector mở Mesh Render lên lại và tắt nút kế bên tên Cube đi như sau:


Khi đó bạn sẽ thấy khối hộp không còn thấy nữa trên khung nhìn Scene kể cả khi bấm vào Box Collider, còn trên thẻ Hierarchy bạn sẽ thấy Cube chuyển sang màu tối hơn


B12. Hãy nhấn nút Play và nhấp chuột vào vùng mà bạn đã nhớ vị trí của khối hộp, lúc này sẽ không có thông báo xuất hiện khi nhấp trúng vào khối hộp nữa.

Trường hợp tương tự sẽ xuất hiện nếu bạn bật trở lại và tắt Box Collider đi vì lúc đó không có collider để kiểm tra va chạm nữa. Nên thông báo sẽ không hiện lên hộp thoại Console.

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.

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.

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") )

Thứ Ba, 8 tháng 4, 2014

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

 

Ở bài viết trước, chúng ta đã cài đặt biến kiểu số nguyên vào script SimpleRotate để điều chỉnh tốc độ quay của khối hộp. Bài viết này, chúng ta sẽ tiếp tục cài đặt biến với các kiểu dữ liệu khác nhau.

Còn nhiều thứ khác bạn cần phải tìm hiểu về biến bởi nó có vài kiểu khác nhau. Trong script SimpleRotate, chúng ta đã cài đặt tốc độ quay của khối hộp thông qua biến myDegrees = 50, chương trình sẽ mặc định biến này thuộc kiểu dữ liệu int ( số nguyên, không có phần thập phân ). Nếu bạn muốn sử dụng số thực ( số có phần thập phân ) như 50.0 thì bạn phải đổi nó sang kiểu float ( kiểu số thực ). Nhiều biến bắt buộc phụ thuộc vào cách thiết lập kiểu dữ liệu của chúng để xử lý cho các công việc tiếp theo.

Hãy mở file SimpleMove lên và thêm vài dòng script vào để tiến hành tìm hiểu.

B1. Double click vào file java SimpleMove ở thẻ Project và chèn thêm các dòng được tô đỏ như sau:

#pragma strict
var mySpeed : float;
var someString : String = "This is a test";
var someSetting : boolean = true;
var someObject : GameObject;

function Update () {
    //transform.Rotate(0,50* Time.deltaTime,0);
    transform.Translate(2* Time.deltaTime, 0,0);
}

Hãy cùng nhìn lại đoạn code vừa được thêm vào:
  • var mySpeed : float; : Định nghĩa kiểu float ( số thực ) cho biến mySpeed nhưng cài đặt giá trị.
  • var someString : String = "This is a test"; : Định nghĩa kiểu String ( chuỗi ) và cài đặt biến này mang giá trị là các ký tự nằm trong dấu ngoặc kép " ". Dạng chuỗi thường được đặt giữa dấu ngoặc kép " " và có màu đặt trưng của kiểu chuỗi trong trình biên tập code của Unity.
    >>Chú ý: Nếu bạn copy và paste văn bản kể cả dấu ngoặc kép từ trình soạn thảo văn bản, các ký tự có thể bị thay đổi, khi đó chương trình biên tập code sẽ báo lỗi, buộc bạn phải tự nhập lại đoạn văn bản.
      • var someSetting : boolean = true; : Định nghĩa biến kiểu Boolean ( luận lý, chỉ mang giá trị True hoặc False) và cài đặt nó mang giá trị True. Ở thẻ Inspector, biến Boolean được hiển thị dưới dạng kiểu hộp đánh dấu checkbox.

      B2. Lưu đoạn script lại và qua Unity Editor, nhấp chọn Cube Parent ở thẻ Hierarchy, qua thẻ Inspector, nhìn vào script SimpleMove đã được gắn ở bài viết trước và xem sự khác biệt.

        >>Mẹo: Ngay cả khi kiểu dữ liệu của biến là bất kỳ kiểu nào cũng được và không bắt buộc phải định nghĩa. Nếu bạn quyết định định nghĩa, thì bạn nên save lại để tránh việc chương trình tự động gán kiểu dữ liệu cho biến. Nếu bạn lập trình trên nền tảng IOS hay Android, bạn nên định nghĩa rõ ràng. Trong loạt bài viết về DevGame này, mình sẽ sử dụng cả hai cách trên.

          Chú ý rằng, phía trước tất cả tên biến điều có từ var. Trong Unity, var được định nghĩa cho các biến public ( biến toàn cục ). Nếu một hàm hay một biến được khai báo là public thì những lớp khác có thể gọi sử dụng trực tiếp được. Ngược lại nếu hàm hay biến đó được khai báo là private thì chỉ những thành viên trong lớp đó mới có thể sử dụng được thôi.

          B3. Bây giờ thử thêm từ private vào phía trước var someSetting và save đoạn script lại.



          B4. Nhấp chọn Cube Parent ở thẻ Hierarchy, qua thẻ Inspector và để ý rằng biến mang kiểu luận lý Some Setting đã biến mất.


          Ngoài ra, trong đoạn script vừa được thêm vào còn có:
          • varsomeObject : GameObject; : Kiểu dữ liệu xuất hiện hầu hết trong các ngôn ngữ lập trình, Unity cho phép bạn định nghĩa biến của bất kể vật thể cấp cao như GameObject, hay thấp hơn là các Transform ( xoay, kéo, co dãn đối tượng) và thậm chí là các loại đối tượng khác như Camera hoặc Light ( đèn ).  

          Một đoạn script có thể truy cập đến các biến hay hàm của đối tượng khác phải được định nghĩa như một giá trị của biến. Tuy nhiên, tên thuộc tính của vật thể là duy nhất, không được trùng nên phải đặt sao cho dễ hình dung công dụng nhất.

          B6. Nhấp chọn Cube Parent ở thẻ Hierarchy. Kéo thả Camera hoặc Directional Light ở thẻ Hierarchy vào biến Some Object ở thẻ Inspector.


          B7. Lưu đoạn script và qua Unity Editor lưu project của bạn lại với tên Demo.



          Thứ Tư, 26 tháng 3, 2014

          COOKBOOK - HIỂN THỊ ĐỒNG HỒ ĐẾM NGƯỢC BẰNG GUI LABEL



          Trong nhiều game quy định thời gian làm nhiệm vụ hay kiếm thêm điểm thưởng bằng cách quy định một khoảng thời gian nhất định nào đó. Trong bài viết này, mình sẽ hướng dẫn các bạn tạo đồng hồ đếm ngược đơn giản bằng GUI.


          Cách để làm

          B1. Tạo một project mới và đặt tên là Coundown như hình sau:


          B2. Tại thẻ Project, nhấp chọn Create | C# Script và đặt tên là CountdownTimer. Double click vào file C# vừa tạo và chèn đoạn code sau vào rồi save file C# lại:

          // file: CountdownTimer.cs
          using UnityEngine;
          using System.Collections;

          public class CountdownTimer : MonoBehaviour {
              private float secondsLeft = 10f;
             
              private void OnGUI(){
                  if( secondsLeft > 0)
                      GUILayout.Label("Countdown seconds remaining = " + (int)secondsLeft     );
                  else
                      GUILayout.Label("Countdown has finished");
                 
              }
             
              private void Update(){
                  secondsLeft -= Time.deltaTime;
              }
          }


          B3. Kéo thả file C# vừa tạo vào Main Camera ở thẻ Hierarchy.


          B4. Ấn nút Play để kiểm tra thành quả.


          Nguyên lý làm việc

          Với mỗi khung hình được load, biến secondsLeft sẽ bị trừ dần bởi hàm thời gian Time.deltaTime. Khi thời gian nhỏ hơn 0, đồng hồ sẽ ngừng đếm. Giá trị của biến có thể được ép sang kiểu số nguyên một cách dễ dàng bằng cách thêm (int) vào trước tên biến.

          Thứ Ba, 11 tháng 3, 2014

          COOKBOOK - HIỂN THỊ RADAR

           

          Radar hiển thị các đối tượng khác liên quan đến người chơi, thông thường radar sẽ là hình tròn và tâm của nó là nơi player đang đứng, và mỗi chấm tròn trên radar tượng trưng cho các đối tượng khác. Radar tinh vi sẽ hiển thị các chấm tròn khác nhau với từng loại đối tượng.


          Chuẩn bị
          Radar.rar

          Cách để làm

          B1. Tại thẻ Hierarchy, nhấp chọn nút Create | Directional Light. Qua thẻ Inspector, điều chỉnh như sau:



          B2. Tiếp tục nhấp chọn nút Create | Terrain và điều chỉnh ở thẻ Inspector như sau:


          B3. Vào Assets | Import Package | Character Controller và nhấp nút Import. Tại thẻ Project, kéo thả 3rd Person Controller vào thẻ Hierarchy. Qua thẻ Inspector, điều chỉnh Position thành X = 0, Y = 1, Z = 0.

           

          B4.  Tại thẻ Hierarchy, nhấp chọn nút Create | Cube. Qua thẻ Inspector, điều chỉnh Position thành X = 0, Y = 1, Z = 5.


          B5. Vẫn đang chọn Cube, ở thẻ Inspector, bấm chọn thanh sổ phía sau Tag và chọn Add Tag. Nhập vào Size Element 0 là cube.



          B6. Nhấp chọn cube ở thẻ Hierarchy, qua thẻ Inspector, đặt tag là cube.


          B7. Tại thẻ Hierarchy, nhấp chọn Create | Cube. Qua thẻ Inspector, điều chỉnh Position thành X = -5, Y = 1, Z = 0. Và đặt tag là cube.


          B8. Tại thẻ Project, nhấp chọn nút Create | C# Script và đặt tên là Radar. Chèn đoạn code sau vào.

          using UnityEngine;
          using System.Collections;

          public class Radar : MonoBehaviour
          {
              const float MAX_DISTANCE = 20f;
              const int RADAR_SIZE = 128;
             
              public Transform playerController;
              public Texture radarBackground;
              public Texture targetBlip;
             
              private void OnGUI()
              {
                  // background displaying top left in square of 128 pixels
                  Rect radarBackgroundRect = new Rect(0,0, RADAR_SIZE, RADAR_SIZE);
                  GUI.DrawTexture(radarBackgroundRect,radarBackground);
                 
                  // find all 'cube' tagged objects
                  GameObject[] cubeGOArray = GameObject.FindGameObjectsWithTag("cube");
                 
                  // draw blips for all within distance
                  Vector3 playerPos = playerController.transform.position;
                  foreach (GameObject cubeGO in cubeGOArray) 
                  {
                      Vector3 targetPos = cubeGO.transform.position;
                      float distanceToTarget = Vector3.Distance(targetPos,playerPos);
                      if( (distanceToTarget <= MAX_DISTANCE) )
                          DrawBlip(playerPos, targetPos, distanceToTarget);
                  }
              }
             
              private void DrawBlip(Vector3 playerPos, Vector3 targetPos, float distanceToTarget)
              {
                  // distance from target to player
                  float dx =  targetPos.x - playerPos.x;
                  float dz =  targetPos.z - playerPos.z;
                 
                  // find angle from player to target
                  float angleToTarget = Mathf.Atan2(dx,dz) * Mathf.Rad2Deg;
                 
                  // direction player facing
                  float anglePlayer = playerController.eulerAngles.y;
                 
                  // subtract player angle, to get relative angle to object
                  // subtract 90
                  // (so 0 degrees (same direction as player) is UP)
                  float angleRadarDegrees =  angleToTarget - anglePlayer - 90;
                 
                  // calculate (x,y) position given angle and distance
                  float normalisedDistance = distanceToTarget / MAX_DISTANCE;   
                  float angleRadians = angleRadarDegrees * Mathf.Deg2Rad;
                  float blipX = normalisedDistance * Mathf.Cos(angleRadians);
                  float blipY = normalisedDistance * Mathf.Sin(angleRadians);   
                 
                  // scale blip position according to radar size
                  blipX *= RADAR_SIZE/2;
                  blipY *= RADAR_SIZE/2;
                 
                  // offset blip position relative to radar center (64,64)
                  blipX += RADAR_SIZE/2;
                  blipY += RADAR_SIZE/2;
                 
                  // draw target texture at calculated location
                  Rect blipRect = new Rect(blipX - 5, blipY - 5, 10, 10);
                  GUI.DrawTexture(blipRect, targetBlip);       
              }
          }


          B9. Kéo thả file C# vừa tạo vào Main Camera ở thẻ Hierarchy.

          B10. Nhấp chọn Main Camera, qua thẻ Inspector, điều chỉnh các mục trong Radar (Script) như hình sau:



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


          Nguyên lí làm việc

          Hai hằng số được định nghĩa trong đoạn code:
          • MAX_DISTANCE: Khoảng cách xa nhất mà radar có thể phát hiện được đối tượng và hiển thị lên màn hình radar.
          • RADAR_SIZE: Kích thước (Đơn vị là px) của radar sẽ hiển thị lên màn hình.

          Class Radar (file C# Radar) có 3 biến public:
          • Biến đầu tiên là để chỉ tâm của radar sẽ được đặt, ở đây là hiển thị tâm tại chỗ đứng của người chơi.
          • 2 biến còn lại là hình ảnh của màn hình radar và điểm nút chỉ đối tượng khác trong game sẽ được hiển thị trên màn hình radar.

          Trong hàm onGUI( ), sẽ hiển thị hình nền của màn hình radar. Một mảng GameObject với các đối tượng được đặt tag sẽ được duyệt qua. Với mỗi phần tử trong mảng, nếu khoảng cách của chúng nằm trong khoảng nhỏ hơn MAX_DISTANCE thì hàm DrawBlip( ) sẽ được gọi.

          Hàm DrawBlip( ) sẽ tìm tọa độ x và z của đối tượng và người chơi để tính toán khoảng cách từ tâm đến đối tượng thông qua hàm lượng giác Atan2( ) của Unity. Hướng nhìn của người chơi được xác định bởi trục Y và việc phải trừ góc quay đi 90 đều tương tự như ở bài viết Hiển thị la bàn.


          Còn nữa

          Hiển thị các đối tượng với màu khác nhau trên radar.
          Chèn thêm các biến Texture vào trong hàm DrawBlip( ) với mỗi biểu tượng khác nhau cho từng đối tượng khác nhau. Các biến này sẽ sử dụng trong hàm GUI.DrawTexture. Điều này cho phép hàm DrawBlip( ) sẽ được gọi với các vòng lặp khác nhau cho từng đối tượng được gắn tag khác nhau.