Thứ Năm, 27 tháng 2, 2014

COOKBOOK - HIỂN THỊ LA BÀN

  - Hiển thị la bàn -

Trong các địa hình hay bản đồ rộng lớn, việc hiển thị la bàn để định hướng cho người chơi là điều rất cần thiết. Bằng việc hiển thị hình ảnh minh họa hướng nhìn theo thời gian thực với GUI. Chúng ta có thể mô phỏng được hướng nhìn của người chơi trên bản đồ là hướng Nam hay Bắc, Đông hay Tây.

Chuẩn bị
Assets.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. Chúng ta sẽ tạo ra một khối hộp để đánh dấu hướng Bắc. Tại thẻ Hierarchy, nhấp chọn nút Create | Cube và qua thẻ Inspector, điều chỉnh Position thành X = 0, Y = 1, Z = 5.


B5. Chúng ta sẽ tạo ra một khối tròn để đánh dấu hướng Tây. Tại thẻ Hierarchy, nhấp chọn nút Create | Sphere và qua thẻ Inspector, điều chỉnh Position thành X = -5, Y = 1, Z = 0.



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

using UnityEngine;
using System.Collections;

public class Compass : MonoBehaviour
{
        public Transform playerController;
        public Texture compassBackground;
        public Texture playerBlip;

        private void OnGUI ()
        {
                // background displaying top left in square of 128 pixels
                Rect compassBackgroundRect = new Rect (0, 0, 128, 128);
                GUI.DrawTexture (compassBackgroundRect, compassBackground);
                GUI.DrawTexture (CalcPlayerBlipTextureRect (), playerBlip);
        }

        private Rect CalcPlayerBlipTextureRect ()
        {
                // subtract 90, so North (0 degrees) is UP
                float angleDegrees = playerController.eulerAngles.y - 90;
                float angleRadians = angleDegrees * Mathf.Deg2Rad;
   
                // calculate (x,y) position given angle
                // blip distance from center is 16 pixels
                float blipX = 16 * Mathf.Cos (angleRadians);
                float blipY = 16 * Mathf.Sin (angleRadians);
   
                // offset blip position relative to compass center (64,64)
                blipX += 64;
                blipY += 64;
   
                // stretch blip image to display in 10x10 pixel square
                return new Rect (blipX - 5, blipY - 5, 10, 10);
        }


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

B8. Giải nén file Assets.rar và kéo thả 2 file ảnh vào thẻ Project. Nhấp chuột chọn Main Camera ở thẻ Hierarchy, qua thẻ Inspector, điều chỉnh các mục ở Compass Script như sau:


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

Nguyên lí làm việc

Để file script Compass làm việc cần 3 biến, biến đầu tiên là đối tượng để hiển thị hướng la bàn (3rd Person Controller - Người chơi), 2 biến còn lại là hình ảnh nền của la bàn (compass Background) và nút chỉ hướng nhìn của người chơi (Player Blip).

Hàm OnGUI( ) được gọi trên từng khung hình, trên từng khung hình, hình nền của la bàn sẽ được gọi và tiếp theo là nút chỉ hướng của người chơi. Vị trí của nút chỉ hướng sẽ được tính toán và trả về nhờ hàm CalcPlayerBlipTextureRect( ). Cả 2 hình ảnh nền của la bàn và nút chỉ hướng đều được hiển thị nhờ hàm DrawTexture( ) thuộc class GUI, với 2 biến để định vị trí (Rect) và hình dạng (Texture). Trong đó Rect(X, Y, chiều Dài, chiều Cao).



Nút định hướng được xác định dựa vào góc quay quanh trục Y. Thành phần (component) y có để được tìm thấy trong eulerAngles. Bạn có thể hình dung khi nhìn lên nhìn xuống và xem xem bạn đang nhìn vào hướng nào, chính là những gì chúng ta sẽ mô phỏng bằng hình ảnh trong la bàn. Trong toán học, góc quay bằng 0 mặc định là hướng Đông, để cho đúng chúng ta sẽ trừ góc quay đi 90*. Góc quay sẽ được chuyển từ độ thành radian nên chúng ta phải dùng hàm lượng giác của Unity. Sau đó, chúng ta phải nhân cho hàm Sin( ) hoặc Cos( ) cho khoảng cách của nút chỉ hướng đến tâm của la bàn (Trong đoạn code trên là 16 px). Kế tiếp, chúng ta cộng thêm 64 vào tọa độ hiển thị X, Y đó chính là tâm của la bàn. Cuối cùng, các giá trị của blipX và blipY chính là vị trí hiển thị của nút chỉ hướng trên màn hình. Xác định được vị trí cho tâm, chiều Dài = chiều Cao = 10 px của nút chỉ hướng, ta sẽ xác định được các biến trong hàm Rect( ) và hiển thị được nút chỉ hướng lên màn hình thông qua hàm DrawTexture( ) được gọi trong hàm OnGUI( ).

Thứ Ba, 25 tháng 2, 2014

Project RPG BÀI 12. CSharpMessenger Extended

 

Đây là file C# thông báo (hay còn gọi là trung tâm thông báo), nó cung cấp đoạn hội thoại để kiểm tra giữa người tạp sự kiện (event) và người dùng của sự kiện đó mà không cần phải đợi đến người dùng hợp tác với người tạo. Điều này hiệu quả hơn cho việc kiểm tra, phát hiện lỗi trong quá trình xử lý.

B1. Nhấp phải vào thư mục Script và chọn Create | Folder và đặt tên là CSMessenger Extended.





B2. Nhấp phải vào thư mục CSMessenger Extended và chọn Create | C# Script và đặt tên là Callback. 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:

// MessengerUnitTest.cs v1.0 by Magnus Wolffelt, magnus.wolffelt@gmail.com
//
// Delegates used in Messenger.cs.

public delegate void Callback();
public delegate void Callback<T>(T arg1);
public delegate void Callback<T, U>(T arg1, U arg2);
public delegate void Callback<T, U, V>(T arg1, U arg2, V arg3);


B3. Nhấp phải vào thư mục CSMessenger Extended và chọn Create | C# Script và đặt tên là Messenger. 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:

// Messenger.cs v1.0 by Magnus Wolffelt, magnus.wolffelt@gmail.com
//
// Inspired by and based on Rod Hyde's Messenger:
// http://www.unifycommunity.com/wiki/index.php?title=CSharpMessenger
//
// This is a C# messenger (notification center). It uses delegates
// and generics to provide type-checked messaging between event producers and
// event consumers, without the need for producers or consumers to be aware of
// each other. The major improvement from Hyde's implementation is that
// there is more extensive error detection, preventing silent bugs.
//
// Usage example:
// Messenger<float>.AddListener("myEvent", MyEventHandler);
// ...
// Messenger<float>.Broadcast("myEvent", 1.0f);


using System;
using System.Collections.Generic;

public enum MessengerMode {
    DONT_REQUIRE_LISTENER,
    REQUIRE_LISTENER,
}


static internal class MessengerInternal {
    static public Dictionary<string, Delegate> eventTable = new Dictionary<string, Delegate>();
    static public readonly MessengerMode DEFAULT_MODE = MessengerMode.REQUIRE_LISTENER;
  
    static public void OnListenerAdding(string eventType, Delegate listenerBeingAdded) {
        if (!eventTable.ContainsKey(eventType)) {
            eventTable.Add(eventType, null);
        }
      
        Delegate d = eventTable[eventType];
        if (d != null && d.GetType() != listenerBeingAdded.GetType()) {
            throw new ListenerException(string.Format("Attempting to add listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being added has type {2}", eventType, d.GetType().Name, listenerBeingAdded.GetType().Name));
        }
    }
  
    static public void OnListenerRemoving(string eventType, Delegate listenerBeingRemoved) {
        if (eventTable.ContainsKey(eventType)) {
            Delegate d = eventTable[eventType];
          
            if (d == null) {
                throw new ListenerException(string.Format("Attempting to remove listener with for event type {0} but current listener is null.", eventType));
            } else if (d.GetType() != listenerBeingRemoved.GetType()) {
                throw new ListenerException(string.Format("Attempting to remove listener with inconsistent signature for event type {0}. Current listeners have type {1} and listener being removed has type {2}", eventType, d.GetType().Name, listenerBeingRemoved.GetType().Name));
            }
        } else {
            throw new ListenerException(string.Format("Attempting to remove listener for type {0} but Messenger doesn't know about this event type.", eventType));
        }
    }
  
    static public void OnListenerRemoved(string eventType) {
        if (eventTable[eventType] == null) {
            eventTable.Remove(eventType);
        }
    }
  
    static public void OnBroadcasting(string eventType, MessengerMode mode) {
        if (mode == MessengerMode.REQUIRE_LISTENER && !eventTable.ContainsKey(eventType)) {
            throw new MessengerInternal.BroadcastException(string.Format("Broadcasting message {0} but no listener found.", eventType));
        }
    }
  
    static public BroadcastException CreateBroadcastSignatureException(string eventType) {
        return new BroadcastException(string.Format("Broadcasting message {0} but listeners have a different signature than the broadcaster.", eventType));
    }
  
    public class BroadcastException : Exception {
        public BroadcastException(string msg)
        : base(msg) {
        }
    }
  
    public class ListenerException : Exception {
        public ListenerException(string msg)
        : base(msg) {
        }
    }
}


// No parameters
static public class Messenger {
    private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;
  
    static public void AddListener(string eventType, Callback handler) {
        MessengerInternal.OnListenerAdding(eventType, handler);
        eventTable[eventType] = (Callback)eventTable[eventType] + handler;
    }
  
    static public void RemoveListener(string eventType, Callback handler) {
        MessengerInternal.OnListenerRemoving(eventType, handler);  
        eventTable[eventType] = (Callback)eventTable[eventType] - handler;
        MessengerInternal.OnListenerRemoved(eventType);
    }
  
    static public void Broadcast(string eventType) {
        Broadcast(eventType, MessengerInternal.DEFAULT_MODE);
    }
  
    static public void Broadcast(string eventType, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        Delegate d;
        if (eventTable.TryGetValue(eventType, out d)) {
            Callback callback = d as Callback;
            if (callback != null) {
                callback();
            } else {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
    }
}

// One parameter
static public class Messenger<T> {
    private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;
  
    static public void AddListener(string eventType, Callback<T> handler) {
        MessengerInternal.OnListenerAdding(eventType, handler);
        eventTable[eventType] = (Callback<T>)eventTable[eventType] + handler;
    }
  
    static public void RemoveListener(string eventType, Callback<T> handler) {
        MessengerInternal.OnListenerRemoving(eventType, handler);
        eventTable[eventType] = (Callback<T>)eventTable[eventType] - handler;
        MessengerInternal.OnListenerRemoved(eventType);
    }
  
    static public void Broadcast(string eventType, T arg1) {
        Broadcast(eventType, arg1, MessengerInternal.DEFAULT_MODE);
    }
  
    static public void Broadcast(string eventType, T arg1, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        Delegate d;
        if (eventTable.TryGetValue(eventType, out d)) {
            Callback<T> callback = d as Callback<T>;
            if (callback != null) {
                callback(arg1);
            } else {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
    }
}


// Two parameters
static public class Messenger<T, U> {
    private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;
  
    static public void AddListener(string eventType, Callback<T, U> handler) {
        MessengerInternal.OnListenerAdding(eventType, handler);
        eventTable[eventType] = (Callback<T, U>)eventTable[eventType] + handler;
    }
  
    static public void RemoveListener(string eventType, Callback<T, U> handler) {
        MessengerInternal.OnListenerRemoving(eventType, handler);
        eventTable[eventType] = (Callback<T, U>)eventTable[eventType] - handler;
        MessengerInternal.OnListenerRemoved(eventType);
    }
  
    static public void Broadcast(string eventType, T arg1, U arg2) {
        Broadcast(eventType, arg1, arg2, MessengerInternal.DEFAULT_MODE);
    }
  
    static public void Broadcast(string eventType, T arg1, U arg2, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        Delegate d;
        if (eventTable.TryGetValue(eventType, out d)) {
            Callback<T, U> callback = d as Callback<T, U>;
            if (callback != null) {
                callback(arg1, arg2);
            } else {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
    }
}


// Three parameters
static public class Messenger<T, U, V> {
    private static Dictionary<string, Delegate> eventTable = MessengerInternal.eventTable;
  
    static public void AddListener(string eventType, Callback<T, U, V> handler) {
        MessengerInternal.OnListenerAdding(eventType, handler);
        eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] + handler;
    }
  
    static public void RemoveListener(string eventType, Callback<T, U, V> handler) {
        MessengerInternal.OnListenerRemoving(eventType, handler);
        eventTable[eventType] = (Callback<T, U, V>)eventTable[eventType] - handler;
        MessengerInternal.OnListenerRemoved(eventType);
    }
  
    static public void Broadcast(string eventType, T arg1, U arg2, V arg3) {
        Broadcast(eventType, arg1, arg2, arg3, MessengerInternal.DEFAULT_MODE);
    }
  
    static public void Broadcast(string eventType, T arg1, U arg2, V arg3, MessengerMode mode) {
        MessengerInternal.OnBroadcasting(eventType, mode);
        Delegate d;
        if (eventTable.TryGetValue(eventType, out d)) {
            Callback<T, U, V> callback = d as Callback<T, U, V>;
            if (callback != null) {
                callback(arg1, arg2, arg3);
            } else {
                throw MessengerInternal.CreateBroadcastSignatureException(eventType);
            }
        }
    }
}


B4. Nhấp phải vào thư mục Script và chọn Create | Folder và đặt tên là HUD Classes.


B5. Nhấp phải vào thư mục HUD Classes và chọn Create | C# Script và đặt tên là VitalBar. 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:

/// <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 {

    // Use this for initialization
    void Start () {
  
    }
  
    // Update is called once per frame
    void Update () {
  
    }

    //This method is called when the gameobject is enabled
    public void OnEnable(){
    }

    //this method is called when the gameobject is disabled
    public void OnDisable(){
    }
}


Bài viết sau sẽ làm rõ hơn về file VitalBar cho các bạn!

Thứ Năm, 20 tháng 2, 2014

COOKBOOK - TẠO ĐỒNG HỒ KIM


  - Tạo đồng hồ kim -

Việc tạo đồng hồ kim tốn nhiều thời gian để vẽ các kim chỉ thời gian nhưng đổi lại sử dụng đồng hồ kim sẽ nổi bật về mặt đồ họa hơn.


Cách để làm

B1. Tại thẻ Hierarchy, nhấp chọn nút Create | Directional Light để thêm thắt chút ánh sáng cho scene.

B2. Vẫn ở thẻ Hierarchy, nhấp chọn Create | Cylinder. Qua thẻ Inspector, điều chỉnh các thông số ở mục Transform như sau:


B3. Ở thẻ Hierarchy, nhấp chọn nút Create | Cube và tạo ra 3 khối hộp tượng trưng cho 3 kim chỉ thời gian và đổi tên, điều chỉnh thông số ở thẻ Inspector lần lượt như sau:

 


B4. Vào GameObject | Create Empty và đặt tên là hours-pivot. Điều chỉnh thông số Position ở thẻ Inspector là (0 , 0 , 0).

 

B5. Tại thẻ Hierarchy, kéo thả hand-hours vào hours-pivot như hình sau:


B6. Thực hiện tương tự với kim giây và kim phút như hình sau:


 



B7. Tại thẻ Project, chọn Create | C# Script và đặt tên là ClockAnalogue, double click vào file C# này và chèn đoạn code sau vào:


using UnityEngine;
using System.Collections;
using System;

public class ClockAnalogue : MonoBehaviour
{
    public Transform secondHandPivot;
    public Transform minuteHandPivot;
    public Transform hourHandPivot;
   
    private void Update()
    {
        DateTime time = DateTime.Now;
        float seconds = (float)time.Second;
        float minutes = (float)time.Minute;
        float hours12 = (float)time.Hour % 12;
        float angleSeconds = -360 * (seconds/60);
        float angleMinutes = -360 * (minutes/60);
        float angleHours = -360 * (hours12 / 12);
       
        // rotate each hand
        secondHandPivot.localRotation = Quaternion.Euler(0f, 0f, angleSeconds);
        minuteHandPivot.localRotation = Quaternion.Euler(0f, 0f, angleMinutes);
        hourHandPivot.localRotation = Quaternion.Euler(0f, 0f, angleHours);
    }
}


B8. Kéo thả file C# này vào Main Camera ở thẻ Hierarchy.

B9. Nhấp chọn Main Camera, qua thẻ Inspector, điều chỉnh mục Transform và kéo thả các pivot vào vị trí như hình sau:


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


Nguyên lí làm việc

Mỗi kim đồng hồ có gốc được đặt tại tâm của mặt đồng hồ, và có độ dài khác nhau để phân biệt kim giờ, phút hoặc giây. Mỗi khung hình chạy qua, hàm Update( ) sẽ xoay các kim chỉ thời gian quanh trục Z căn cữ bởi hàm thời gian DateTime. Mỗi kim quay đều với góc quay 360*. Các góc quay sẽ được thực hiện bởi hàm Quaternion.Euler( ) được cung cấp bởi Unity.


Còn nữa

Điều chỉnh camera để hiển thị đồng hồ ở góc màn hình

Tại thẻ Hierarchy, nhấp chọn nút Create | Camera và đổi tên thành Camera-Clock. Tại thẻ Inspector, bỏ dấu stick ở mục Audio Listener đi và điều chỉnh ở mục Camera và Transform như hình sau:



Thứ Tư, 19 tháng 2, 2014

DEVGAME 1.4 - CẤU TRÚC CỦA MỘT TỆP MÃ - THỰC THI HÀM THEO THỨ TỰ

 

Nếu bạn muốn khối hộp vừa xoay tròn vừa di chuyển thẳng theo hướng trục X, bạn có thể nhờ đến sự trợ giúp của GameObject. Trong Unity, GameObject thường để phân thứ tự cho các hàm, gom tất cả các asset trong scene, cố định thứ tự của các component và nhiều công dụng hữu ích khác. Bạn sẽ thường thấy nhiều lợi ích hơn với GameObject.

Trước khi tạo GameObject, bạn nên chia 2 hoạt động xoay tròn và di chuyển thành 2 file script khác nhau.

1. Double click vào file java SimpleRotate ở thẻ Project, trong chương trình biên tập code, nhấp chọn File | Save As và gõ vào khung tên là SimpleMove.
 

2. Trở qua cửa sổ Unity, double click vào file java SimpleMove vừa tạo ở bước trên để mở file này lên.


3. Trong file SimpleRotate, thêm dấu chú thích // vào phía trước transform.Translate để chương trình không thực hiện lệnh này khi biên dịch. Ấn Ctrl + S để save file script này lại.

#pragma strict

function Update () {

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


4. Trong file SimpleMove, thêm dấu chú thích // vào phía trước transform.Rotate. Ấn Ctrl + S để save file script này lại.

#pragma strict

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


5. Trở lại cửa sổ Unity, vào GameObject | Create Empty và đặt tên cho GO vừa tạo là Cube Parent.

 

6. Tại thẻ Hierarchy, kéo thả Cube vào Cube Parent như sau:


7. Kéo thả file java SimpleMove ở thẻ Project và Cube Parent ở thẻ Hierarchy.


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

Bây giờ khối hộp sẽ xoay vòng từ từ và di chuyển thẳng theo trục X thay vì chạy vòng vòng như ở bài trước.

Thứ Ba, 18 tháng 2, 2014

Project RPG BÀI 11. TẠO ĐIỂM HỒI SINH


Khi nhân vật được khởi tạo hoặc chết trong game, sau một khoảng thời gian nhân vật đó sẽ được hồi sinh và được đưa đến điểm nhất định hay còn gọi là "về làng", điểm đó được gọi là điểm hồi sinh (spawn point).



B1. Double click vào scene Level1 để tiến hành làm việc trên scene này. Sau đó vào Game Object | Create other | Terrain để tiến hành tạo địa hình cho game.


B2. Nhấp phải vào thẻ Project và chọn Import Package | Terrain Assets để sử dụng các file ảnh làm vật liệu cho địa hình.


B3. Tại thẻ Hierarchy, nhấp chọn Terrain, qua thẻ Inspector, sử dụng các công cụ trong Terrain (Script) để tạo độ ghồ ghề, mấp mô cho terrain theo ý bạn.

 

 

B4. Vẫn ở thẻ Inspector, nhấp chọn Paint Texture, nhấp chuột vào nút Edit Textures và chọn Add Texture. Tại khung Texture, nhấp chọn nút Select và double click vào Grass (Hill). Ấn nút Add để gán vật liệu này cho địa hình.

   

B5. Vào Game Object | Create Empty và đặt tên là Player Spawn Point, dùng công cụ dịch chuyển và kéo game object này ra vị trí rộng rãi để làm điểm hồi sinh. Nhớ kéo lên phía trên địa hình một khoảng bởi vì khi khởi tạo mô hình, nhân vật của bạn có thể bị rơi xuống bên dưới địa hình bởi lực hút.


B6. Double click vào file C# GameSettings và chèn thêm dòng code tô đỏ như sau:

public class GameSettings : MonoBehaviour {
    public const string PLAYER_SPAWN_POINT = "Player Spawn Point";        

    //This is the name of the gameobject that the player will spawn on at the start of level
    void Awake(){
        DontDestroyOnLoad(this);
    }


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

using UnityEngine;
using System.Collections;

public class GameMaster : MonoBehaviour {
    public GameObject playerCharacter;
    public GameObject gameSettings;
    public Camera mainCamera;

    public float zOffset;
    public float yOffset;
    public float xRotOffset;

    private GameObject _pc;
    private PlayerCharacter _pcScript;

    private Vector3 _playerSpawnPointPos;            //this is the place where player will spawn

    // Use this for initialization
    void Start () {
        _playerSpawnPointPos = new Vector3(107, 1, 116);        //the default position for our player spawn point

        GameObject go = GameObject.Find(GameSettings.PLAYER_SPAWN_POINT);

        if(go == null){
            Debug.LogWarning("Can not find Player Spawn Point");

            go = new GameObject(GameSettings.PLAYER_SPAWN_POINT);
            Debug.Log("Created Player Spawn Point");

            go.transform.position = _playerSpawnPointPos;
            Debug.Log("Moved Player Spawn Point");
        }

        _pc = Instantiate(playerCharacter, go.transform.position, Quaternion.identity) as GameObject;
        _pc.name = "pc";

        _pcScript = _pc.GetComponent<PlayerCharacter>();

        zOffset = -2.5f;
        yOffset = 2.5f;
        xRotOffset = 22.5f;
        mainCamera.transform.position = new Vector3 (_pc.transform.position.x, _pc.transform.position.y + yOffset, _pc.transform.position.z + zOffset);
        mainCamera.transform.Rotate(xRotOffset, 0, 0);

        LoadCharacter();
    }

    public void LoadCharacter(){
        GameObject gs = GameObject.Find("__GameSettings");

        if(gs == null){
            GameObject gs1 = Instantiate(gameSettings, Vector3.zero, Quaternion.identity) as GameObject;
            gs1.name = "__GameSettings";
        }
            GameSettings gsScript = GameObject.Find ("__GameSettings").GetComponent<GameSettings>();

            //loading the character data
            gsScript.LoadCharacterData();


    }
}


Nếu bạn tự tạo ra điểm hồi sinh và dùng công cụ di chuyển bằng chuột để kéo thả điểm hồi sinh đến nơi bạn muốn như đã làm ở B5 rồi bạn có thể qua B9. Bạn cũng có thể để file script tự tạo điểm hồi sinh cho bạn bằng cách thiết lập tọa độ mặc định cho game object này thông qua B8.

B8 (Xóa Player Spawn Point ở thẻ Hierarchy đi). Hãy thay đổi tọa độ điểm hồi sinh bằng cách đổi tọa độ của 3 tọa độ được tô đỏ (107, 1, 116) ở file C# GameMaster phù hợp với địa hình mà bạn đã tạo, nên đặt tọa độ điểm hồi sinh ở nơi rộng và bằng phẳng để tránh việc nhân vật bị kẹt vào địa hình.

B9. Ấn Ctrl + S để lưu scene Level1 lại. Nhấp nút Play để kiểm tra thành quả.

Thứ Bảy, 15 tháng 2, 2014

COOKBOOK - TẠO ĐỒNG HỒ KĨ THUẬT SỐ BẰNG GUI

  - Tạo đồng hồ kĩ thuật số bằng GUI -

Dù rằng ở thế giới thực hay thế giới trong game, có rất nhiều game sử dụng nhiều dạng đồng hồ với nhiều kiểu hiện thị khác nhau. Hầu hết các loại đồng hồ hiển thị là một chuỗi (string) biểu diễn dạng số nguyên (integer) của giờ, phút, giây.



Cách để làm

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

using UnityEngine;
using System.Collections;

using System;

public class ClockDigital : MonoBehaviour
{
    private void OnGUI ()
    {

        DateTime time = DateTime.Now;
        string hour = LeadingZero( time.Hour );
        string minute = LeadingZero( time.Minute );
        string second = LeadingZero( time.Second );

        GUI.Box(new Rect (5, 5, 100, 25), hour + ":" + minute + ":" +  second);   
    }

     // given an integer, return a 2-character string
     // adding a leading zero if required
    private string LeadingZero(int n)
    {
        return n.ToString().PadLeft(2, '0');
    }
}


B2. Kéo thả file C# này vào Main Camera ở thẻ Hierarchy.


Nguyên lí làm việc

Sử dụng thư viện System cho phép chúng ta sử dụng class Datetime để trả về thời gian hiện tại của hệ thống. Dòng đầu tiên trong hàm OnGUI ( ) sẽ trả về thời gian thực và gán nó vào biến time trong object (đối tượng) DateTime.

Giờ, phút, giây được tách ra từ object DateTime và được chuyển đổi từ dạng số nguyên (integer) sang dạng chuỗi (string) có 2 kí tự bằng hàm LeadingZero ( ). Hàm này sẽ chèn thêm số 0 vào hàng chục nếu giá trị của giờ, phút, giây chỉ có hàng đơn vị. (VD: 01, 02, 03...).

Cuối cùng, giờ, phút, giây được ngăn cách với nhau bởi dấu : và được hiển thị thông qua GUI label.


Còn nữa

Nếu bạn muốn hiển thị đồng hồ 12 số thay vì 24 số thì bạn chỉ việc lấy số giờ và chia lấy phần nguyên với 12.

string hour = LeadingZero( time.Hour % 12);

Thứ Sáu, 14 tháng 2, 2014

Project RPG BÀI 10C. DỌN DẸP FILE ModifiedStat


Chuỗi bài viết dọn dẹp nhà cửa vì lo ăn tết nên post muộn :D



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

/// <summary>
/// ModifiedStat.cs
/// This is the base class for all stats that will be modifiable by attributes
/// </summary>
using System.Collections.Generic;                //Generic was added so we can use the List<>

public class ModifiedStat : BaseStat {
    private List<ModifyingAttribute> _mods;        //A listof Attribute that modify this stat
    private int _modValue;                        //The amount added to the baseValue from the modifiers

    /// <summary>
    /// Initializes a new instance of the <see cref="ModifiedStat"/> class.
    /// </summary>
    public ModifiedStat(){
        UnityEngine.Debug.Log("Modified Created");
        _mods = new List<ModifyingAttribute>();
        _modValue = 0;
    }

    /// <summary>
    /// Add a ModifyingAttribute to list of mods for this ModifiedStat
    /// </summary>
    /// <param name="mod">Mod.</param>
    public void AddModifier( ModifyingAttribute mod ){
        _mods.Add(mod);
    }

    /// <summary>
    /// Reset _modValue to 0.
    /// Check to see if we have at least one ModifyingAttribute in our list of mods.
    /// If we d, then interate through the list and add the AdjustedBaseValue * ratio to our modValue
    /// </summary>
    private void CalculateModValue(){
        _modValue = 0;

        if(_mods.Count > 0)
            foreach(ModifyingAttribute att in _mods)
                _modValue += (int)(att.attribute.AdjustedBaseValue * att.ratio);
    }

    /// <summary>
    /// This function is overriding the AdjustBaseValue in the BaseStat class
    /// Calculate the AdjustBaseValue from the BaseValue + Buffvale+ _modValue
    /// </summary>
    /// <value>The adjusted base value.</value>
    public new int AdjustedBaseValue{
        get{ return BaseValue + BuffValue + _modValue; }
    }

    /// <summary>
    /// Update this instance.
    /// </summary>
    public void Update(){
        CalculateModValue();
    }

    public string GetModifyingAttributesString(){
        string temp = "";

        //UnityEngine.Debug.Log("Number of mods: " + _mods.Count);

        for(int cnt = 0; cnt < _mods.Count; cnt++){
            temp += _mods[cnt].attribute.Name;
            temp += "_";
            temp += _mods[cnt].ratio;

            if(cnt < _mods.Count - 1)
                temp += "|";

        }
       
        //UnityEngine.Debug.Log(temp);
        return temp;
    }
}

/// <summary>
/// A structue that will hold an Attribute and a ratio that will be added as a modifying attribute to our ModifiedStats
/// </summary>
public struct ModifyingAttribute{
    public Attribute attribute;        //the attribute to be used as a modifier
    public float ratio;                //the percent of the attributes AdjustedBaseValue that will be to our ModifiedStats

    /// <summary>
    /// Initializes a new instance of the <see cref="ModifyingAttribute"/> struct.
    /// </summary>
    /// <param name="att">
    /// Att. the attribute to be used
    /// </param>
    /// <param name="rat">
    /// Rat. the ratio to be used
    /// </param>
    public ModifyingAttribute(Attribute att, float rat){
        UnityEngine.Debug.Log ("Modifying Attribute Created");
        attribute = att;
        ratio = rat;
    }
}

Thứ Năm, 13 tháng 2, 2014

DEVGAME 1.3 - CẤU TRÚC CỦA MỘT TỆP MÃ - HÀM THỜI GIAN


Time.deltaTime là một phần quan trọng của việc viết code trên Unity. Time.deltaTime cho phép chúng ta tương tác với đối tượng theo giây thay vì một khung hình như ở bài viết trước. Vì thế nội dung hoạt cảnh sẽ thích hợp khi phát trên nhiều máy kể cả việc tỷ lệ khung hình có tốc độ chênh lệch nhau. Time.deltaTime là một biến sẽ trả về một lượng thời gian đã qua kể từ lần gọi trước đến khi gọi hàm Update.

1. Double click vào file java Simple Rotate đã tạo ở bài viết trước và sửa dòng code transform.Rotate lại thành như dưới đây:
  
#pragma strict

function Update () {

    transform.Rotate(0, 5 * Time.deltaTime,0);
}

2. Save đoạn script lại và nhấp nút Play để kiểm tra kết qua.
Bây giờ, vật thể sẽ quay 5 độ/giây thay vì 5 độ/khung hình như trước kia.

3. Tăng góc quay từ 5 lên 50 và save đoạn scrip lại để thấy sự khác biệt.

|| Mẹo: Nhớ lưu file script thường xuyên sau khi thay đổi để thấy sự khác biệt.

Bây giờ hãy thêm phần Translate (dịch chuyển) và thấy rằng sẽ như thế nào nếu bạn code bị lỗi. Hãy chú ý chữ cái đầu tiên của từ translate.
1. Chèn dòng code sau vào bên dưới transform.Rotate.

transform.translate(2 * Time.deltaTime, 0, 0);

Ngay khi bạn save file script sau khi chèn câu lệnh trên, một thông báo lỗi error sẽ hiển thị bên dưới thẻ Khung nhìn Game.


Bạn có thể click vào dòng thông báo đó để mở hộp thoại Console để xem chi tiết.

 

Trong trường hợp này, lỗi cú pháp là lỗi dễ nhận ra, và chương trình sẽ thông báo nên làm gì cho chúng ta.
2. Thay chữ t của từ translate thành chữ hoa và save đoạn script lại. Tên hàm luôn được viết hoa chữ đầu.

#pragma strict

function Update () {

    transform.Rotate(0, 50* Time.deltaTime,0);
    transform.Translate(2 * Time.deltaTime, 0, 0);
}


3. Nhấp chọn lại hộp thoại Console.


Bây giờ, thông báo lỗi error đã biến mất và hộp thoại console đã trống trơn.

4. Tắt hộp thoại console đi.

5. Nhấp nút Play để kiểm tra kết quả.

Bây giờ khối hộp sẽ dịch chuyển 2 mét/giây, nhưng nó không giống như những gì chúng ta muốn. Thật ra do dòng code xoay vòng được thực hiện trước, và dịch chuyển thực hiện kế tiếp dựa trên trục tọa độ của khối hộp. Nếu bạn muốn khối hộp vừa xoay vòng vừa dịch chuyển thẳng tiến theo trục ngang (trục X), bạn có thể tham khảo ở bài viết sau.

Thứ Tư, 12 tháng 2, 2014

COOKBOOK - TÔ SÁNG VẬT THỂ KHI NHẤP CHUỘT VỚI COLLIDER


  - Tô sáng vật thể khi nhấp chuột với collider -

Tô sáng (highlight) là một hiệu ứng rất hữu dụng để người chơi có thể biết rằng có thể tương tác với vật thể. Điều này rất hữu dụng trong các thể loại game như xếp hình và phiêu lưu theo kiểu nhấp chuột, và nó còn có thể được sử dụng vào việc tạo giao diện người dùng bằng 3D.



Chuẩn bị
HighLight.unitypackage

Cách để làm

B1. Nhấp phải vào thẻ Project và chọn Import Package | Custom Package và mở file HightLight vừa tải ở trên.


B2. Double click vào highlightScene trong thẻ Project để mở scene này lên.


B3. Tại thẻ Hierarchy, nhấp chọn highlightCube, qua thẻ Inspector, thay đổi Shader cho vật thể này từ Diffuse thành VertexLit.


B4. Vẫn ở thẻ Inspector, thay đổi Base Texture của vật thể bằng cách nhấp chọn nút Select và chọn baseBox như hình sau:


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

using UnityEngine;
using System.Collections;

public class HighlightObject : MonoBehaviour
{
    public Color initialColor;
    public Color highlightColor;
    public Color mousedownColor;
    private bool mouseon = false;
    void OnMouseEnter()
    {
        mouseon = true;
        renderer.material.SetColor("_Emission", highlightColor);
    }
    void OnMouseExit()
    {
        mouseon = false;
        renderer.material.SetColor("_Emission", initialColor);
    }
    void OnMouseDown()
    {
        renderer.material.SetColor("_Emission", mousedownColor);
    }
    void OnMouseUp()
    {
        if (mouseon)
            renderer.material.SetColor("_Emission", highlightColor);
        else
            renderer.material.SetColor("_Emission", initialColor);
    }
}


B6. Kéo thả file C# vừa tạo vào highlightCube ở thẻ Hierarchy.


B7. Nhấp chọn highlightCube ở thẻ Hierarchy và qua thẻ Inspector điều chỉnh Initial Color thành màu xanh đậm (R: 0, G: 100, B: 0) và Mousedown Color thành xanh nhạt (R: 0, G: 255, B: 0).


B8. Gán box collider cho highlightCube bằng cách vào Component | Physics | Box Collider.


B9. Nhấp nút Play để kiểm tra thành quả. Khối hộp sẽ đổi màu mỗi khi bạn rê chuột hoặc nhấp chuột vào nó.

Nguyên lý làm việc
Box collider sẽ kiểm tra con trỏ chuột có đặt trên vật thể hay không, nó làm việc như một trigger để thay đổi màu Emissiver Color trong Shader VertexLit. Biến mouseon dùng để kiểm tra con trỏ có đang nhấp hay nằm ngoài box collider để thay đổi màu sắc phụ thuộc vào con trỏ.

Còn nữa...
Bạn có thể sử dụng các shader khác tùy ý, nhưng nhớ phải thay đổi giá trị biến màu cho phù hợp với thông số của vật liệu nhé.

Tô sáng bằng shader self-illuminated
Hãy đổi Shader của highlightCube thành Self-illumianted và thay thế _Emission thành _Color trong file C#.

Sử dụng shader Transparent.
Shader Transparent sẽ tạo hiệu ứng trong suốt khá thú vị. Bạn hãy nhớ điều chỉnh độ trong suốt của vật thể bằng giá trị Alpha khi chọn màu cho vật thể nhé. Nhớ thay thế _Emission thành _Color.