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模型。


2017年10月22日 星期日

Unity程式初體驗(二)-淺談讀取鍵盤輸入與遊戲物件顏色控制

使用Input.GetKeyDown來讀取鍵盤輸入內容

在Unity系統其輸入系統的介面就是Input類別,該類別可以用來讀取鍵盤、滑鼠、甚至於手機上的三軸加速計,在本範例中使用Input.GetKeyDown API來讀取鍵盤的值。因為GetKeyDown為static函式,亦即類別等級函式,因此不需要建立物件,就可以直接呼叫。若函式不是static函式,我們稱為物件等級,就一定要先建立物件才能呼叫使用。

可以用KeyCode來列舉鍵盤值

KeyCode其資料型態是列舉,因此我們可以KeyCode.R來表示"R"鍵。

獲取GameObject的渲染器組件

我們可以使用GetComponent()函式,利用泛型來指定Renderer物件,利用GetComponent()函式來取得遊戲物件的組件。

利用渲染器上的material.color屬性來改變遊戲物件的顏色值

在經由GetComponent()函式獲取GameObject的渲染器組件後,我們就可以利用渲染器上的material.color屬性來改變遊戲物件的顏色值,我們可以用Color.red來指定紅色值。


以下是完整的程式列表:

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

public class NewBehaviourScript : MonoBehaviour {

// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
        if (Input.GetKeyDown(KeyCode.R))
        {
            GetComponent<Renderer>().material.color = Color.red;
        }
        if (Input.GetKeyDown(KeyCode.G))
        {
            GetComponent<Renderer>().material.color = Color.green;
        }
        if (Input.GetKeyDown(KeyCode.B))
        {
            GetComponent<Renderer>().material.color = Color.blue;
        }
    }
}

以上內容參考Scripts as Behaviour Components教學。

2017年10月12日 星期四

Decoding 6 servo channel inputs with an Arduino UNO

最近學生在玩 RC 遙控,本來這是用在四軸飛行器的接收器,拿下來當遙控車使用。

原本以為 Arduino 的 SDK 都寫得不錯,所有的功能都能輕易完成。
PS. 事實上也是這樣。很快就完成了。
可是,我們實際上線使用時,發現控制動作會有嚴重的延遲。經過反覆的分析測試,發現 Arduino 的 pulsein 函數在其內部會有 delay 的動作,造成了當我們的 signal input 有多腳時,就會造成控制動作的延遲。套一句敏哥上課說的話,這不符合 real time 的要求。所以,我們去找了幾個方法。最後,找到這個作者使用中斷計時法來解決 6 channel 的輸入計時。

http://ceptimus.co.uk/?p=66

特地記錄下來,讓開發 RC 控制的同好,可以更容易進行開發。

2017年10月9日 星期一

Unity程式設計初體驗(一)-從MonoBehaviour類別開始

好玩的程式設計

程式設計是一門很好玩的學科,可以訓練邏輯概念,透過指令的排列組合,完成某一個特定任務,最方便是電腦已經成為每個家庭必供的家電用品,對於有些家庭成員來說,花在電腦上的時間可能都比看電視時間還長。學習程式設計自學很重要也很方便,因為只要一部電腦即可,在現在以工業4.0或社會5.0都很重視程式設計,學習程式設計可以指揮機器人的運作,也可發展網路服務軟體,既好玩又有趣。

Unity是學習程式設計非常實作的平台

為什麼我們學習Unity?Unity應用在哪些領域,前景又如何?目前許多知名的遊戲都是使用Unity開發,更重要的是Unity具有很強的跨平台能力,不管是桌機、手機、遊戲機都有支持。Unity具有很好優勢和前景,特別是在3D/AR/VR上。Unity程式語言可以使用C#或JavaScript,目前以C#為主流。

學習Unity程式設計就從MonoBehaviour類別開始

MonoBehaviour是基礎的類別,這個類別有兩個很重要的函式Start()和Update(),Start()只會執行一次,負責初始化的任務,但Update()就會不斷地被呼叫執行,是程式的主函式。以下是我們建立一個新的C#程式的樣版,類別名稱是NewBehaviourScript,繼承MonoBehaviour類別。

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


public class NewBehaviourScript : MonoBehaviour {


   // Use this for initialization
   void Start () {

  }

   // Update is called once per frame
   void Update () {

  }
}

GameObject是遊戲世界的基本物件

GameObject我們稱遊戲物件,它是Unity場景中所有實例的基本的類別。首先我們先介紹Find()函式,該函式可以用來找遊戲物件,例如:要找一個叫Cube的物件,我們可以使用GameObject.Find("Cube");,當找到這個物件時,會傳回GameObject物件指標,因此我們需要先宣告一個GmaeObject物件,private GameObject cube;採用private是表示該物件是私有的,只能在該類別內使用。找到可以控制的物件後,我們就可以使用GameObject內的translation變數來進行對遊戲物件的轉換控制。translation變數其資料型態是Translation變數,它可以用來控制遊戲物件的位置(Position)、旋轉(Rotation)、大小(Scale)等。首先我們先來學習如何控制遊戲物件的旋轉方向,我們可以呼叫Rotate()函式,三個參數分別是X、Y、Z,我們來控制Y軸的旋量,採用時間物件Time.deltaTime。其完整的程式如下:

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

public class NewBehaviourScript : MonoBehaviour {

    private GameObject cube;

    // Use this for initialization
    void Start () {
       cube = GameObject.Find("Cube");

    }

// Update is called once per frame
void Update () {
        cube.transform.Rotate(0, 100 * Time.deltaTime, 0);
    }
}

使用Prefab預製功能,讓控制更輕鬆

Unity中的Prefab資源類型我們稱它為預製,它可以透過添加組件並將其屬性設置為適當的值,可以方便地在場景中構建一個遊戲物件(GameObject),那兩者之間有何差別呢?Prefab資源類型可以讓您存儲一個完整的組件和屬性的GameObject對象。 預製作為模板,您可以在該模板中在場景中創建新的對象實例。 對預製資產進行的任何編輯都會立即反映在從其生成的所有實例中,但也可以單獨覆蓋每個實例的組件和設置。
我們可以通過選擇Asset->Create Prefab來建立預製,然後再將對象從場景拖動到“空”預製資產上。換句話,場景上的所有物件,都是 GameObject物件,Prefab就是把場景上,製作好的 GameObject 拖曳到 Assets 目錄下保存起來。在使用預製功能要注意,在前面的例子,我們使用到Find()找特定的物件,因此您使用預製功能時,把原本的物件變成三個,一個Cube,其兩個是使用預製Prefab來製造出的兩個Cube,您會發現在轉動的Cube只有一個,要三個都能使用,可以改成下列程式,改用this物件指標。

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

public class NewBehaviourScript : MonoBehaviour {


    // Use this for initialization
    void Start () {

    }

// Update is called once per frame
void Update () {
        this.transform.Rotate(0, 100 * Time.deltaTime, 0);
    }
}