2017年11月20日 星期一

Unity程式設計初體驗(九)-3D文字的程式設計

回應同學的問題:Unity的文字物件,是否能像一般物件一樣可以使其旋轉或是放大縮小等...動畫效果?

Unity程式設計初體驗(三)-遊戲物件位移和旋轉控制文章中,有說明如何控制物件的前進、後退、以及向左或向右旋轉,本文以3D文字物件為例,新增文字縮放功能,如下圖:


其步驟如下:

  1. 在功能表上選擇File->New Project功能選項,出現對話框,輸入專案名稱後,再按Create Project按鈕,即可以建立新專案。
  2. 選擇Game Object->3D Object->3D Text就可以建立3D文字物件。
  3. 在右下角Project專案視窗中,在Assets資料夾上按下右鍵,選擇Create->C# Script建立新的C#腳本檔案,然後使改檔名成為TextControl 。
  4. 原始程式如下(黃底是表示新增的程式):


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

public class TextControl : MonoBehaviour {
    public float moveSpeed = 10f;
    public float turnSpeed = 50f;
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
    if (Input.GetKey(KeyCode.UpArrow))
        transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);

    if (Input.GetKey(KeyCode.DownArrow))
        transform.Translate(-Vector3.forward * moveSpeed * Time.deltaTime);

    if (Input.GetKey(KeyCode.LeftArrow))
        transform.Rotate(Vector3.up, -turnSpeed * Time.deltaTime);

    if (Input.GetKey(KeyCode.RightArrow))
        transform.Rotate(Vector3.up, turnSpeed * Time.deltaTime);

    if (Input.GetKey(KeyCode.B))
            transform.localScale += new Vector3(1.01f * Time.deltaTime, 1.01f * Time.deltaTime, 1.01f * Time.deltaTime);

    if (Input.GetKey(KeyCode.S))

            transform.localScale -= new Vector3(1.01f * Time.deltaTime, 1.01f * Time.deltaTime, 1.01f * Time.deltaTime);
    }
}
上面黃底粗體字的部份,指依照Unity程式設計初體驗(三)-遊戲物件位移和旋轉控制文章後,新增的程式碼。從上面程式要轉換座標,我們可以用Translate()函式,要旋轉改變方向可以用Rotate()函式,要改變大小可以用localScale屬性

Unity程式設計初體驗(八)-操縱人物模型的程式設計II

前一篇文章Unity程式設計初體驗(七)-操縱人物模型的程式設計中,左上角有原來程式提供的2D選項表單,這次我們試著把它拿掉,並設計一個全新的程式來操縱人物模型。
步驟如下:

1.撰寫下列程式(PlayZombie.cs)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayZombie : MonoBehaviour {
    public Transform[] transforms;

    private Animator[] animator;

    private string currentState = "";

    private int state = 0;
    private int timecnt = 100;

    // Use this for initialization
    void Start () {
        animator = new Animator[transforms.Length];
        for (int i = 0; i < transforms.Length; i++)
        {
            animator[i] = transforms[i].GetComponent();
        }
        for (int j = 0; j < animator.Length; j++)
        {
            //animator[j].SetBool("idle0ToIdle0", false);
            animator[j].SetBool("idle0ToIdle1", false);
            animator[j].SetBool("idle0ToWalk", false);
            animator[j].SetBool("idle0ToRun", false);
            animator[j].SetBool("idle0ToWound", false);
            //animator[j].SetBool("idle0ToSkill1", false);
            animator[j].SetBool("idle0ToSkill0", false);
            animator[j].SetBool("idle0ToAttack1", false);
            animator[j].SetBool("idle0ToAttack0", false);
            animator[j].SetBool("idle0ToDeath", false);
        }
    }

// Update is called once per frame
void Update () {
        AnimatorStateInfo stateInfo = animator[0].GetCurrentAnimatorStateInfo(0);

        if (!stateInfo.IsName("Base Layer.idle0"))
        {
            for (int j = 0; j < animator.Length; j++)
            {
                //animator[j].SetBool("idle0ToIdle0", false);
                animator[j].SetBool("idle0ToIdle1", false);
                animator[j].SetBool("idle0ToWalk", false);
                animator[j].SetBool("idle0ToRun", false);
                animator[j].SetBool("idle0ToWound", false);
                //animator[j].SetBool("idle0ToSkill1", false);
                animator[j].SetBool("idle0ToSkill0", false);
                animator[j].SetBool("idle0ToAttack1", false);
                animator[j].SetBool("idle0ToAttack0", false);
                animator[j].SetBool("idle0ToDeath", false);
            }
        }
        else
        {
            for (int j = 0; j < animator.Length; j++)
            {
                animator[j].SetBool("walkToIdle0", false);
                animator[j].SetBool("runToIdle0", false);
                animator[j].SetBool("deathToIdle0", false);
            }
        }
        timecnt = timecnt - 1;
        if (timecnt <0 p="">        {
            timecnt = 0;
            switch (state)
            {
                case 0:
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("idle0ToAttack0", false);
                        animator[j].SetBool("idle0ToRun", false);
                    }
                    Debug.Log("state 0");
                    state = 1;
                    timecnt = 100;
                    break;

                case 1:
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("idle0ToAttack1", true);
                    }
                    Debug.Log("state 1");
                    state = 2;
                    timecnt = 300;
                    break;
                case 2:
                    for (int j = 0; j < animator.Length; j++)
                    {
                         animator[j].SetBool("idle0ToAttack0", true);
                    }
                    Debug.Log("state 2");
                    state = 0;
                    timecnt = 500;
                    break;
                default:

                    break;
            }
        }
    }
}

2. 依照下圖設定動畫物件


3.把步驟1的程式碼拖移到Camera,設定Camera中PlayZombie(Script)中Transform的屬性。

2017年11月19日 星期日

Unity程式設計初體驗(七)-操縱人物模型的程式設計

在前一章我們介紹Unity程式初體驗(六)-Unity的動畫系統的文章,瞭解到Unity動畫系統中有四個主要部份,分別是:動畫夾(Animation clips)、動畫控制器(Animator Controller)、操縱人物模型(Rigged Character Model)、動畫組件(Animator Component)。今天我們來試試看如何操縱人物模型。



我們設計的程式原始如下:
using UnityEngine;
using System.Collections;

public class PlayGUI : MonoBehaviour
{
    public Transform[] transforms;

    public GUIContent[] GUIContents;

    private Animator[] animator;

    private string currentState = "";

    private int state = 0;
    private int timecnt = 0;

    // Use this for initialization
    void Start()
    {
        animator = new Animator[transforms.Length];
        for (int i = 0; i < transforms.Length; i++)
        {
            animator[i] = transforms[i].GetComponent();
        }
    }

    void OnGUI()
    {
        GUILayout.BeginVertical("box");
        for (int i = 0; i < GUIContents.Length; i++)
        {

            if (GUILayout.Button(GUIContents[i]))
            {
                currentState = GUIContents[i].text;
            }

            AnimatorStateInfo stateInfo = animator[0].GetCurrentAnimatorStateInfo(0);

            if (!stateInfo.IsName("Base Layer.idle0"))
            {
                for (int j = 0; j < animator.Length; j++)
                {
                    //animator[j].SetBool("idle0ToIdle0", false);
                    animator[j].SetBool("idle0ToIdle1", false);
                    animator[j].SetBool("idle0ToWalk", false);
                    animator[j].SetBool("idle0ToRun", false);
                    animator[j].SetBool("idle0ToWound", false);
                    //animator[j].SetBool("idle0ToSkill1", false);
                    animator[j].SetBool("idle0ToSkill0", false);
                    animator[j].SetBool("idle0ToAttack1", false);
                    animator[j].SetBool("idle0ToAttack0", false);
                    animator[j].SetBool("idle0ToDeath", false);
                }
            }
            else
            {
                for (int j = 0; j < animator.Length; j++)
                {
                    animator[j].SetBool("walkToIdle0", false);
                    animator[j].SetBool("runToIdle0", false);
                    animator[j].SetBool("deathToIdle0", false);
                }
            }

            if (currentState != "")
            {
                if (stateInfo.IsName("Base Layer.walk") && currentState != "walk")
                {
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("walkToIdle0", true);
                    }
                }

                if (stateInfo.IsName("Base Layer.run") && currentState != "run")
                {
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("runToIdle0", true);
                    }
                }

                if (stateInfo.IsName("Base Layer.death") && currentState != "death")
                {
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("deathToIdle0", true);
                    }
                }

                switch (currentState)
                {
                    /*
        case "idle0":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToIdle0", true);
}
break;
*/
                    case "idle1":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToIdle1", true);
                        }
                        /*
                        break;
                    case "idle0":

                        break;
                    case "walk":
                        for (int j = 0; j < animator.Length; j++) {
                            animator[j].SetBool("idle0ToWalk", true);
                        }
                        */
                        break;
                    case "run":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToRun", true);
                        }
                        break;
                    case "attack0":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToAttack0", true);
                        }
                        break;
                    case "attack1":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToAttack1", true);
                        }
                        break;
                    case "skill0":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToSkill0", true);
                        }
                        break;
                        /*
                    case "skill1":
                        for (int j = 0; j < animator.Length; j++) {
                            animator[j].SetBool("idle0ToSkill1", true);
                        }
                     
                        break;*/
                    case "wound":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToWound", true);
                        }
                        break;
                    case "death":
                        for (int j = 0; j < animator.Length; j++)
                        {
                            animator[j].SetBool("idle0ToDeath", true);
                        }
                        break;

                    default:
                        break;
                }
                currentState = "";
            }
        }
        GUILayout.EndVertical();
    }

    void Update()
    {
        timecnt = timecnt + 1;
        if (timecnt > 50) {
            timecnt = 0;
        switch (state)
            {
                case 0:
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("idle0ToAttack0", true);
                    }
                    Debug.Log("state 0");
                    state = 1;
                    break;

                case 1:
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("Attack0Toidle0", true);
                    }
                    Debug.Log("state 1");
                    state = 2;
                    break;
                case 2:
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("idle0ToAttack1", true);
                    }
                    Debug.Log("state 2");
                    state = 3;
                    break;
                case 3:
                    for (int j = 0; j < animator.Length; j++)
                    {
                        animator[j].SetBool("idle0ToRun", true);
                    }
                    Debug.Log("state 3");
                    state = 0;
                    break;
                default:

                    break;
            }
        }
     
    }
}

上面程式黃底文字是我們增加的程式,其中有兩個變數,一個是state來控制動畫運作的狀態值,另一個timecnt是用來延遲動畫切換的狀態值,讓動畫可以按照我們想要的動作來運作。為了要讓我很清楚地瞭解程式的運作,我們在程式中增加Debug.Log的程式碼。

2017年11月18日 星期六

Unity程式設計初體驗(六)-Unity的動畫系統

在前一篇文章Unity程式初體驗(五)-動畫簡易控制程式解析,讓我們看到Unity動畫程式設計是以狀態圖的方式來進行動作的切換,讓我們好奇到底Unity的動畫系統是長什麼樣子。我們可以從Unity官方文件中,可以看到有一篇文章-
Animation System Overview,在該篇文章中,有一個系統架構圖如下圖。

上圖約列可以區分成4個部份:
(1) 動畫夾(Animation clips):儲存許多動畫的片斷動作。
(2) 動畫控制器(Animator Controller):動畫切換的狀態圖,可以用Animator視窗來顥示。
(3) 操縱人物模型(Rigged Character Model):具有骨架的特定配置,其被映射到Unity的通用阿凡達格式(Avatar format)。
(4) 動畫組件(Animator Component):在Inspector視窗中可以看動畫組件同時擁有Animator控制器和操縱人物模型。

接下來前一篇文章所使用到的範例,來看它的動畫系統中,最重要的4個部份。
(1) 動畫夾(Animation clips):

(2) 動畫控制器(Animator Controller):

(3) 操縱人物模型(Rigged Character Model):


(4) 動畫組件(Animator Component):




2017年11月17日 星期五

Unity程式設計初體驗(五)-動畫簡易控制程式解析


在Asset Store 下載Zombie 0_1,找到之後把它滙入(import),並選擇在Scenes資料夾中的Demo的場景範例,點後後可以選定Main Camera物件,在右邊的Inspector可以檢視Main Camera物件的相關內容,在下方有一個PlayGUI(Script)的視窗,內部儲存腳本的變數,有一個是Trnsforms,另一個是GUI Contents,如下圖。

回到專案的目錄,在Asset子目錄下,可以找到PlayGUI.cs的檔案,打開來其程式碼如下圖(僅摘錄部份內容):

public class PlayGUI : MonoBehaviour {
public Transform[] transforms;

public GUIContent[] GUIContents;

private Animator[] animator;

private string currentState = "";
:
:
:
}
上圖程式中可以看到public和private兩個關鍵詞,分別表示變數成員是"公開的"表示可以由外部程式來存取和"私有的"表示只能自己使用。因此在Unity的介面中,可以看到Transforms和GUI Contents表示Transform和GUIContent都是以陣列方式呈現,如下圖。


在PlayGUI 類別中,定義一個Start()函式,這是一個初始化的函式,在這個函式中,會讀取上圖中Trnsforms的內容,利用把它放置GetComponent()函式來取得元件的動畫資訊,存放到animator的變數中。在這裏transforms.Length會等於1,如上圖所示,只有一個元件就是Element 0,內容就是Zombie_0_1 (Transform),也就是畫面中的動畫。

// Use this for initialization
void Start () {
    animator = new Animator[transforms.Length];
for (int i = 0; i < transforms.Length; i++) {
animator[i] = transforms[i].GetComponent();
}
}

2017年11月16日 星期四

Unity程式設計初體驗(四)-下載動畫遊戲物件及簡易控制

Unity提供商城,供設計師選購遊戲元件,有許多的遊戲元件都是免費的,本文將介紹如何下載,以及簡易的使用方式。



大家可以查看下列動畫就可以得知Zombie 0_1的功能


動畫流程狀態圖


從上圖我們可以很清楚地看到,動畫轉換的狀態圖,動畫狀態的起始點是idle0,經由idle0可以到其他地方如skill0, wound, run等,除了Exit狀態外,idle0可以到達任何狀態。我們可以在Script目錄中找到PlayGUI.cs,在這個檔案內您可以找到switch關鍵詞,switch代表著可以多選一,我們可以經由現在狀況(currentState)的變數,來決定要切到那一狀態,例如:"run", attack0等。animator[j].SetBool("idle0ToIdle1", true);我們可以經由動畫陣列中SetBool來設定動畫的狀態值,以此例就是要從idle0切換到idle1。

switch (currentState) {

 case "idle1":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToIdle1", true);
}

break;
case "run":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToRun", true);
}
break;
case "attack0":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToAttack0", true);
}
break;
case "attack1":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToAttack1", true);
}
break;
case "skill0":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToSkill0", true);
}
break;

case "wound":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToWound", true);
}
break;
case "death":
for (int j = 0; j < animator.Length; j++) {
animator[j].SetBool("idle0ToDeath", true);
}
break;

default:
break;
}
currentState = "";
}

2017年10月23日 星期一

Unity程式設計初體驗(三)-遊戲物件位移和旋轉控制

有關於鍵盤的控制請參考前一篇文章淺談讀取鍵盤輸入與遊戲物件顏色控制


鍵盤的上下左右控制

我們可以使用Input.GetKey()函式來讀取按下鍵盤的鍵值,下列程式分別用來讀取上下左右鍵:
  • Input.GetKey(KeyCode.UpArrow)表示是按下向上鍵。
  • Input.GetKey(KeyCode.DownArrow)表示是按下向下鍵。
  • Input.GetKey(KeyCode.LeftArrow)表示是按下向左鍵。
  • Input.GetKey(KeyCode.RightArrow)表示是按下向右鍵。

3D向量資料型態Vector3

  • Vecto.forward 表示 Vector3(0, 0, 1

  • Vecto.back表示 Vector3(0, 0, -1)
  • Vector.up 表示Vector3(0, 1, 0)
  • Vector.down 表示Vector3(0, -1, 0)
  • Vector.left 表示Vector3(-1, 0, 0)
  • Vector.right 表示Vector3(1, 0, 0)
以下是完整的程式的列表

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

public class NewBehaviourScript : MonoBehaviour {
    public float moveSpeed = 10f;
    public float turnSpeed = 50f;
    // Use this for initialization
    void Start () {

}

// Update is called once per frame
void Update () {
        if (Input.GetKey(KeyCode.UpArrow))
            transform.Translate(Vector3.forward * moveSpeed * Time.deltaTime);

        if (Input.GetKey(KeyCode.DownArrow))
            transform.Translate(-Vector3.forward * moveSpeed * Time.deltaTime);

        if (Input.GetKey(KeyCode.LeftArrow))
            transform.Rotate(Vector3.up, -turnSpeed * Time.deltaTime);

        if (Input.GetKey(KeyCode.RightArrow))
            transform.Rotate(Vector3.up, turnSpeed * Time.deltaTime);
    }
}
以上內容參考Translate and Rotate教學。

您可以試著到Unity的商店下載一個模型,然後把上面的程式放到該3D模型上,您會發現原來這麼簡單就能控制你3D模型。