Hiển thị các bài đăng có nhãn Hướng dẫn. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn Hướng dẫn. Hiển thị tất cả bài đăng

Thứ Năm, 8 tháng 5, 2014

Project RPG BÀI 16. HIỂN THỊ TÊN KHI XÁC ĐỊNH MỤC TIÊU



Ở bài viết Project RPG BÀI 4. Xác định mục tiêu, chúng ta đã tạo ra script Targetting để chọn đối tượng trong phạm vi của nhân vật bằng phím Tab và thay đổi mục tiêu qua lại giữa các đối tượng. Ở bài viết này, chúng ta sẽ tạo ra một script mới dựa theo script Targetting nhưng có thêm phần hiển thị tên mục tiêu cùng thanh máu thay vì chỉ thay đổi màu sắc như ở bài trước.


B1. Double click vào scene Level1 ở thẻ Project để tiếp tục làm việc với scene này.

B2. Vào Edit | Project Settings | Tags and Layers và qua thẻ Inspector, mục Tags nhập vào Element 0 là Enemy.


B3. Tại thẻ Project, nhấp chọn mob_Slug ở thư mục Prefabs và qua thẻ Inspector, điều chỉnh tag là Enemy.

B4. Tại thẻ Hierarchy, đổi tên 3 đối tượng mob_Slug thành mob_Slug 1, mob_Slug 2, mob_Slug 3 và chỉnh tag cho cả 3 là Enemy. (Lưu ý: không nên chọn tất cả rồi đặt tag trong một lần vì có thể dính tag cho đối tượng con là Armateur)

 

B5. Tại thẻ Hierarchy, nhấp chọn Create | 3D Text và đặt tên là Name.


B6. Nhấp chọn Name ở thẻ Hierarchy vừa tạo ở bước trên và qua thẻ Inspector, điều chỉnh như sau:


B7. Kéo thả Name vào mob_Slug 1 rồi qua thẻ Inspector, điều chỉnh tọa độ và Mesh Render của Name như sau:


B8. Tại thẻ Hierarchy, nhấp chọn mob_Slug 1 và qua thẻ Inspector, bấm chọn nút Apply để các mob_slug còn lại đều có thêm Name giống như mob_Slug 1 vừa điều chỉnh.

 


B9. Nhấp phải vào thư mục Script và chọn Create | C# Script và đặt tên là TargetMob. Double click vào file này và xóa tất cả đi, chèn lại đoạn code sau vào:

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

public class TargetMob : MonoBehaviour {
    public List<Transform> targets;
    public Transform selectedTarget;
   
    private Transform myTransform;
    // Use this for initialization
    void Start () {
        targets = new List<Transform>();
        selectedTarget = null;
        myTransform = transform;
        AddAllEnemies();
    }
   
    public void AddAllEnemies(){
        GameObject[] go = GameObject.FindGameObjectsWithTag("Enemy");
       
        foreach(GameObject enemy in go)
            AddTarget(enemy.transform);
    }
   
    public void AddTarget(Transform enemy){
        targets.Add (enemy);
    }
   
    private void SortTargetsByDistance(){
        targets.Sort(delegate(Transform t1, Transform t2){
            return Vector3.Distance(t1.position, myTransform.position).CompareTo(Vector3.Distance(t2.position, myTransform.position));
        });
       
    }
   
    private void TargetEnemy(){
        if(selectedTarget == null){
            SortTargetsByDistance();
            selectedTarget = targets[0];
        }
        else{
            int index = targets.IndexOf (selectedTarget);
           
            if(index < targets.Count - 1){
                index++;
            }
            else{
                index = 0;
            }
            DeselectTarget();
            selectedTarget = targets[index];
        }
        SelectTarget();
    }
   
    private void SelectTarget(){
        Transform name = selectedTarget.FindChild ("Name");

        if (name == null) {
            Debug.LogError("Could not find the Name on " + selectedTarget.name);
            return;
        }

        name.GetComponent<TextMesh>().text = selectedTarget.GetComponent<Mob>().Name;
        name.GetComponent<MeshRenderer>().enabled = true;
        selectedTarget.GetComponent<Mob>().DisplayHealth();

        Messenger<bool>.Broadcast("show mob vitalbars", true);
    }

    private void DeselectTarget(){
        selectedTarget.FindChild ("Name").GetComponent<MeshRenderer> ().enabled = false;
       
        selectedTarget = null;
        Messenger<bool>.Broadcast("show mob vitalbars", false);
    }

    // Update is called once per frame
    void Update () {
        if(Input.GetKeyDown(KeyCode.Tab)){
            TargetEnemy();
        }
    }
}

B10. Kéo thả file C# TargetMob vào Game Master ở thẻ Hierarchy.


B11. Double click vào file C# Mob và xóa tất cả đi chèn lại đoạn code bên dưới vào:

 using UnityEngine;
using System.Collections;

public class Mob : BaseCharacter {
    public int curHealth;
    public int maxHealth;
  
    // Use this for initialization
    void Start () {
    //    GetPrimaryAttribute ((int)AttributeName.Constituion).BaseValue = 100;
    //    GetVital((int)VitalName.Health).Update ();

        Name = "Slug Mob";
    }
  
    // Update is called once per frame
    void Update () {

    }

    public void DisplayHealth(){
        Messenger<int, int>.Broadcast("mob health update", curHealth, maxHealth);

    }
}


B12. Tại thẻ Project, nhấp chọn mob_Slug 1 rồi qua thẻ Inspector, điều chỉnh Cur Health và Max Health như sau:


B13. Thực hiện tương tự với các mob_Slug còn lại nhưng nhập vào các giá trị Cur Health khác nhau còn Max Health giống bước trên.


B14. Double click vào file C# VitarBar nằm trong thư mục HUDClasses, xóa tất cả đi và chèn lại đoạn code sau vào:

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

public class VitalBar : MonoBehaviour {
    public bool _isPlayerHealthBar;
   
    private int _maxBarLength;
    private int _curBarLength;
    private GUITexture _display;

    void Awake(){
        _display = gameObject.GetComponent<GUITexture>();
    }

    // Use this for initialization
    void Start () {

        _maxBarLength = (int)_display.pixelInset.width;

        _curBarLength = _maxBarLength;
        _display.pixelInset = CalculatePosition ();
        OnEnable();
    }
   

    //This method is called when the gameobject is enabled
    public void OnEnable(){
        if(_isPlayerHealthBar)
            Messenger<int, int>.AddListener("player health update", OnChangeHealthBarSize);
        else{
            ToggleDisplay(false);
            Messenger<int, int>.AddListener("mob health update", OnChangeHealthBarSize);
            Messenger<bool>.AddListener("show mob vitalbars", ToggleDisplay);
        }
    }
   
    //this method is called when the gameobject is disabled
    public void OnDisable(){
        if(_isPlayerHealthBar)
            Messenger<int, int>.RemoveListener("player health update", OnChangeHealthBarSize);
        else{
            Messenger<int, int>.RemoveListener("mob health update", OnChangeHealthBarSize);
            Messenger<bool>.RemoveListener("show mob vitalbars", ToggleDisplay);
        }
    }
   
    //This method will calculate the total size of the healthbar in relation to the % of health the target has left
    public void OnChangeHealthBarSize(int curHealth, int maxHealth){
        //Debug.Log("We heard an event: curHealth = " + curHealth + " - maxHealth = " + maxHealth);
        _curBarLength = (int)((curHealth / (float)maxHealth) * _maxBarLength);    //this calculate the current bar length based on the player health %

        //_display.pixelInset = new Rect(_display.pixelInset.x, _display.pixelInset.y, _curBarLength, _display.pixelInset.height);
        _display.pixelInset = CalculatePosition();
    }
   
    //setting the health bar to the player or mob
    public void SetPlayerHealth(bool b){
        _isPlayerHealthBar = b;
    }

    private Rect CalculatePosition(){
        float yPos = _display.pixelInset.y / 2 - 10;

        if(!_isPlayerHealthBar){
            float xPos = (_maxBarLength - _curBarLength) - (_maxBarLength / 4 + 10);
            return new Rect(xPos, yPos, _curBarLength, _display.pixelInset.height);
        }

        return new Rect(_display.pixelInset.x, yPos, _curBarLength, _display.pixelInset.height);

    }

    private void ToggleDisplay (bool show){
        _display.enabled = show;

    }
}


B15. Save scene và ấn nút Play để kiểm tra thành quả. Ấn Tab để thay đổi qua lại giữa các mục tiêu.

Thứ Năm, 1 tháng 5, 2014

ProjectGame 1.3 - Tạo class CharacterController và SpriteManager



Trong bài viết này, chúng ta tạo file Javascript mới để điều khiển việc di chuyển của nhân vật và các ảnh chuyển động cho từng động tác của nhân vật. Chúng ta có thể code bằng Unitron (Mac), UniSciTE (Windows), hoặc MonoDevelop. MonoDevelop là công cụ biên tập chính được thiết kế cho code C# và môi trường .NET, vì vậy nếu bạn quen C# hơn bạn có thể yêu thích nó. Tuy nhiên, chúng ta sẽ code bằng JavaScript bởi vì nó có nhiều hàm nhanh hơn và debug tốt hơn, như tìm hoặc thay thế từ trong toàn bộ project bằng cách nhấn tổ hợp phím Command + Shift + Fin (Mac) hoặc Control + Shift + Fin (Windows) và tự động hoàn thành từ tìm kiếm. Nếu bạn đã biết JavaScript thì việc chuyển sang C# khá dễ dàng.


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


Chuẩn bị

Bây giờ, chúng ta sẽ bắt tay vào code, nhưng trước tiên, chúng ta phải thiết lập lại đôi chút.

B1. Tại thẻ Project, vào thư mục Scripts và xóa tất cả các file đi.

B2. Tiếp theo, chúng ta phải thiết lập lại Unity để sử dụng MonoDevelop như trình biên tập code mặc định. Bằng cách vào Edit | Preferences.

B3. Chúng ta sẽ thấy các mục thiết lập cho Unity trong cửa sổ vừa hiện lên. Trong tab General, vào External Script Editor và thay đổi Use build-in editor thành MonoDevelop bằng cách nhấp chọn nút Browse và chọn tìm đến thư mục cài đặt Unity và chọn Unity | MonoDevelop | MonoDevelop.exe.


MonoDevelop là một "Môi trường phát triển tích hợp" (IDE) cơ bản được thêm vào từ Unity 3.X, nó có môi trường code và debug tốt hơn. Chúng ta có thể tìm hiểu thêm về cách điều chỉnh qua trang web của Unity:

http://unity3d.com/support/documentation/Manual/HOWTO-MonoDevelop.html


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

B1. Tại thẻ Project, nhấp phải vào thư mục Scripts và chọn Create | JavaScript và đặt tên là CharacterController2D.



B2. Double click vào file JavaScript CharacterController2D để mở file này lên và bắt đầu code.

B3. Trước tiên chúng ta sẽ tiến hành khai báo các biến cần thiết như sau:

public var f_speed : float = 5.0;
public var loopSprites : SpriteManager[];
private var in_direction : int;

Ở đây, biến f_speed biểu thị cho tốc độ của nhân vật và chúng ta thiết lập nó là public vì vậy chúng ta co thể tùy chỉnh nó trong thẻ Inspector ở Unity Editor. Mãng loopSprite của lớp SpriteManager sẽ kiểm soát quá trình cập nhật texture của sprite khi chuyển động, chúng ta sẽ tạo ra lớp này sau. Biến in_direction lưu hướng của nhân vật, biến này chỉ mang 2 giá trị là 1 (sprite kế tiếp bên phải) và -1 (sprite kế tiếp bên trái).

B4. Tiếp theo, chúng ta sẽ bổ sung code sau vào hàm Start( ) đã mặc định được tạo sẵn.

public function Start() : void {
       in_direction = 1;
       //Initialization Sprite Manager
       for (var i : int = 0; i<loopSprites.length; i++) {
              loopSprites[i].init();
       }
       //Update Main Camera to the character position
       Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y,Camera.main.transform.position.z);
}

B5. Tiếp theo, chúng ta sẽ bổ sung đoạn code sau vào hàm Update( ) cũng đã được mặc định tạo sẵn:

// Update is called once per frame
public function Update () : void {
       if (Input.GetButton("Horizontal")) {
              //Walking
              in_direction = Input.GetAxis("Horizontal") < 0 ? -1: 1;
              rigidbody.velocity = new Vector3((in_direction*f_speed), rigidbody.velocity.y, 0);
              //Reset Stay animation frame back to the first frame
              loopSprites[0].resetFrame();

              //Update Walking animation while the character is walking
              loopSprites[1].updateAnimation(in_direction, renderer.material);
       } else {
              //Stay
              //Reset Walking animation frame back to the first frame
              loopSprites[1].resetFrame();
              //Update Stay animation while the character is not walking
              loopSprites[0].updateAnimation(in_direction, renderer.material);
       }
}

B6. Sau đó chúng ta sẽ tạo thêm hàm LateUpdate( ) phía bên dưới hàm Update( ). Chúng ta sẽ sử dụng hàm này để cập nhật vị trí camera sau khi nhân vật di chuyển bằng cách điều chỉnh tọa độ để theo sau nhân vật.

public function LateUpdate() : void {
       //Update Main Camera
       Camera.main.transform.position = new Vector3(transform.position.x, transform.position.y, Camera.main.transform.position.z);
}


B7. Kế tiếp, chúng ta sẽ tạo lớp SpriteManager để quản lý các ảnh chuyển động của chúng ta trong file JavaScript CharacterController2D vừa tạo ở các bước trên. Tiếp tục bổ sung đoạn code sau vào bên dưới hàm LateUpdate( ) trong file script chúng ta đang làm.
class SpriteManager {
       public var spriteTexture : Texture2D; //Set Texture use for a loop animation such as walking, stay, etc.
       public var in_framePerSec : int; //Get frame per sec to calculate time
       public var in_gridX : int; //Get max number of Horizontal images
       public var in_gridY : int; //Get max number of Vertical images
       private var f_timePercent : float;
       private var f_nextTime : float; //Update time by using frame persecond
       private var f_gridX : float;
       private var f_gridY : float;
       private var in_curFrame : int;
     
       public function init () : void {
              f_timePercent = 1.0/in_framePerSec;
              f_nextTime = f_timePercent; //Update time by using frame persecond
              f_gridX = 1.0/in_gridX;
              f_gridY = 1.0/in_gridY;
              in_curFrame = 1;
       }

       public function updateAnimation (_direction : int, _material : Material) : void {
              //Update material
              _material.mainTexture = spriteTexture;
              //Update frame by time
              if (Time.time>f_nextTime) {
                     f_nextTime = Time.time + f_timePercent;
                     in_curFrame++;
                     if (in_curFrame>in_framePerSec) {
                            in_curFrame = 1;
                     }
              }
              _material.mainTextureScale = new Vector2 (_direction * f_gridX, f_gridY);
              var in_col : int = 0;
              if (in_gridY>1) {
                     //If there is more than one grid on the y-axis update the texture
                     in_col= Mathf.Ceil(in_curFrame/in_gridX);
              }
              if (_direction == 1) { //Right
                     _material.mainTextureOffset = new Vector2(((in_curFrame)%in_gridX) * f_gridX, in_col*f_gridY);
              } else { //Left
                     //Flip Texture
                     _material.mainTextureOffset = new Vector2(((in_gridX + (in_curFrame)%in_gridX)) * f_gridX, in_col*f_gridY);
              }
       }

       public function resetFrame () :void {
              in_curFrame = 1;
       }
}

B8. Bây giờ, ấn Ctrl + S để save file script này lại, kéo thả nó vào Player ở thẻ Hierarchy. Nhấp chọn Player và qua thẻ Inspector điều chỉnh Size = 2 trong Loop Sprites và các mục khác như sau:


B9. Save scene lại, và ấn nút Play để kiểm tra thành quả. Bạn hãy ấn phím mũi tên qua trái / qua phải hoặc nút A / D để di chuyển qua trái hoặc qua phải.


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

Chúng ta vừa tạo một đoạn script để kiểm soát chuyển động của và xử lý các động tác của nhân vật. Trước tiên chúng ta thiết lập biết in_direction là 1 bởi vì chúng ta muốn nhân vật lúc bắt đầu sẽ quay mặt sang bên phải. Khi đó, mảng hình ảnh sẽ được lặp và khởi tạo trong lớp SpriteManager tùy theo độ dài mà chúng ta đặt. Chúng ta sẽ sử dụng main camera từ scene hiện tại thông qua cú pháp Camera.main. Cú pháp này cho phép ta truy cập đến đối tượng Main Camera từ bất kì đâu chúng ta muốn và khi đó chúng ta hướng vị trí của camera theo nhân vật. Tiếp theo, chúng ta sẽ đặt đoạn code vào hàm Update( ) đã được mặc định tạo sẵn, tương tự như hàm Start( ). Hàm này sẽ điều khiển quá trình chuyển động khi nhân vật từ bước đi sang nhảy và cập nhật các ảnh chuyển động.

Sau đó, chung ta sử dụng lớp Input để phát hiện khi người chơi nhấn phím bấm từ bàn phím. Chúng ta làm tất cả việc điều khiển nhân vật trong hàm Update( ). Đầu tiên, chúng ta sử dụng if (Input.GetButton("Horizontal")) { } để kiểm tra việc người chơi đã nhấn phím Horizontal (mặc định trong Unity là A, D, ←, →) và chúng ta di chuyển nhân vật nếu phím được nhấn. Dòng đầu tiên của hàm if kiểm tra hướng của nhân vật in_direction =Input.GetAxis("Horizontal") < 0 ? -1: 1; có nghĩa là nếu nhấn nút Horizontal, chúng ta sẽ lấy giá trị tọa độ từ hàm Input.GetAxis("Horizontal"). Hàm Input.GetAxis sẽ trả về giá trị từ -1 đến 1 biểu thị cho việc người chơi đang nhấn nút. Sau đó, chúng ta kiểm tra giá trị có nhỏ hơn 0 hay không. Nếu có thì hàm trả về -1 (di chuyển qua trái), nếu không thì trả về 1 (di chuyển qua phải). Khi đó dòng rigidbody.velocity = new Vector3((in_direction*f_speed), rigidbody.velocity.y, 0); chúng ta đã gán hướng và tốc độ vào rigidbodyvelocity. Chúng ta không gán giá trị cho trục Z bởi vì nhân vật của chúng ta không di chuyển theo hướng đó.

Cuối cùng, chúng ta tạo lớp SpriteManager cho file Javascript của chúng ta để điều khiển việc xử lý các vật liệu gán cho sprite bằng cách sử dụng tối đa số bức ảnh chúng ta đã tính với thời gian khi chạy mỗi bức ảnh. Hãy nhìn qua lớp SpriteManager. spriteTexture cơ bản dùng để thiết lập vật liệu của các sprite được lấy từ class. Những texture này sẽ được gọi và gán vào vật liệu chính khi nhân vật thay đổi di chuyển, như từ bước đi sang đứng yên, bước đi sang nhảy và vân vân.... in_framePerSec là tổng số ảnh texture của sprite, sẽ được tính để hiển thị trong khung hình kế tiếp. in_gridX là số dòng của sprite texture, và in_gridY là số cột, để tính toán Tiling và Offset của texture như chúng ta đã tìm hiểu ở bài viết trước. Chúng ta thiết lập private cho các biến f_timePercent, f_nextTime, f_gridX, f_gridY, và in_curFrame, mà chúng ta sử dụng để tính toán trong hàm updateAnimation( ). Tiếp theo, chúng ta có hàm init( ). Hàm này cơ bản là thiết lập các biến. Sau đó, hàm updateAnimation( ) sẽ lấy vật liệu và hướng nhìn của nhân vật để xử lý và cập nhật các ảnh chuyển động. Cuối cùng, chúng ta có hàm resetFrame( ) để thiết lập lại số khung hình chuyển động về 1.


Kiến thức bổ sung

Input Manager


Trong Unity, chúng ta có thể tùy chỉnh thiết lập Input Manager bằng cách vào Edit | Project Settings | Input. Trong thẻ Inspector, nhấp chọn Axes và bạn sẽ thấy Size: 17, là độ dài mảng của tất cả các input. Nếu có hơn 17 input, chúng ta có thể nhập số vào đây (mặc định là 17). Tiếp theo chúng ta sẽ thấy có 17 tên từ Horizontal đến Jump như thiết lập mặc định. Mỗi cái sẽ có các biến riêng chúng ta có thể điều chỉnh.

Chúng ta có thể thông tin thêm tại website của Unity:

http://unity3d.com/support/documentation/Components/classInputManager.html

Trong file script của chúng ta, chúng ta sử dụng hàm Input.GetButton("Horizontal"). GetButton dùng để kiểm tra nếu các nút Horizontal được nhấn hay không. Horizontal là tên của input đầu tiên như chúng ta đã thấy ở ảnh minh họa bên trên. Chúng ta cũng có thể dùng Input.GetKey("left") để làm việc đó. Nó cũng có kết quả tương tự với Input.GetButton, nhưng khác nhau là GetKey chỉ phát hiện khi phím chỉ định được nhấn. Vì vậy GetButton dễ cho việc người dùng thay đổi phím trong suốt quá trình chơi. 2 nút Negative Button và Positive Button sẽ gửi giá trị âm và dương, thích hợp cho việc điều hướng như lên, xuống, trái, phải. Ngoài ra còn có biến Dead, biến này sẽ thiết lập bất kì số nào nhỏ hơn từ giá trị của biến này đến 0, rất thích hợp cho việc sử dụng joystick. Cũng có thể điều chỉnh Type thành key/mouse và bật biến Snap để khi nhận được giá trị đối nghịch tọa độ sẽ trả về 0.


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.

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ứ Tư, 9 tháng 4, 2014

Project RPG BÀI 14B. DỌN DẸP FILE SKILL


Thêm chú thích và các công dụng để dễ hiểu rõ hơn phần nào về các tính năng của script Skill...


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

/// <summary>
/// Skill.cs
///
/// This class contain all the extra function that are needed for a skill
/// </summary>
public class Skill : ModifiedStat {
    private bool _known;            //a boolean variable to toggle if a character knowns a skill

    /// <summary>
    /// Initializes a new instance of the <see cref="Skill"/> class.
    /// </summary>
    public Skill(){
        UnityEngine.Debug.Log ("Skill created");
        _known = false;
        ExpToLevel = 25;
        LevelModifier = 1.1f;
    }

    /// <summary>
    /// Gets or sets a value indicating whether this <see cref="Skill"/> is known.
    /// </summary>
    /// <value><c>true</c> if known; otherwise, <c>false</c>.</value>
    public bool Known{
        get{ return _known; }
        set{ _known = value; }
    }
}

/// <summary>
/// This enumeration is just a list of skills the player can learn
/// </summary>
public enum SkillName{
    Melee_Offence,
    Melee_Defence,
    Ranged_Offence,
    Ranged_Defence,
    Magic_Offence,
    Magic_Defence
}

Project RPG BÀI 14A. DỌN DẸP FILE VITAL


Thêm chú thích và các công dụng để dễ hiểu rõ hơn phần nào về các tính năng của script Vital...


Double click vào file Vital và xóa tất cả đi, chèn lại đoạn script sau vào:

/// <summary>
/// Vital.cs
/// This class contain all the extra function for a character vitals
/// </summary>

public class Vital : ModifiedStat {
    private int _curValue;                //this is the current value of this vital

    /// <summary>
    /// Initializes a new instance of the <see cref="Vital"/> class.
    /// </summary>
    public Vital(){
        UnityEngine.Debug.Log ("Vital Created");
        _curValue = 0;
        ExpToLevel = 40;
        LevelModifier = 1.1f;
    }

    /// <summary>
    /// When getting the _curValue, make sure that it is not greater than our AdjustedBaseValue
    /// If it is, make it the same as our AdjustedBaseValue
    /// </summary>
    /// <value>The current value.</value>
    public int CurValue{
        get{
            if(_curValue > AdjustedBaseValue)
                _curValue = AdjustedBaseValue;
           
            return _curValue;
        }
        set{ _curValue = value; }
    }
}

/// <summary>
/// This enumerations is just a list of the vitals our character will have
/// </summary>
public enum VitalName{
    Health,
    Energy,
    Mana
}

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

ProjectGame 1.1 - Platform Game Đơn Giản - Tạo Camera và Level


Vẫn còn nhiều platform game kinh điển tồn tại trong kí ức mọi người như Mario, Sonic, Megaman... Platform game là dạng game với động tác nhảy và yếu tố trọng lực là chính, ngoài ra các yếu tố phụ khác đã góp phần làm nên tên tuổi của các platform game kinh điển trên. Bài viết này được chia ra làm 6 phần nhỏ. Tất cả đều là nền tảng 2D, để các bạn dễ hiểu hơn những kiến thức về lập trình game và dễ đi sâu hơn vào các project game sau trên nền tảng 3D.


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

Tài nguyên
GameProject1



  PG 1.1 - Platform Game đơn giản: Tạo Camera và Level

B1. Vào File | New Project để tạo Project mới với tên là SimplePlatform như hình sau:


B2. Giải nén file GameProject1 vừa tải ở trên và kéo thả thư mục Game Project 1 vào thẻ Project như hình sau:


B3. Tại thẻ Project, nhấp chuột chọn Plane trong thư mục Game Project 1 | FBX và qua thẻ Inspector, điều chỉnh Scale Factor = 1. Sau đó nhấn Apply để hoàn tất việc điều chỉnh.


B4. Kéo thả Plane vào thẻ Hierarchy và đổi tên thành Background.


B5. Nhấp chọn Background trong thẻ Hierarchy, qua thẻ Inspector và điều chỉnh như hình sau:


B6. Vẫn đang chọn Background, ở thẻ Inspector, nhấp phải vào Animator và chọn Remove Component.


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


B8. Nhấp chọn M_Background, qua thẻ Inspector, nhấp vào chữ Select và chọn Background như hình sau.


B9. Vẫn đang chọn M_Background, điều chỉnh như sau:
  • Shader: Diffuse
  • Main Color: R = 164, G = 219, B = 225, A = 255
  • Base (RGB): Tiling X =  2, Y = 2; Offset: X = 0, Y = 0



B10. Nhấp chọn Background ở thẻ Hierarchy, qua thẻ Inspector, tại mục Mesh Renderer | Material điều chỉnh Element 0 là M_Background.


B11. Vào Edit | Project Setting | Tags & Layers và nhập vào các giá trị như sau:


B12. Nhấp chọn Background ở thẻ Hierarchy, qua thẻ Inspector, chọn Tag = Background và Layer = Background như hình sau:


B13. Tại thẻ Hierarchy, nhấp chọn Main Camera, qua thẻ Inspector, điều chỉnh Projection thành Perspective và Position như trong hình sau:


B14. Tại thẻ Hierarchy, nhấp chọn Create | Directional Light và qua thẻ Inspector điều chỉnh như sau:


B15. Vào GameObject | Create Empty và đổi tên thành Level. Qua thẻ Inspector, điều chỉnh các thông số như hình sau:

B16. Tại thẻ Hierarchy, nhấp chọn nút Create | Cube và đổi tên thành Floor. Đặt tag là Floor, layer là Level như hình sau:

B17. Thực hiện lại bước trên một lần nữa với tên cube là Wall, tag là Wall và layer là Level.


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


B19. Nhấp chọn M_Level vừa tạo và qua thẻ Inspector, bấm vào ô điều chỉnh màu và chỉnh các thông số màu sắc là R = 150, G = 230, B = 225, A = 255.


B20. Kéo thả M_Level ở thẻ Project vào Wall và Floor ở thẻ Hierarchy.

 


B21. Tại thẻ Hierarchy, kéo thả Wall và Floor vào Level như hình sau:


B22. Tại thẻ Hierarchy, nhấp chọn Wall và ấn Ctrl + D 6 lần để tạo thêm 6 bản sao nữa. Nhấp chọn Floor và ấn Ctrl + D 2 lần để tạo ra thêm 2 bản sao nữa. Và đặt tên như hình dưới đây:


B23. Tại thẻ Hierarchy, điều chỉnh các bản sao vừa tạo ở bước trên với tên và thông số ở thẻ Inspector như dưới đây:
  • Floor 1 : Position: x = -4, y = -9, z = 0 - Scale: x = 125, y = 15, z = 1
  • Floor 2: Position: x = -6,  y = 5, z = 0 - Scale: x = 32, y = 1, z = 1
  • Floor 3: Position: x = -25, y = 12, z = 0 - Scale: x = 19.5, y = 1, z = 1
  • Floor 4: Position: x = 14, y = 12, z = 0 - Scale: x = 20, y = 1, z = 1
  • Floor 5: Position: x = -7, y = 9, z = 0 - Scale: x = 9, y = 1, z = 1
  • Floor 6: Position: x = -31, y = 1, z = 0 - Scale: x = 6, y = 1, z = 1
  • Floor 7: Position: x = 21, y = 2, z = 0 - Scale: x = 10, y = 1, z = 1

  • Wall 1: Position: x = -49, y = 17, z = 0 - Scale: x = 36, y = 40, z = 1
  • Wall 2: Position: x = 42, y = 17, z = 0 - Scale: x = 38, y = 39, z = 1
  • Wall 3: Position: x = -7, y = 23, z = 0 - Scale: x: 1, y = 36, z = 1

B24. Nhấn Ctrl + S để save scene này lại với tên là SimplePlatform.



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

Vừa rồi chúng ta đã tạo Background đặt phía sau Level, Level bao gồm các khối gạch được xếp theo tầng (Floor) và tường (Wall) để nhân vật có thể di chuyển lên các tầng và giới hạn phạm vi di chuyển của nhân vật bởi các bức tường.





Ở đây, chúng ta điều chỉnh Main Camera ở chế độ Perspective để tạo chiều sâu cho game, khiến góc nhìn đẹp mắt hơn.


Kiến thức bổ sung
Phần Projection ở Main Camera có 2 giá trị là Orthographic (Góc nhìn trực quan) và Perspective (Góc nhìn phối cảnh). Ở chế độ Orthographic, vật thể sẽ không bị kéo dãn bởi khoảng cách của camera. Bởi vậy trong màn hình game, chúng ta chỉ thấy được 1 mặt của vật thể. Ở chế độ Perspective, chúng ta sẽ nhìn thấy được các mặt bên của vật thể do có chiều sâu, tựa như góc nhìn thực tế của mắt người bởi luật phối cảnh.