做自己的Pokemon Go:Unity Location-Based Game遊戲製作教學(II)

【寫在前面】本教學是承接「做自己的Pokemon Go:Unity Location-Based Game遊戲製作教學(I)」的進階部分,完成前一個教學可以了解如何透過GPS定位並觸發簡單事件,這份教學將把觸發事件延伸到基於陀螺儀的AR(擴增實境)效果,最終就像Pokemon Go那樣,可以在戶外走到特定地點後,可以遇到虛擬角色出現在真實環境中。

再次強調,感謝強者學弟 Yi-Wei 協助課程設計

軟體版本:Unity 5.5.0f3  MapNav1.3.4 NGUI2.5a GoogleVRForUnity (如要發佈製手機Android SDK/JDK請自行安裝好)

update:2017/1/18 補充步驟2-1與2-2

p.s:本系列教學所有需要License的外掛/素材/工具小樽皆有購買合法官方授權,請大家支持正版唷,啾咪

1.開啟教學(I)完成的進度,我們希望使用者觸發地圖事件後,按下”點擊畫面開始戰鬥”後會進入基於陀螺儀的AR顯示模式,因此要讓點擊畫面的事件可以被傳遞,需要一個偵測”點擊畫面”觸發的事件,請雙擊2D_Pointer屬性中我們寫好的Player.cs開啟程式編輯器,找到 public class Player : MonoBehaviour { 這行程式,在他下方添加程式碼 bool CanFight;,如下,這是宣告一個布林(bool)的值命名為CanFight,會用來裝填判斷螢幕是否被觸碰的狀態,布林值得表現有true (成立/是/1) 和 false (不成立/否/0),是事件判斷的常用變數

[code lang=”csharp”]
public class Player : MonoBehaviour {
bool CanFight; //宣告布林變數命名為CanFight
[/code]

2.接著找到 void Update () { 這行,在其下方填入下面程式碼,這是一個 if 判別式,判別成立是當按下的事件,顯示Degug文字”開始戰鬥”,條件if()內就是把判斷 Input.GetMouseButtonDown(0)這個事件=電腦上滑鼠左鍵輸入=手機螢幕上點一下是否成立的結果,寫入我們步驟一宣告的CanFight這個布林值,如果事件成立(true),Debug.Log是Unity用來顯示Debug的log內容,()內可以放字串,但是文字字串要用雙引號””夾起來,否則會被視為數值,變成一串亂碼。

[code lang=”csharp”]
void Update(){
if(CanFight && Input.GetMouseButtonDown(0)){
Debug.Log("開始戰鬥");
}
[/code]

2-1.找到 void OnTriggerStay(Collider other){ 下的 if(other.tag == “Dot”){ 這行條件,在下面加入

[code lang=”csharp”]
CanFight = true;
[/code]

找到  void OnTriggerExit(Collider other){ 下的 if(other.tag == “Dot”){ 這行條件,在下面加入

[code lang=”csharp”]
CanFight = false;
[/code]

2-2.完成上述的修改後,Player.cs 程式碼應該呈現如下:

[code lang=”csharp”]
using UnityEngine;
using System.Collections;

public class Player : MonoBehaviour {
bool CanFight;
// Use this for initialization
void Start () {

}

// Update is called once per frame
void Update () {
if(CanFight && Input.GetMouseButtonDown(0)){
Debug.Log("開始戰鬥");
}
}

void OnTriggerStay(Collider other){
if(other.tag == "Dot"){
other.gameObject.transform.GetChild(0).GetComponent<Renderer>().material.color = Color.green;
other.gameObject.transform.GetChild(1).gameObject.SetActive(true);
CanFight = true;
}
}

void OnTriggerExit(Collider other){
if(other.tag == "Dot"){
other.gameObject.transform.GetChild(0).GetComponent<Renderer>().material.color = Color.white;
other.gameObject.transform.GetChild(1).gameObject.SetActive(false);
CanFight = false;
}
}

}

[/code]

3.完成以上步驟後,儲存Script的修改執行看看,只要步驟正確,當滑鼠移動接觸到驚嘆號後,點一下滑鼠左鍵(同等於在手機螢幕上點一下的意思),左下角Log會出現”開始戰鬥”

4.接著我們會用到兩個新的外掛,先匯入NGUI Next-Gen UI2.5a.unitypackage,最新版本的 NGUI 也可以使用,但是因為對比這份教學撰寫時的版本簡化了一些步驟,並且設定上有些不同,有把握的同學/朋友可以直接用新版操作,如果完全初心者請先使用小樽提供的課程素材,如果不是小樽課程的學生~就請使用你們的根性克服這個問題 XD,匯入NGUI的時候如果遇到下面的畫面可以直接按 I Made a Backup. Go Ahead讓他自動更新。

4.匯入GoogleVR的外掛時如果出現下面的畫面,表示他需要補充一些額外的libraries,請按Import Package匯入。

5.從工具列 NGUI > Open the UI Wizard / Create your UI

6.將Hierachy 中剛剛產生的 UI Root (2D) 改名為 Cam 用來製作AR實景背景的攝影機,如下圖紅框處

7.點選Cam最底下的Panel,並在其屬性視窗執行Add Component > ( NGUI > UI > Stretch),並設置如下圖的參數,設定完以後會發現場景上的是窗框會隨著畫面做自適應縮放。

8.在Panel底下新增一個空物件 (Create Empty) 命名為CamObj ,並在其屬性視窗執行Add Component > ( NGUI > UI > Texture)

9.在CamObj的屬性下方,Add Component > New Script,建立一個新的Script,命名為Cam,語言是C sharp(C#),找到 void Start () { 這一行,在其下方輸入以下程式碼,這個步驟的用意是透過 WebCamTexture 這個指令設定一個命名為 c 的視訊攝影機材質 (可以撥放視訊攝影機內容的材質),接著把遊戲物件本身的材質替換成 c 這個視訊攝影機材質,接著就撥放c,結果上就會在Panel的下方出現視訊攝影機的即時影像,在手機上就是開啟相機鏡頭,並把即時的影像變成材質貼在這個位置,作為我們AR效果裡面實景背景的素材。

[code lang=”csharp”]
WebCamTexture c = new WebCamTexture (); //設定視訊攝影鏡頭材質
gameObject.GetComponent<UITexture> ().mainTexture = c; //替換掉物件本身的材質
c.Play (); //開始撥放攝影機
[/code]

10.調整 CamObj 的 Transform中的S(scale)縮放屬性的XY值,可以配合手動的方式把這個遊戲物件的面積(綠色邊框),拉的跟Pnael呈現出來的紫色框一樣大(超過一點點也沒關係),這是為了確保我們的攝影機鏡頭 (CamObj) 的顯示畫面是佔滿背景並且會隨著視窗(Panel)大小縮放。

11.選取Cam,新增一個新Layer命名為Cam,並套用之 ( 套用時選 Yes, Change Children = 包含套用到底下所有物件)

12.設定Cam底下Camera的屬性,Culling Mask (顯示遮罩) 選擇我們剛剛建立的Layer Cam,其他不勾選(不顯示),Depth(深度) 設定為-3

13.到上述的步驟完成,場景上共會有兩台攝影機,分別屬於 GoogleMap 和 Cam 兩組物件群組,它們各自有自己的Layer,並且Camera的數值以-1和-3區隔開,就是為了讓攝影機階層有上下之分,數值當然不一定要是-1和-3,只要確保兩台攝影機的上下關係,最上層的數字越大,反之越小即可,下面開始要利用 GoogleVR 的外掛來達成基於陀螺儀的鏡頭動態效果。

14.接著在Hierachy中再新增一個空物件(Create Empty),命名為ModelShow,我們要用他來顯示用來疊加在實景上的虛擬影像,完成後請在其下方建立一個攝影機(滑鼠右鍵>camera,或者從工具列GameObject>camera),命名為 HeadCamera,新增Layer命名為ModelShow,並且套用給它,並在屬性的地方設定Clear Flags為Depth only,Culling Mask 設定為 ModelShow (Layer要先建立好才看的到),並且把Depth改成-2,也就是介於底部的攝影機(-3)與地圖的攝影機(-1)之間,設定如下:

15.從 Assets\GoogleVR\Scripts 裡面找到 GvrViewer,把它拖曳到HeadCamera的屬性底下,並且把 VR mode Enabled 的選項關閉,這個步驟是利用Google VR寫好的頭戴顯示器的code,來完成基於陀螺儀的攝影機追蹤動作,啟用VR mode Enabled會開啟雙眼模式,在本範例中先不使用,如果做出心得的同學往後想利用cardboard來呈現的話可以使用。

16.接下來要匯入模型素材,修課同學請從小樽提供的練習素材中匯入 Model.unitypackage ,這是小樽付費購買的模型,僅供修課同學練習用,其他朋友可以自行建模或尋找其他來源模型,要注意的是如果之後有打算發佈到手機,盡可能避免使用高面數或素材解析度很大的模型,因為手機不一定撐得住。如果使用小樽提供的模型,匯入後從Assets\Suriyun\Teruteru\Prefab挑一隻喜歡的模型,拖曳到 ModelShow 底下,本案例挑選了小惡魔TeruteruB,請記得把他的Layer也設定到ModelShow,並且手動調整它的位置,讓他可以預設出現在攝影機前方 (當然…不是硬性規定,只是方便我們製作的時候觀察) ,另外模型匯入的時候預設方向是背對攝影機,可以把Rotation Y設定為180讓他面相螢幕,如下圖

17.選擇 HeadCamera,在下方點選 Add Component > New Script ,建立一個命名為Around的c sharp(c#) Script,找到 public class Around : MonoBehaviour { 這行,在其下方輸入以下程式碼,這是宣告兩個全域遊戲物件,命名為model和Pos。

[code lang=”csharp”]
public GameObject Model,Pos;
[/code]

18.找到 void Update () { 這行,在其下方輸入以下程式碼,這段程式的意義是讓被宣告為model的遊戲物件以Pos為中心順時針旋轉,後面的 15*Time.delatTime 則控制這個動作的速度,同學也可以修改數值來改變旋轉移動的速度。

[code lang=”csharp”]
Model.transform.RotateAround(Pos.transform.position,Vector3.up,15*Time.deltaTime);
[/code]

18.回到HeadCamera的屬性視窗,由於我們剛剛寫了Script宣告了兩個全域的遊戲物件,Around的Script屬性中就出現了對應的Model和Pos兩個欄位,請用滑鼠左鍵按住並拖曳 Pos 到Model 欄位,並將 TurteruB 拖曳到 Model 欄位。這樣一來就會變成小惡魔的模型 (TurteruB) 會繞著自己的母物件 ModelShow 順時針旋轉。

  1. 接著我們希望在進入AR模式後,可以有按鈕返回地圖模式,以免一去不回,請匯入素材包裡面的 Back.png 到 Assets 中,也可以自行建立素材,匯入 Assets 後記得將 Texture Type屬性改成 Sprite 按下Apply轉成2D遊戲物件,並且拖拉到 Hierachy 中的 Cam > Panel 底下,把Layer選擇到Cam,這樣這個按鈕會依附在視訊攝影機的畫面上方。

20.在Back物件選取的狀態下,於其屬性視窗下方點Add Component < New Script 建立一個名為 Back 的C Sahrp,雙擊左鍵打開程式編輯器,找到  public class Back : MonoBehaviour { 這行,下面輸入以下程式碼,這是用來宣告現在我們舞台上的三大群組:地圖畫面 / 視訊攝影機(AR背景) / AR虛擬腳色。

[code lang=”csharp”]
public GameObject GoogleMap,Cam,ModelShow;
[/code]

  1. 找到  void Update () { }這段程式碼,在其結束符號 } 下方加入以下程式碼,這是用來宣告一個函式 (function),切換三個群組的啟用狀態,當這個函式被呼叫時,會執行顯示 地圖畫面,關閉 視訊攝影機(AR背景) + AR虛擬腳色,意義上就是退回我們初始的地圖狀態。

[code lang=”csharp”]
//function名稱可以自訂
void ClickToBack(){
GoogleMap.SetActive(true);
Cam.SetActive(false);
ModelShow.SetActive(false);
}
[/code]

22.回到Back的屬性介面,把剛剛宣告的三個物件個別填入對應的群組物件如下:

23.繼續在Back下方點選 Add Component > NGUI > Interaction 選擇 Button Message,並且在其Target處拖曳入Back自己本身,在Function Name的地方填入剛剛我們在Back Script裡面宣告的函示名稱,在本教學中命名成ClickToBack (注意大小寫要一致),如下圖

24.接著再按Add Component > Physics > Box Collider 建立一個碰撞器,把Is Trigger打勾,用來產生按鈕的碰撞事件

25.選擇Cam底下的Camera,到UI Camera的Script屬性區,把Event Receiver Mask選程只有Cam,確保碰撞觸發的階層是在 Cam 這個Layer。

  1. 回到 Hierachy 中 GoogleMap 群組物件中的 2D_Pointer,在屬性視窗中找到Player (Script),雙擊Player開啟程式編輯器,找到 public class Player : MonoBehaviour { 這行,在下方加入下面這行程式,宣告三個群組元件。

[code lang=”csharp”]

public GameObject GoogleMap,Cam,ModelShow;

[/code]

27.接著找到我們原本寫的 Debug.Log(“開始戰鬥”); 這行,在下方加入三個初始化的程式碼如下:

[code lang=”csharp”]
GoogleMap.SetActive(false);
Cam.SetActive(true);
ModelShow.SetActive(true);
[/code]

28.回到2D_Pointer的屬性視窗,由於我們剛剛再Player.cs中宣告了三個全域的遊戲物件,因此請記得把Hierachy中對應的這三個群組物件拖曳到對應的欄位,如下。

29.最後,請把Cam和ModelShow兩個群組物件設定為關閉,讓這兩個物件一開始是不啟動的,只有當程式呼叫時才會出現,只要選擇物件,從屬性視窗左上角,找到物件名稱欄位,其左邊的框框就是物件的啟動狀態,勾勾取消表示不啟動,並且該物件名稱再Hierachy中文字會變成淺灰色,如下圖

只要上面的步驟都確實完成,恭喜你~完成了最基本款的單點Unity Location-Based Game遊戲製作,按下執行 (請確定有網路),當地圖loading完以後就會出現在畫面上,並且你可以用方向鍵或wasd移動自己的座標,當接觸到驚嘆號時,驚嘆號會從紅色變成綠色,此時只要點擊畫面,就會進入AR模式,攝影機會啟動變成背景,你的虛擬腳色就會繞著你飛來飛去,而你可以模擬手機轉動 (按住鍵盤的alt移動滑鼠) 看到畫面的變化,而當你發佈到手機上玩的時候~就跟Pokemon Go的捕捉畫面啟動AR模式的情境一模一樣啦~好玩吧 >_0

本教學中所有程式碼小樽都放在GitHub上,需要請自取 https://github.com/skyhunter/LocationBasedGame

===我是分隔線===

後記:如果想要改變互動的模式,可以先試著練習增加地圖上的互動點,挑戰完成後,再嘗試於AR模式中增加互動的模式,因為程式的寫法有太多可能,小樽本教學只是提出一個盡可能讓所有背景的學生/朋友都能在有限時間內做出一定成果的案例,如果原本就熟悉Unity的朋友應該是可以有更多更優秀的作法,如果您願意也可以分享給小樽,或直接留言提供給更多往後需要學習的朋友們,因為範例中用到的外掛很多都不是新的,有部分是付費的,基於尊重智慧財產權,因此不另外打包整包專案的範例,僅提供範例中原創的C# Script,如果有朋友有需要,可以循官方的管道取得相關資源,祝大家都能做出自己喜歡的遊戲…我要去補眠了zzzzzz。