2013年11月14日 星期四

UNITY3D - Unity遊戲開發基礎Q&A

因為換工作交接的關係,才讓我意識到一般的程式設計人員要理解Unity有蠻大的障礙,如果有開發過遊戲或許還好一點,但如果只開發過一般應用程式的話就幾乎要重新理解觀念了。

Q1.Unity開發的程式進入點是什麼?
A1.Unity並沒有所謂的主程式進入點,遊戲以“場景”為一個主體,並且“場景”中有多個“遊戲物件”,每個“遊戲物件”內有一或多個“行為元件”。“行為元件”內才有所謂的起始進入點Awake,但要注意的是除非有經過設定每個“行為元件”的執行順序,不然在執行時“行為元件”的執行順序是不一定的。

Q2.寫好的Class怎麼使用呢?
A2.Unity內新增的程式檔案預設都會繼承MonoBehaviour,只要繼承MonoBehaviour就可以將這程式附加到“遊戲物件”上成為“行為元件”。只有在場景中的行為元件上的AwakeStart...等等MonoBehaviour的函式才會被執行。

Q3.MonoBehaviour有哪些函式?在什麼情況下用?
A3.通常最常用的是
AwakeStartUpdate還有碰撞相關的OnCollisionEnterOnTriggerEnterAwake 、Start用於初始化(Awake會比Start先執行),Update是每個Frame會執行一次,其他詳細的MonoBehaviour請參閱這裡

Q4.Frame是什麼意思?
A4.在遊戲中是指程式執行頻率也就是FPS(是Frames Per Second不是First Person Shooter),意味著一秒鐘執行多少個Frame。

Q5.有辦法知道每Frame的實際執行耗時嗎?
A5.Unity有提供Time.deltaTime可以取得每Frame實際耗時。

Q6.設定行為元件的enable?
A6.在Unity Editor上行為元件的勾選可以控制元件的enable,或者也可以在程式中直接設定enable。enable會影響StartUpdate是否執行,使用時需多加注意。

Q7.遊戲物件的父子物件關係?
A7.遊戲物件可以跟別的遊戲物件成為父子物件關係,子物件會隨著父物件移動。

Q8.如何執行別的行為元件的函式?
A8.1)宣告一該行為元件型態變數,并在Unity Editor上將指定的行為元件設定到變數上,或者是在程式裡用Object.FindObjectOfType(在別的遊戲物件上)、GetComponent(在本身的遊戲物件上)並執行函式。2)將該函式宣告為static,但並不建議此方法,除非是用在類似整個遊戲狀態管理上。3)使用Unity的SendMessage機制,SendMessage系列有三種,1.SendMessage(string methodName):對本身遊戲物件上的所有行為元件(不含父物件及子物件上的)通知執行跟methodName同名的函式;2.SendMessageUpwards(string methodName):對本身遊戲物件及父物件上的所有行為元件(不含子物件上的)通知執行跟methodName同名的函式;3.BroadcastMessage(string methodName):對本身遊戲物件及子物件上的所有行為元件(不含父物件上的)通知執行跟methodName同名的函式。

Q9.Prefab機制?
A9.當“遊戲物件”綁上很多“行為元件”後,要怎麼去重複利用這個“遊戲物件”呢?於是Prefab機制就出現了,將設定好的“遊戲物件”拖曳到Project View就可以將它變成Prefab。只要將Prefab拖曳到場景中就可以變成已經設定好的“遊戲物件”,這裡要注意的是Prefab跟產生出來的“遊戲物件”有父子關係。只要Prefab上的設定一更動,所有由此Prefab產生的“遊戲物件”都會更動,但如果是“遊戲物件”更動的話是不會影響Prefab及其它“遊戲物件”。 跟Prefab有關係的“遊戲物件”上會出現這些控制按鈕,Select可以讓你找到此物件的Prefab;Revert會讓此物件的設定回復得跟Prefab一樣;Apply會將此物件的設定覆蓋回Prefab,連帶的會更動到由此Prefab產生出來的其他“遊戲物件”。最後還要注意的是,更動“遊戲物件”上“行為元件”的設定值將會使這設定值被設立旗標(設定值會變粗體),表示此設定值跟Prefab脫鉤,Prefab更動也不會影響到此設定值


之後有想到什麼再補充上去吧...

2013年8月6日 星期二

福島GameJam 2013作品-神蹟之躍



http://fgj.igda.jp/dokuwiki/doku.php?id=team%3Ataipeiiii_c

這次在福島GameJam做出來的小小作品,因為時間只有30小時,所以隊名叫30hr應援團。

這次的主題是Jump,於是做了個類跳棋的遊戲。




遊戲目的是讓左下方出現的災民移動到右上方的出口

操作方式是是用滑鼠點擊可以交換同一行或同一列的地形,其中有些地形是無法交換的。

按Space Bar後再點擊地形可強制把地形更換。

詳細地形功能及地形變化表請參考網頁。

另,本作品將會在TGS東京電玩展的Indie Corner展出,請有到TGS的朋友幫忙拍個照讓我做個紀念,謝謝!

2013年2月4日 星期一

Global Game Jam讓你的心跳起來!

  自從去年2012年在Google+上看到有人分享參加MIT Game Jam的心得文章之後,心裡就一直想找機會參加這類活動,但去年12月的Ludum Dare又因為正好公司所作的遊戲正忙著收尾準備上架導致沒辦法參加(雖然事後還是忍不住自己做了個應景的世界末日遊戲http://dl.dropbox.com/u/26037476/Wasabi/Wasabi.html)。

  於是看到Global Game Jam的活動資訊時,不管三七二十一就填好報名資料送交了。一些同事知道我報名參加後問我說為什麼我要參加這種沒有名次沒有獎品的活動(還要爆肝...),當下我是無言以對,但事後想來也許就是因為那股熱血吧?我想要知道自己的實力、想知道這個世界有多廣、想見識更多高手,就如同這次的作品想表達的:就算是行尸走肉,只要找到目標就可以讓你的心臟再次跳動!(肝可以爆,但是請爆在你認為值得的事物上。)

  這次的主題很棒,就只有聽到心跳聲,但是卻可以讓人作出各種聯想。晚餐過後我們與遲到的成員會合並展開討論,一開始我提出了根據心跳聲的狀況來移動的遊戲,但是為了不浪費我們組的美術就捨棄了,我們從最直觀的方向入手,心跳聲來自心臟,再衍生到文明病,是一款玩家必須消除進入心臟有害物質的遊戲。 

  但還沒定案時,Ingrid讓我們看了一款雙螢幕的flash遊戲(我忘了名字 囧rz),大家討論後覺得切兩個螢幕可以讓玩家有緊張感可以採用,於是其中一個螢幕就變成心臟了(?),心臟是提供給人動力的來源,但我們又想要跟常識相反,另一個螢幕就變成殭屍在跑了XD。為了給殭屍跑的理由,原本是讓他對一個女生一見鍾情所以追著她跑,但後來又惡搞成是為了還那個女生錢包XD,再後來為了讓女生穿過障礙物不會顯得突兀所以她變成女鬼了(這是什麼發展XD),雖然最後我還是幫她寫了簡單的AI但是她已經沒辦法復活成人了XD。

  在最後一刻我們終於順利把作品完成,實在是非常感謝大家的努力!

感謝

企劃:Kinan
美術LightAngel (連續三天搬電腦主機來往住家與會場,超猛!)
主程式:Vincent Sai (感謝自己選擇把肝爆在GGJ)
場景程式:伍小韻
程式:小熊、劉柏賢

  雖然三天下來我真是體力耗盡,回到家也只是拼命的睡,但有一點可以肯定的是,下次我還是會參加!

作品頁面連結:http://globalgamejam.org/2013/hopping-dead

2012年12月13日 星期四

少年Pi的半路叛逃

前言:
這只是我硬把兩個不相干的東西湊在一起,若只是想看《少年Pi的奇幻漂流》電影介紹的可以直接離開了…

《半路叛逃》這本書我買了好段時間,但因為覺得文筆不好所以就沒寫書評。
而在看了《少年Pi的奇幻漂流》電影加上聽了東江亮先生的一席話後,心裡有無限的感觸造成這篇文章的出現。


少年Pi在大海中漂流時所面對的危機有:老虎、糧食短缺、茫然無助。
獨立遊戲開發者在這遊戲紅海中奮戰面對的是:自己、時間、失敗的考驗。

※注:如果有看過少年Pi的人,應該也很清楚劇中的老虎指的是Pi他自己。

孤身一人(或小團隊)在開發遊戲的路上其實也很類似在海中漂流,你永遠不知道這海有多廣?還有多久能上岸?下場會是淹死還是餓死?

而最早面對到的危機就是老虎(自己),這老虎反映了自身的各種慾望與放縱,很諷刺的是投入獨立開發往往都是因為想要釋放這隻老虎,但多數人往往會因為沒意識到它的危險而葬身虎口。

當能夠面對老虎不再逃避它的時候,就是可以開始嘗試馴服它的時候,但要知道馴服野獸並不是那麼簡單的事情,必須不氣餒的多方嘗試。

※注:我也還只在面對老虎的階段,很佩服半路兄可以順利將老虎馴服並且撐過後面的考驗。

要注意的是在海中漂流,最為重要的是不能失去希望,但也不要做過高的期望(上岸以後會成為富翁等等),先好好面對你面前的老虎吧。

最後,非常推薦大家去買《半路叛逃》這本書來看,也推薦大家去看《少年Pi的奇幻漂流》這部電影。

2012年12月3日 星期一

(遊戲試做) - 消方塊遊戲

沒試過做這種類型的遊戲,但身邊有人想做那就來想一下遊戲運作機制是如何。

流程應該是如下

1.產生全場景的隨機方塊
2.根據玩家所移動的方塊去運算是否消除自身及週遭的方塊
3.產生新的方塊
4.運算新的方塊及因消除而移位的方塊是否消除自身及週遭的方塊
5.重覆2-4

※注:本雛形不含圖形及輸入處理,純粹只是運作機制的試做。
C#雛形

2012年11月7日 星期三

IOS - IAP的那些鳥事

為了搞IAP經歷了許多痛苦和鳥事,深深覺得Apple也不是那麼人性化嘛= =

以下是開發IAP的注意事項

1.記得要加入StoreKit.framework (這不是廢話嗎?)

2.在iTunes Connect建立App (就算單純測試也要建立,建立了並不會馬上出現在AppStore),填寫資料時Bundle ID必須跟你的App Bundle ID一樣,最麻煩的是要提供一堆符合規定Size的圖檔,請善用command + shift + 4截圖吧。

3.建立完App之後一樣在iTunes Connect那點進App的詳細設定,右上角有個Manage In App Purchases,在裡面建立IAP的商品。

4.IAP的Product ID可以隨便設,要注意的是這是唯一性的,就算刪除了也無法再重用一樣的Product ID。

5.設定完之後在App頁面那裡點進View Detail,下方有個IAP設定,進去把剛才建立的IAP都加進去吧。(當App進入等待上傳Binary檔的流程時此設定將無法更改,切記!)

6.Xcode Build的時候記得Code Signing要選對,也就是對應App Bundle ID那個,我連Any IOS SDK的Code Signing也改了。(IAP必須用實際測試哦)

7.接下來,只要code沒寫錯Product ID也沒錯的話,但是在撈IAP產品資訊的時候卻頻頻出現invalidProductIdentifiers,你就只能擇日再測了。(沒錯!完全猜不透哪個環節出錯,不斷反覆檢查各個環節都找不出原因,就只能看著這個訊息發呆!但過兩天你甚麼都不用改它就突然可以撈到資料了...)

8.需要注意的還有一點,就在我測試沒問題之後過了幾天我把code移到正式專案時發生了慘劇,它突然又invalidProductIdentifiers了!就這樣又瞎忙了兩天發現問題出現在測試IAP用的Bundle ID跟正式專案是一樣的,而這兩個App居然同時存在於機器上,我天真的以為它會因為Bundle ID一樣而被覆蓋,但顯然它沒有......於是死馬當活馬醫將機器上的這兩個App都刪除重Build正式專案,結果資料又撈出來了= =(如果等個兩天還是invalidProductIdentifiers,請檢查看看機器上有沒有跟你專案一樣Bundle ID的App存在吧!)

2012年10月15日 星期一

UNITY3D - SendMessage多重參數

善用Unity3D內建的SendMessage函式可以讓程式更容易拆解、更好重覆利用,在Component Oriented Design上是很好用的工具。

但SendMessage系列有個缺陷是只能接收單一參數,除非自己將多重參數包成struct、class使用,這樣在使用上也變得無法統一不太便利。

如何改善呢?

試了幾種方法後發現用Hashtable較為合適,且C#或JavaScript皆可使用。按Key來得知所接收的變數該塞在哪?不用像用Array時還要確定變數的位子。

C#

void Start () {
    Hashtable args = new Hashtable();
//想塞什麼型態都可以
    args.Add("string", "i am string"); 
    args.Add("int", 123);
    args.Add("float", 567.89f);
    args.Add("Vector3", new Vector3(1, 2, 3));
    SendMessage("TestMessage", args);
}
void TestMessage (Hashtable args) { //可以直接使用或新建變數接收,個人覺得新建變數接收使用上較為直覺 //接收時須轉型為正確型態     string stringVar = (args.Contains("string")) ? args["string"].ToString() : "";     int intVar = (args.Contains("int")) ? (int)args["int"] : 0;     float floatVar = (args.Contains("float")) ? (float)args["float"] : 0.0f;     Vector3 vectorVar = (args.Contains("Vector3")) ? (Vecotr3)args["Vector3"] : Vector.zero; }


========================================

JavaScript


function Start () {
    var args : Hashtable = new Hashtable();

//想塞什麼型態都可以

    args.Add("string", "i am string"); 
    args.Add("int", 123);
    args.Add("float", 567.89f);
    args.Add("Vector3", new Vector3(1, 2, 3));
    SendMessage("TestMessage", args);
}
function TestMessage (args : Hashtable) {
    string stringVar = (args.Contains("string")) ? args["string"] : "";
    int intVar = (args.Contains("int")) ? args["int"] : 0;
    float floatVar = (args.Contains("float")) ? args["float"] : 0.0f;
    Vector3 vectorVar = (args.Contains("Vector3")) ? args["Vector3"] : Vector.zero;
}