Thứ Tư, 18 tháng 12, 2013

AI 1.3 - BẦY, ĐÀN VÀ LŨ (PHẦN II)

  1.3 - Bầy, đàn và lũ ( Phần II )

Tìm đường A*

Có nhiều game, trong đó bạn có thể tìm các con quỷ hoặc địch mà theo sau người chơi, hoặc đi đến một điểm riêng khi tránh các chướng ngại. Chẳng hạn, chúng ta nhìn về một trò game RTS một cách hình thức. Các bạn có thể chọn một nhóm các đơn vị và nhấp vào một vị trí nơi bạn muốn chúng di chuyển hoặc nhấp vào các đơn vị địch để tấn công chúng. Đơn vị của bạn khi đó cần tìm ra đường để đến đích mà không đụng vào các chướng ngại. Đơn vị địch cũng cần có thể làm cùng như thế. Các chướng ngại có thể khác nhau từ các đơn vị khác nhau. Chẳng hạn, một đơn vị hạm đội trên không có thể băng qua đồi, trong khi đất hoặc các đơn vị pháo binh cần tìm ra đường quanh nó.


A* (phát âm là « A sao ») là thuật toán tìm đường được sử dụng rộng rãi trong game, vì sự biểu diễn và độ chính xác của nó. Hãy nhìn vào một ví dụ để thấy làm sao nó làm việc. Hãy nói chúng ta muốn đơn vị của mình di chuyển từ điểm A đến điểm B, nhưng có một bức tường trên đường, và nó không thể di chuyển thẳng đến mục tiêu. Vì thế, cần tìm ra một con đường đến điểm B khi đang tránh bức tường.

Hình : Top-down view of our map

Chúng ta đang tìm một ví dụ 2D đơn giản. Nhưng cùng ý tưởng có thể được áp dụng vào môi trường 3D. Để tìm ra đường từ điểm A đến điểm B, chúng ta cần biết nhiều về bản đồ chẳng hạn như vị trí của vật thể. Vì điều đó chúng ta có thể chia toàn bộ bản đồ thành những lát nhỏ, tiêu biểu là gắn lưới cho bản đồ, như việc biểu diễn trong hình sau :

Map represented in a 2D grid

Các ô cũng có thể là một trong những hình khác như lục giác hoặc tam giác. Nhưng chúng ta sẽ chỉ dùng ô vuông ở đây, nó đơn giản và đầy đủ cho mục đích của chúng ta. Đại diện cho toàn bộ bản đồ lưới, tìm kiếm khu vực đơn giản hơn, và đây là một bước quan trọng trong việc tìm đường. Chúng ta giờ đây có thể liên quan đến bản đồ của chúng ta trong một mảng 2D nhỏ. Bản đồ của chúng ta bây giờ biểu diễn bằng 5 x 5 ô vuông, có tổng cộng 25 ô. Chúng ta có thể tìm kiếm con đường tối ưu để đến đích. Làm sao chúng ta làm được điều này ? Bằng việc tính toán điểm số di chuyển của mỗi ô kề sát với ô bắt đầu, là một ô trên bản đồ không có một chướng ngại, và rồi chọn ô có giá trị thấp nhất.

Có bốn ô kề nhau với người chơi, nếu chúng ta không xét những bước di chuyển chéo. Bây giờ, chúng ta cần biết hai số để tính điểm di chuyển cho mỗi ô đó. Hãy gọi chúng là G và H, với G là giá trị di chuyển từ ô đầu đến ô hiện tại, và H là giá trị để đến ô đích từ ô hiện tại.

Bằng cách thêm G và H, chúng ta có thể có điểm cuối cùng của ô đó; hãy gọi nó là F. Vì thế chúng ta sẽ dùng công thức này: F = G + H.

Valid adjacent tiles

Trong ví dụ này, chúng ta sẽ dùng một cách đơn giản gọi là chiều dài Manhattan (cũng được biết như là hình học Taxicab), trong đó chúng ta chỉ tính tổng số ô giữa ô đầu và ô đích để biết khoảng cách giữa chúng.


Calculating G

Hình trên chỉ ra những tính toán của G theo 2 cách khác nhau. Chúng ta chỉ thêm một đối tượng (giá trị để di chuyển một ô) đến điểm G của ô trước khi đến điểm G hiện tại của ô hiện tại. Chúng ta có thể cho những giá trị khác nhau đối với các ô khác nhau. Chẳng hạn, chúng ta có thể muốn cho giá trị di chuyển cao hơn đối với các di chuyển chéo (nếu chúng ta đang xét đến chúng), hoặc để chỉ những ô đang bị chiếm, hãy nói một cây cầu hoặc một đường bùn. Giờ chúng ta biết làm sao để có G. Chúng ta nhìn vào phép tính H. Hình sau biểu thị giá trị H khác nhau từ những ô đầu khác nhau đến ô đích. Bạn có thể cố tính các ô vuông giữa chúng để hiểu làm sao chúng ta có những giá trị đó.

 
Calculating H

Vì thế, bây giờ chúng ta biết làm sao để có G và H. Hãy đi trở lại ví dụ đầu để tìm ra đường ngắn nhất từ A đến B. Đầu tiên chúng ta chọn ô bắt đầu, và xét những ô liền kề hợp lý, chỉ ra trong hình sau. Rồi chúng ta tính điểm G và H của mỗi ô, chỉ ra trong những góc trái bên dưới và bên phải của ô theo thứ tự. Và rồi điểm F cuối cùng, mà G + H chỉ ra góc trái bên trên. Hiển nhiên, ô đến bên phải trực tiếp của ô đầu có điểm F thấp nhất.

Vì thế, chúng ta chọn ô này như bước di chuyển tiếp theo của chúng ta, và lưu giữ ô trước đó như ô cha của nó.

Ô cha này sẽ có ích về sau, khi chúng ta theo để vết phía đường cuối cùng của chúng ta.

Starting position

Từ ô hiện tại, chúng ta làm bước phát triển tương tự lần nữa, xác định các ô liền kề đúng. Lần này chỉ có hai ô liền kề hợp lý ở trên và dưới. Ô bên trái là ô mà chúng ta đã xác định, và có chướng ngại ở ô bên phải. Chúng ta tính G, H, và rồi điểm F của những ô liền kề mới đó. Lần này chúng ta có bốn ô ở bản đồ của chúng ta với tất cả các ô có cùng điểm, sáu. Vì thế, cái nào là ô chúng ta chọn? Chúng ta có thể chọn bất kỳ trong chúng. Điều đó thật sự chẳng là vấn đề trong ví dụ này, nếu chúng có cùng điểm số. Thông thường, chúng ta chỉ chọn ô được thêm vào gần đây để liệt kê ô liền kề của chúng ta. Điều này vì sau đó, chúng ta sẽ dùng vài cấu trúc dữ liệu, như một danh sách để lưu trữ các ô đó, mà đang được xem xét cho bước tiếp theo. Vì thế, việc kết nối đến ô gần nhất được thêm vào danh sách có thể nhanh hơn việc tìm kiếm thông qua danh sách để đến một ô riêng mà được thêm vào trước đó.

Trong phần demo này, chúng ta sẽ chỉ chọn ngẫu nhiên ô cho phần test tiếp theo của chúng ta, để chứng minh rằng nó có thể tìm ra đường đi ngắn nhất.

Second Step

Vì vậy, chúng ta chọn ô được tô đỏ này. Chúng ta kiểm tra lần nữa ở những ô liền kề. Trong bước này, chỉ có một ô liền kề mới với một điểm F được tính của 8. Vì thế, điểm thấp nhất ngay bây giờ vẫn là 6. Chúng ta có thể chọn bất kỳ ô nào với điểm 6.


 
Third Step

Vì thế, chúng ta chọn một ô ngẫu nhiên từ tất cả những ô với điểm 6. Nếu chúng ta lặp lại bước tiến này cho tới khi chúng ta đến ô đích, chúng ta sẽ kết thúc với một bảng hoàn toàn với tất cả các điểm cho mỗi ô hợp lý.

 
Reach target

Bây giờ chúng ta phải làm để lưu vết điểm đầu từ ô đích bằng cách dùng ô cha (parent). Nó sẽ cho ta một con đường như hướng dẫn dưới đây:

 
Lưu vết - Path traced back

Đây là khái niệm của tìm đường A* bằng nút thắt, không hiển thị bất kỳ mã. A* là một khái niệm quan trọng trong khu vực tìm đường AI, nhưng từ Unity 3.5, có một cặp của những đặc điểm mới như việc mạng lưới điều hướng tự động và chi tiết trong chương 8, Navigation Mesh. Những đặc điểm này làm cho việc tìm đường trong game của chúng ta dễ dàng hơn. Quả thật, thậm chí bạn có thể cần biết về A* để thực thi việc tìm đường cho nhân vật AI của bạn. Tuy nhiên, việc biết cách hệ thống hiển nhiên hoạt động phía sau scene sẽ giúp bạn trở thành một nhà lập trình AI chắc chắn. Không may, những đặc điểm điều hướng tiến triển đó trong Unity chỉ có thể trong phiên bản Pro vào thời điểm này.

Chủ Nhật, 15 tháng 12, 2013

Project RPG BÀI 6. KHỞI TẠO NHÂN VẬT

 
Ở bài viết trước mình đã cung cấp file code về các thông số vật lý của nhân vật để làm dữ liệu cho việc khởi tạo các tính chất vật lý của nhân vật. Bài viết này mình sẽ sử dụng các cơ sở dữ liệu ở bài viết trước để tạo nên menu khởi tạo các thông số cho nhân vật.



B1. Nhấp phải vào folder Character Classes và chọn Creat | C# Script và đặt tên là PlayerCharacter. Double click vào file vừa tạo và chèn đoạn code sau vào:

public class PlayerCharacter : BaseCharacter {

}

B2. Nhấp phải vào folder Character Classes và chọn Creat | C# Script và đặt tên là CharacterGenerator. Double click vào file vừa tạo và chèn đoạn code sau vào:

using UnityEngine;
using System.Collections;
using System;                 //used for Enum class

public class CharacterGenerator : MonoBehaviour {
    private PlayerCharacter _toon;
    private const int STARTING_POINTS = 350;
    private const int MIN_STARTING_ATTRIBUTE_VALUE = 10;
    private const int STARTING_VALUE = 50;
    private int pointsLeft;

    private const int OFFSET = 5;
    private const int LINE_HEIGHT = 20;

    private const int STAT_LABEL_WIDTH = 100;
    private const int BASEVALUE_LABEL_WIDTH = 30;
    private const int BUTTON_WIDTH = 20;
    private const int BUTTON_HEIGHT = 20;

    private int statStartingPos = 40;

    // Use this for initialization
    void Start () {
        _toon = new PlayerCharacter();
        _toon.Awake();

        pointsLeft = STARTING_POINTS;
        for(int cnt = 0; cnt < Enum.GetValues(typeof(AttributeName)).Length; cnt++){
            _toon.GetPrimaryAttribute(cnt).BaseValue = STARTING_VALUE;
            pointsLeft -= (STARTING_VALUE - MIN_STARTING_ATTRIBUTE_VALUE);
        }
        _toon.StatUpate();
    }
    
    // Update is called once per frame
    void Update () {

    }

    void OnGUI(){
        DisplayName();
        DisplayPointsLeft();
        DisplayAttributes();
        DisplayVitals();
        DisplaySkills();
    }

    private void DisplayName(){
        GUI.Label(new Rect(10, 10, 50, 25),"Name:");
        _toon.Name = GUI.TextField(new Rect(65, 10, 100, 25), _toon.Name );

    }

    private void DisplayAttributes(){
        for(int cnt = 0; cnt < Enum.GetValues(typeof(AttributeName)).Length; cnt++){
            GUI.Label(new Rect( OFFSET,                                    //x
                                statStartingPos + (cnt * LINE_HEIGHT),     //y
                                STAT_LABEL_WIDTH,                         //width
                                LINE_HEIGHT                                //height
                               ), ((AttributeName)cnt).ToString());
            GUI.Label(new Rect( STAT_LABEL_WIDTH + OFFSET,                 //x
                                statStartingPos + (cnt * LINE_HEIGHT),     //y
                                BASEVALUE_LABEL_WIDTH,                     //width
                                LINE_HEIGHT                                //height
                               ), _toon.GetPrimaryAttribute(cnt).AdjustedBaseValue.ToString());
            if(GUI.Button(new Rect( OFFSET + STAT_LABEL_WIDTH + BASEVALUE_LABEL_WIDTH,     //x
                                    statStartingPos + (cnt*BUTTON_HEIGHT),                 //y
                                   BUTTON_WIDTH,                                         //width
                                   BUTTON_HEIGHT                                        //height
                                   ),"-")){
                if(_toon.GetPrimaryAttribute(cnt).BaseValue > MIN_STARTING_ATTRIBUTE_VALUE){
                    _toon.GetPrimaryAttribute(cnt).BaseValue--;
                    pointsLeft++;
                    _toon.StatUpate();
                }
            }

            if(GUI.Button(new Rect( OFFSET + STAT_LABEL_WIDTH + BASEVALUE_LABEL_WIDTH + BUTTON_WIDTH,     //x
                                    statStartingPos + (cnt*BUTTON_HEIGHT),                                 //y
                                    BUTTON_WIDTH,                                                         //width
                                    BUTTON_HEIGHT                                                        //height
                                   ),"+")){
                if(pointsLeft > 0){
                    _toon.GetPrimaryAttribute(cnt).BaseValue++;
                    pointsLeft--;
                    _toon.StatUpate();
                }
            }
        }
    }

    private void DisplayVitals(){
        for(int cnt = 0; cnt < Enum.GetValues(typeof(VitalName)).Length; cnt++){
            GUI.Label(new Rect( OFFSET,                                             //x
                                statStartingPos + ((cnt + 7)* LINE_HEIGHT),         //y
                                STAT_LABEL_WIDTH,                                     //width
                                LINE_HEIGHT                                            //height
                               ), ((VitalName)cnt).ToString());
            GUI.Label(new Rect( OFFSET + STAT_LABEL_WIDTH,
                                statStartingPos + ((cnt + 7) * LINE_HEIGHT),
                                BASEVALUE_LABEL_WIDTH,
                                LINE_HEIGHT
                               ), _toon.GetVital(cnt).AdjustedBaseValue.ToString());
            
        }

    }

    private void DisplaySkills(){
        for(int cnt = 0; cnt < Enum.GetValues(typeof(SkillName)).Length; cnt++){
            GUI.Label(new Rect( OFFSET + STAT_LABEL_WIDTH + BASEVALUE_LABEL_WIDTH + BUTTON_WIDTH * 2 + OFFSET * 2,             //x
                                statStartingPos + (cnt * LINE_HEIGHT),                                                         //y
                                STAT_LABEL_WIDTH,                                                                             //width
                                LINE_HEIGHT                                                                                    //height
                               ), ((SkillName)cnt).ToString());
            GUI.Label(new Rect( OFFSET + STAT_LABEL_WIDTH + BASEVALUE_LABEL_WIDTH + BUTTON_WIDTH * 2 + OFFSET * 2 + STAT_LABEL_WIDTH,     //x
                                statStartingPos + (cnt * LINE_HEIGHT),                                                                     //y
                                BASEVALUE_LABEL_WIDTH,                                                                                     //width
                                LINE_HEIGHT                                                                                                //height
                               ), _toon.GetSkill(cnt).AdjustedBaseValue.ToString());
            
        }
        
    }

    private void DisplayPointsLeft(){
        GUI.Label(new Rect(250, 10, 100, 25), "Points Left: " + pointsLeft.ToString());

    }
}

B3. Tạo thẻ Project, nhấp chọn nút Creat và chọn Folder và đặt tên là Scenes. Ấn Ctrl + Shift + S để save scene hiện tại lại với tên là Targetting và kéo thả vào thư mục Scenes.


B4. Vào File | New Scene, sau đó ấn Ctrl + S để save scene này lại và đặt tên là Character Generator.


B5. Tại thẻ Project, kéo thả file C# Script CharacterGenerator vào MainCamera ở thẻ Hierarchy.

B6. Double click vào file BaseCharacter ở thẻ Project | Script | Character Classes và chèn thêm đoạn code được tô đỏ như sau:

    private void SetupVitals(){
        for(int cnt = 0; cnt < _vital.Length; cnt++){
            _vital[cnt] = new Vital();
        }
        SetupVitalModifiers();
       
    }
    private void SetupSkills(){
        for(int cnt = 0; cnt < _skill.Length; cnt++){
            _skill[cnt] = new Skill();
        }
        SetupSkillModifiers();
       
    }

B7. Bấm Ctrl + S để save scene Character Generator lại. Ấn nút Play để kiểm tra thành quả.

Thứ Ba, 10 tháng 12, 2013

Cookbook - HIỆU ỨNG TẠO HẠT

 - Hiệu ứng tạo hạt -

Với phiên bản Unity 3.5 trở đi, rất nhiều hiệu ứng hữu dụng được tạo ra bởi Particle System ( Hệ thống tạo hạt ). Nhiều hiệu ứng cần phải viết script đều được cung cấp trong hệ thống Shuriken particle.


Chuẩn bị

Trước tiên hãy tìm hiểu qua về các thiết lập và khung giá trị của particle system trước khi sử dụng nó.
- Energy : Khoảng thời gian các hạt tồn tại trong game cho đến khi nó biến mất.
- Looping : Số lượng các hạt sẽ được khởi tạo lại khi hạt cuối cùng biến mất.
- Speed, direction, rotation : Mỗi hạt đều có hướng di chuyển khác nhau, tùy theo điều chỉnh tốc độ, hướng di chuyển hay góc độ quay của từng hạt.

Ngoài ra còn có các điều chỉnh phụ phía sau khung nhập giá trị như:
- Constant : Các hạt đều mang các thông số tùy chỉnh giống nhau.
- Curve : Các thông số của các hạt sẽ thay đổi dựa theo độ lồi lõm của đồ thị (VD: các hạt ban đầu từ từ to hơn theo thời gian hoặc ngược lại).
- Random number between maximum and minimum constants: Thông số của các hạt sẽ thay đổi dựa theo 2 giá trị hằng số người dùng nhập vào.
-Random number between two curve: Thông số của các hạt sẽ thay đổi dựa theo 2 đồ thị người dùng thiết lập.

Hình dưới đây là 2 đồ thị điều chỉnh kích thước của các hạt, các hạt lúc mới tạo thành sẽ có kích thước nhỏ ( từ 0.0  đến 0.2 ), và trở nên to dần sau 3 giây ( trục ngang - x ).
Kích thước của các hạt được lấy ngẫu nhiên trong vùng giữa 2 đồ thị, vì thế càng về cuối, giá trị này sẽ thay đổi trong khoảng từ 1.0  đến 2.8 so với ban đầu.



Cách để làm

B1.  Tạo project mới (File | New project) và đánh dấu vào ô Particle.unityPackage.


B2. Tại thẻ Hierarchy, nhấp chọn nút Creat | Particle System.

B3. Nhấp chuột chọn Particle System ở thẻ Hierarchy, qua thẻ Inspector, bấm chuột vào hình bánh răng kế bên component Particle System và chọn Reset.


B4. Vẫn tại thẻ Inspector, kéo thanh trượt xuống bên dưới cùng của component Particle System. Bấm chuột chọn dòng Renderer và đổi Material thành SoapBubble.


B5. Điều chỉnh StartSpeed thành 2 và StartSize là hằng số ngẫu nhiên từ 0.2 đến 1.



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

Project RPG BÀI 5.2. NỀN TẢNG NHÂN VẬT


Để tạo được các thuộc tính vật lý hay các thông số vật lý cho nhân vật, các bạn phải khai báo các thuộc tính này thành các class khác nhau, bài viết này chủ yếu khởi tạo các thuộc tính cơ bản của người chơi như Tốc độ, Trí tuệ, Sức mạnh,... tất cả đều code bằng C# và chưa thể sử dụng được liền mà phải đợi sử dụng ở Bài 6. Khởi Tạo Nhân Vật.



B1. Nhấp phải vào folder Character Classes rồi chọn Creat | C Sharp Script và đặt tên là BaseCharacter.

B2. Double click vào file C# vừa tạo và chèn đoạn code sau vào:

using UnityEngine;
using System.Collections;
using System;                    //added to access the enum class

public class BaseCharacter : MonoBehaviour {
    private string _name;
    private int _level;
    private uint _freeExp;

    private Attribute[] _primaryAttribute;
    private Vital[] _vital;
    private Skill[] _skill;

    public void Awake(){
        _name = string.Empty;
        _level = 0;
        _freeExp = 0;

        _primaryAttribute = new Attribute[Enum.GetValues(typeof(AttributeName)).Length];
        _vital = new Vital[Enum.GetValues(typeof(VitalName)).Length];
        _skill = new Skill[Enum.GetValues(typeof(SkillName)).Length];

        SetupPrimaryAttributes();
        SetupVitals();
        SetupSkills();
    }

    public string Name{
        get{ return _name;}
        set{ _name = value;}
    }
    public int Level{
        get{ return _level;}
        set{ _level = value;}
    }
    public uint FreeExp{
        get{ return _freeExp;}
        set{ _freeExp = value;}
    }
       
    public void AddExp(uint exp){
        _freeExp += exp;

        CalculateLevel();
    }

    //take avg of all of the players skills and assign that as the player lv
    public void CalculateLevel(){

    }

    private void SetupPrimaryAttributes(){
        for(int cnt = 0; cnt < _primaryAttribute.Length; cnt++){
            _primaryAttribute[cnt] = new Attribute();
        }
    }
    private void SetupVitals(){
        for(int cnt = 0; cnt < _vital.Length; cnt++){
            _vital[cnt] = new Vital();
        }
       
    }
    private void SetupSkills(){
        for(int cnt = 0; cnt < _skill.Length; cnt++){
            _skill[cnt] = new Skill();
        }
       
    }

    public Attribute GetPrimaryAttribute(int index){
        return _primaryAttribute[index];
    }

    public Vital GetVital(int index){
        return _vital[index];
    }

    public Skill GetSkill(int index){
        return _skill[index];
   
    }
    private void SetupVitalModifiers(){
        //health
        GetVital((int)VitalName.Health).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Constituion), .5f));
        //energy
        GetVital((int)VitalName.Energy).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Constituion), 1));
        //mana
        GetVital((int)VitalName.Mana).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Willpower), 1));

    }
    private void SetupSkillModifiers(){
        //melee offence
        GetSkill((int)SkillName.Melee_Offence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Might), .33f));
        GetSkill((int)SkillName.Melee_Offence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Nimbleness), .33f));
        //melee defence
        GetSkill((int)SkillName.Melee_Defence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Speed), .33f));
        GetSkill((int)SkillName.Melee_Defence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Constituion), .33f));
        //magic offence
        GetSkill((int)SkillName.Magic_Offence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Concentration), .33f));
        GetSkill((int)SkillName.Magic_Offence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Willpower), .33f));
        //magic defence
        GetSkill((int)SkillName.Magic_Defence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Concentration), .33f));
        GetSkill((int)SkillName.Magic_Defence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Willpower), .33f));
        //ranged offence
        GetSkill((int)SkillName.Ranged_Offence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Concentration), .33f));
        GetSkill((int)SkillName.Ranged_Offence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Speed), .33f));
        //ranged defence
        GetSkill((int)SkillName.Ranged_Defence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Speed), .33f));
        GetSkill((int)SkillName.Ranged_Defence).AddModifier(new ModifyingAttribute(GetPrimaryAttribute((int)AttributeName.Nimbleness), .33f));
    }

    public void StatUpate(){
        for(int cnt = 0; cnt < _vital.Length; cnt++)
            _vital[cnt].Update();
        for(int cnt = 0; cnt < _skill.Length; cnt++)
            _skill[cnt].Update();
    }
}

Thứ Sáu, 6 tháng 12, 2013

Project RPG BÀI 5.1. THÔNG SỐ NHÂN VẬT


Để tạo được các thuộc tính vật lý hay các thông số vật lý cho nhân vật, các bạn phải khai báo các thuộc tính này thành các class khác nhau, bài viết này chủ yếu khởi tạo các thuộc tính cơ bản của người chơi như Tốc độ, Trí tuệ, Sức mạnh,... tất cả đều code bằng C# và chưa thể sử dụng được liền mà phải đợi sử dụng ở các bài viết sau.


B1. Nhấp phải vào thư mục Script ở thẻ Project rồi chọn Creat | Folder và đặt tên là Character Classes.


B2. Nhấp phải vào thư mục Character Classes rồi chọn Creat | C Sharp Script và đặt tên là BaseStat.

B3. Double Click vào file C# vừa tạo và chèn đoạn code sau vào:

 public class BaseStat {
    private int _baseValue;                //the base value of this stat
    private int _buffValue;                //the amount of the buff to this stat
    private int _expToLevel;            //the total amount of exp needed to raise this skill
    private float _levelModifier;            //the modifier applied to the exp needed to raise this skill

    public BaseStat(){
        _baseValue = 0;
        _buffValue = 0;
        _levelModifier = 1.1f;
        _expToLevel = 100;
    }

    #region Basic Setters and Getters
    //Basic Setters and Getters
    public int BaseValue{
        get{ return _baseValue;}
        set{ _baseValue = value;}

    }

    public int BuffValue{
        get{ return _buffValue;}
        set{ _buffValue = value;}
      
    }

    public int ExpToLevel{
        get{ return _expToLevel;}
        set{ _expToLevel = value;}
      
    }

    public float LevelModifier{
        get{ return _levelModifier;}
        set{ _levelModifier = value;}
      
    }
    #endregion

    private int CalculateExpToLevel(){
        return (int)(_expToLevel * _levelModifier);
    }

    public void LevelUp(){
        _expToLevel = CalculateExpToLevel();
        _baseValue++;
    }

    public int AdjustedBaseValue{
        get{ return _baseValue + _buffValue; }
    }
}


B4. Tạo thêm file C Sharp Script trong thư mục Character Classes và đặt tên là Attribute.

B5. Double Click vào file C# vừa tạo và chèn đoạn code sau vào:

public class Attribute : BaseStat {
    public Attribute(){
        ExpToLevel = 50;
        LevelModifier = 1.05f;
    }
}

public enum AttributeName{
    Might,
    Constituion,
    Nimbleness,
    Speed,
    Concentration,
    Willpower,
    Charisma
}


B6.  Tạo thêm file C Sharp Script trong thư mục Character Classes và đặt tên là ModifiedStat.

B7. Double Click vào file C# vừa tạo và chèn đoạn code sau vào:

 using System.Collections.Generic;

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

    public ModifiedStat(){
        _mods = new List<ModifyingAttribute>();
        _modValue = 0;
    }

    public void AddModifier( ModifyingAttribute mod ){
        _mods.Add(mod);
    }

    private void CalculateModValue(){
        _modValue = 0;

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

    public new int AdjustedBaseValue{
        get{ return BaseValue + BuffValue + _modValue; }
    }

    public void Update(){
        CalculateModValue();
    }
}

public struct ModifyingAttribute{
    public Attribute attribute;
    public float ratio;

    public ModifyingAttribute(Attribute att, float rat){
        attribute = att;
        ratio = rat;
    }
}


B8. Tạo thêm file C Sharp Script trong thư mục Character Classes và đặt tên là Vital.

B9. Double Click vào file C# vừa tạo và chèn đoạn code sau vào:

public class Vital : ModifiedStat {
    private int _curValue;

    public Vital(){
        _curValue = 0;
        ExpToLevel = 50;
        LevelModifier = 1.1f;
    }

    public int CurValue{
        get{
            if(_curValue > AdjustedBaseValue)
                _curValue = AdjustedBaseValue;

            return _curValue;
        }
        set{ _curValue = value; }
    }
}

public enum VitalName{
    Health,
    Energy,
    Mana
}


B10. Tạo thêm file C Sharp Script trong thư mục Character Classes và đặt tên là Skill.

 

B11. Double Click vào file C# vừa tạo và chèn đoạn code sau vào:

 public class Skill : ModifiedStat {
    private bool _known;

    public Skill(){
        _known = false;
        ExpToLevel = 25;
        LevelModifier = 1.1f;
    }

    public bool Known{
        get{ return _known; }
        set{ _known = value; }
    }
}

public enum SkillName{
    Melee_Offence,
    Melee_Defence,
    Ranged_Offence,
    Ranged_Defence,
    Magic_Offence,
    Magic_Defence
}


Chủ Nhật, 1 tháng 12, 2013

AI 1.3 - BẦY, ĐÀN VÀ LŨ (PHẦN I)

  1.3 - Bầy, đàn và lũ ( Phần I )
Nhiều sinh vật sống chẳng hạn như chim, cá, côn trùng, và động vật trên cạn thực hiện nhiều động tác chẳng hạn như di chuyển, săn bắt, và tìm mồi theo bầy đàn. Chúng sống và săn bắt theo bầy đàn, vì điều đó làm cho chúng mạnh hơn và an toàn khỏi đám thú ăn thịt hơn là việc săn mồi riêng biệt. Vì thế, để nói bạn muốn tạo một nhóm những bầy chim trên bầu trời; nó sẽ tiêu tốn quá nhiều thời gian và nỗ lực cho các nhà làm phim để thiết kế ra các chuyển động và những cử động của từng con chim. Nhưng nếu chúng ta đưa vào một vài luật đơn giản cho mỗi con chim nối đuôi nhau, chúng ta có thể đạt đến trí thông minh độc lập của toàn nhóm có hành vi phức tạp, toàn thể.

Người đi đầu của khái niệm này chính là Craig Reynolds, người đã giới thiệu một thuật toán về bầy đàn
trong tờ giấy SIGGRAPH, 1987, Flocks, Herds and School - A Distributed Behavioral Model. Ông ấy đã tạo ra thuật ngữ “boid”, có vẻ như “bird”, nhưng liên quan đến một đối tượng “bird-like”. Ông ấy đã
đề nghị ba luật đơn giản để áp dụng cho mỗi đơn vị, chúng là :

Craig Reynolds
Chia cắt : Luật này được dùng để duy trì một khoảng cách ngắn với những các boid - bầy chim, để tránh đập vào chúng

Thẳng hàng : Luật này được dùng để sắp hàng chính nó với các hướng trung bình của các người hàng xóm của nó, và rồi di chuyển trong cùng tốc độ với chúng như một bầy

Cố kết : Bước này được dùng để duy trì một khoảng cách nhỏ với các trung tâm của nhóm
Ba luật đơn giản này là tất cả những gì chúng ta cần để bổ sung tính thực tế và thói quen bầy đàn phức tạp một cách công bằng của đàn chim. Chúng cũng có thể được áp dụng cho các hành vi nhóm của bất kỳ các hình thể thực thể khác với một ít hoặc không có sự thay đổi. Chúng ta sẽ thực hiện cách làm thế nào để bổ sung hệ thống bầy đàn thế này trong bài AI 5.1 - Bầy đàn.

Tìm đường và hướng theo

Thỉnh thoảng chúng tôi muốn nhân vật AI của mình đi lang thang trong thế giới game, theo một con đường đã định hoàn toàn hoặc phỏng chừng. Chẳng hạn như trong một trò chơi đua xe, các đối thủ AI cần định hướng theo đường. Và những thuật toán quyết định như thuật toán boid bầy đàn đã nói trước đó, chỉ có thể đưa ra các quyết định tốt. Nhưng cuối cùng, tất cả xuống dốc để xử lý các bước di chuyển và những hành vi hướng theo. Các hành vi hướng theo dành cho các nhân vật AI đã được nghiên cứu trong các chủ đề dành cho một cặp nhóm mười. Một tờ giấy có thể ghi chú trong lĩnh vực này chính là Steering Behaviors for Autonomous Characters, dựa theo Craig Reynolds, xuất bản năm 1999 ở Game Developers Conference (GDC). Ông ta đã xếp loại các hành vi hướng theo thành ba lớp theo sau :


Để tôi trích dẫn ví dụ gốc từ bài viết của ông ấy để hiểu ba lớp này :
“Chẳng hạn, việc xét về, vài người cao bồi định tập hợp lại gia súc bên ngoài bãi. Một con bò lang thag khỏi bầy. Người chủ bảo một cao bồi tìm kẻ đi lạc về. Người cao bồi nói “nhanh lên” với ngựa của ông ta, và hướng nó về phía con bò, có thể tránh các chướng ngại dọc theo đường đi. Trong ví dụ này, người chủ biểu diễn hành động chọn lọc, chú ý rằng tình trạng thế giới đã thay đổi (một con bò rời khỏi đàn), và thiết lập một đích đến( tìm lại kẻ đi lạc). Mức độ hướng theo đại diện bởi người cao bồi, đang phân tích đích đến thành một chuỗi các đích đến con đơn giản (tiến đến chỗ con bò, tránh các vật chướng ngại, và tìm lại con bò đi lạc). Một đích đến con tương ứng với một hành vi hướng theo dành cho đôi người cao bồi và con ngựa. Bằng cách dùng các ký hiệu điều khiển khác nhau (những lệnh giọng nói, thúc, và ghì cương), người cao bồi hướng ngựa của mình đến thẳng mục tiêu. Theo thuật ngữ thông thường, những dấu hiệu này biểu thị các khái niệm như đi nhanh hơn, đi chậm lại, quẹo phải, quẹo trái, và tiến lên. Con ngựa thi hành mức độ di chuyển. Nhận các tín hiệu điều khiển của người cao bồi khi input, con ngựa di chuyển theo hướng chỉ ra. Chuyển động là kết quả của một sự tương tác phức tạp của sự nhận thức tri giác của con ngựa, giác quan cân bằng của nó, và các vòng cổ trên cơ của nó để kết hợp vào bộ xương của chúng.”


Rồi ông ấy biểu diễn cách thiết kế và thi hành vài hành vi hướng theo thông thường và đơn giản dành cho cá nhân các nhân vật AI và từng đôi. Những hành vi thế này bao gồm tìm kiếm và bỏ trốn, đuổi theo và tránh, lang thang, đến nơi, tránh chướng ngại, áp tường theo dõi và tìm đường. Chúng ta sẽ bổ sung vài hành vi này trong bài AI 6.1 - Tìm Đường và Các hành vi Hướng theo.

Cookbook - XOAY GÓC NHÌN CAMERA QUANH VẬT THỂ

 
  - Xoay góc nhìn quanh vật thể -

Như các bạn thường thấy ở những trang bán điện thoại online, các mặt hàng được trưng bày sẽ có một số hình 3 chiều để các bạn có thể nhìn thấy toàn diện chiếc điện thoại mà bạn ưng ý nhất. Bài viết này mình sẽ hướng dẫn các bạn tạo ra hiệu ứng xoay góc nhìn của camera một quanh vật thể như chức năng đã trình bày ở trên.

Chuẩn bị
Mobile.unitypackage

Cách để làm

B1. Nhấp phải vào vùng trống trong thẻ Project và chọn Import Package | Custom Package và Import file Mobile vừa tải ở link trên. Double click vào scene Xoay_Camera_Scene.






B2. Tại thẻ Project, nhấp chọn nút Creat | Java Script và đặt tên là InspectCamera.

B3. Double click vào file C# vừa tạo và chèn đoạn code sau vào:

var target : Transform;
var distance = 10.0;

var xSpeed = 250.0;
var ySpeed = 120.0;

var yMinLimit = -20;
var yMaxLimit = 80;

private var x = 0.0;
private var y = 0.0;

@script AddComponentMenu("Camera-Control/Inspect Camera")
var zoomInLimit = 2.0;
var zoomOutLimit = 1.0;
private var initialFOV : float;

function Start () {
    initialFOV = camera.fieldOfView;
    transform.position = new Vector3(0.0f, 0.0f, -distance) + target.position;    
    var angles = transform.eulerAngles;
    x = angles.y;
    y = angles.x;

    // Make the rigid body not change rotation
       if (rigidbody)
        rigidbody.freezeRotation = true;
}

function LateUpdate () {
   if (target && Input.GetMouseButton(0)) {
    if(Input.GetKey(KeyCode.RightShift) || Input.GetKey(KeyCode.LeftShift)){
       var zoom = camera.fieldOfView - Input.GetAxis ("Mouse Y");
      if(zoom >= initialFOV / zoomInLimit && zoom <= initialFOV / zoomOutLimit){
          camera.fieldOfView -= Input.GetAxis ("Mouse Y");
   }        
     } else {
      x += Input.GetAxis("Mouse X") * xSpeed * 0.02;
        y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02;
             }
     y = ClampAngle(y, yMinLimit, yMaxLimit);
     var rotation = Quaternion.Euler(y, x, 0);
     var position = rotation * Vector3(0.0, 0.0, -distance) + target.position;
      transform.rotation = rotation;
      transform.position = position;
    }
}


static function ClampAngle (angle : float, min : float, max : float) {
    if (angle < -360)
        angle += 360;
    if (angle > 360)
        angle -= 360;
    return Mathf.Clamp (angle, min, max);
}

B4. Kéo thả file Java Script này vào Main Camera ở thẻ Project.

B5. Nhấp chọn Main Camera, qua thẻ Inspector, nhấp chuột vào nút tròn cuối dòng Target của InspectCamera (Script) và chọn mobile.



B6. Nhấp nút Play để kiểm tra thành quả. Bạn có thể giữ chuột và kéo ra các hướng để xoay góc nhìn. Nhấn Shift và kéo thả chuột để phóng to / thu nhỏ góc nhìn.