Unity - 取得Android手機中的AssetBundle來使用 (使用Unity的API)

  取得Android手機中保存的資料並且載入到遊戲中,之前有發過一篇(Unity - 取得Android手機中的圖檔來做為材質),使用的方法是製作自己的PlugIn,目的在於內部java抓取資料傳送給Unity並使用,不過如果只是要讀取某些下載的檔案像是Bundle之類的,可以不用這麼麻煩。

  簡單來說可以用Application.persistentDataPath來取得你存放檔案的位置,或者是你明確的知道該檔案放在外部SD卡的哪個資料夾下,接著都可以用WWW來讀取該物件,讀取完畢後再看是要怎麼做就行了。

Unity - PuzzleGame 5 (接水管接電線)

  接電線或是接水管類型的遊戲,大致上就是有一個可以調整的盤面,上面有許多格子可以旋轉,不一定是正方形,不過這邊簡單製作用正方形比較方便,格子上會有一些路徑,旋轉格子讓這些路徑連接起來。

  通常會有一個起點一個終點,既然這邊是接電線,起點就是電源,終點就用電燈,當路徑從起點到終點有達成連線的時候,遊戲就完成了。



  製作這個我覺得可以有幾種做法,一種就是亂數安排路徑,好處是每次結果都會不同,但是壞處就是難度比較無法掌控;另一種就是預先把版面設計幾種,好處就是可以掌控電線的路徑圖案跟難度,壞處就是要花時間把版面設定完畢,而且變化不大,重玩幾次就可以背下答案。

  這邊使用的亂數安排路徑,因為比較省時間,另外也可以使用之前做過的迷宮產生器來產生路線資料。

Unity - PuzzleGame 4 (記憶配對)

  記憶配對遊戲,場面上有數張卡片,每次可以翻開兩張卡片,如果兩張卡片圖案相同,那麼就可以把這兩張卡片從場上移除,如果兩張卡片圖案不同,就必須把兩張卡片翻回背面蓋好。

  最重要的就是記住翻過的卡片位置,當然也記住那張圖案是什麼,避免因為忘記再重複翻之前翻過的卡片浪費時間,遊戲就是越快把卡片全部移除越好。


  製作使用的Unity版本為5.2.2f1。

Unity - PuzzleGame 3 (倒水問題)

  一個經典的倒水問題,桌上只有一個5公升跟一個3公升的水瓶,假設有一個無窮的水源,要你使用這兩個瓶子來量取某一個數量的水,可以是1公升也可以是4公升,諸如此類。

  這個題目也不局限於5公升跟3公升的水瓶,不過這邊就用最簡單的這兩個單位來製作,基本原理是一樣的,差別在於達到需求水量所需的步驟數不同。


  製作使用的Unity版本為5.2.2f1。

Unity - PuzzleGame 2 (環形旋轉拼圖)

  旋轉拼圖,其實也就是個多層轉盤而已,主要目的在調整每一層環形圖片的角度,使得每一層環的圖片可以接在一起,形成一張完整沒有折斷的圖。

  遊戲的機制也很簡單,就讓使用者調整每個環旋轉的角度,然後比對每個環跟前後兩個環的角度,如果差異在某個誤差值以內就視為擺放正確,而如果每個環都擺放正確就遊戲結束。


  製作使用的Unity版本為5.2.2f1。

Unity - PuzzleGame 1 (9宮格總和15)

  接下來做做幾個益智遊戲,很久以前就有玩到許多這類型的遊戲,有些機制還滿有趣的,試著複製幾個類型來做看看就是了,不過有許多算是偷懶的部分就隨意吧。


  我想先從簡單又算經典的九宮格總和15來做,機制很容易懂,就是九宮格的三個橫向、三個直向以及交叉對角線的總和都是15,記得第一次玩到這個的時候感覺有點難,不過在了解了以後其實是滿容易的,雖然版面排列不只一種,但是基本上都差不多就是了。

  製作使用的Unity版本為5.2.2f1。

Unity - Build AssetBundle注意目標平台

  這其實只是一個小問題而已,不過當初在製作的時候沒有注意到,結果就是在電腦Editor上執行測試,AssetBundle裡的材質都有讀取到,結果Build到手機上後材質都不見了,反而浪費了許多時間在找問題。

  這邊用的API是BuildPipeline.BuildAssetBundle(),根據官方的API說明來看(http://docs.unity3d.com/ScriptReference/BuildPipeline.BuildAssetBundles.html),可以知道BuildPipeline.BuildAssetBundle()他預設的BuildTarget是WebPlayer,當初也就沒設定到這部分造成手機讀不到。

  所以只要簡單的依據平台設定例如:BuildTarget.Android就解決當初電腦測試正常,Android手機看不到東西的問題了。

Unity - 簡易亂數迷宮產生3(Generate maze from a seed)

  這次把之前產生迷宮的方法做個小小的修改,而這個小小的修改就可以讓迷宮的使用簡單的Seed來產生,這樣子做法的好處跟之前的比起來,之前的亂數是無法自己控制的,每次產生的迷宮都不一樣;改為Seed之後,雖然亂數的計算還是交給電腦,但是在亂數產生上可以有自己的控制,因此我同樣可以亂數產生迷宮,但如果需要也可以再度產生出某個相同的迷宮,只要把Seed記錄下來。

  同樣使用Kruskal's algorithm來做為迷宮產生的判斷。


Unity - 簡易亂數迷宮產生2(Randomized Kruskal's algorithm)

  這次亂數迷宮產生,換一個方法來產生,原本以為應該也不會差太多,事實上產生出來的路徑還真的有些許不同,也不能說哪個好或哪個不好,反正就是不同概念產生的東西。



  本次使用的演算法概念是Randomized Kruskal's algorithm(https://en.wikipedia.org/wiki/Maze_generation_algorithm),大概就是只先把所有的牆面紀錄列表,亂數取一個牆,兩兩格子為一組,如果這一組格子是被這個牆面切兩半,並且成為獨立兩個個體,就把這面牆打通。

Unity - 簡易亂數迷宮產生1(Depth-first search)

  這次來玩玩亂數迷宮產生,感覺好像很複雜,事實上如果真要研究下去是可以花不少時間的,不過如果只是單純的產生一張圖或做些簡易的變化,其實只要了解基本的概念後就可以很快地建立一張圖了,後續要做變化就需要再花時間了。


  本次使用的演算法概念是Depth-first search(https://en.wikipedia.org/wiki/Maze_generation_algorithm),也有看到好像叫Growing tree,也就是先亂數從陣列中取一個格子作為起始點,然後從這個起始點開始隨機往四周某一個方向開一個洞到下一格,再從下一格隨機往四周開一個洞過去,一直到四周已經無法開洞或是已經走過的格子就停止,此時倒回上一格在嘗試往另一個方向做同樣的事;雖然講起來有點繁瑣,但也就是一個遞迴的概念了。

忙了一陣子

  稍稍忙了三四個月,一方面是工作上的專案剛好要上架所以做最後的整理跟Debug,另一方面是剛好開始製作子彈編輯器,這是很久以前就有計畫要做的東西,剛好未來要幫朋友做的2D射擊遊戲也可能會用到,抽空的時間多少就花在研究以及設計編輯器上。

  目前專案已經上架,所以空閒時間比較多了,也把子彈編輯器製作接近完成了,目前子彈編輯器的子彈部分完成約7成,發射部分完成約9成多,接著花點時間整合完後就可以開始製作教學跟Demo,雖然只是一個小小的工具,不過做完應該也會嘗試審核上架看看。

  因為開始比較閒一點啦,終於可以再度開始研究發文一些無聊功能了XD,像是無聊看到的亂數迷宮產生,或者是以前研究一半的節奏遊戲相關之類的東西都滿不錯的。

手做組木玩具 - DIY Wooden Puzzle(Hash Key Burr)

  貓走道的箱子做到一半,繼續用廢料來做組木玩具換個心情,這次做個跟之前稍稍不一樣造型的,同樣也是找了個別人設計感覺看起來還不錯的來試試看。



Unity - 簡單製作自己的3D圖像化音樂顯示器(Simple 3D Audio Visualization)

  這次動手做自己的3D圖形化音樂撥放器,使用Unity提供的API來取得聲音檔的資訊,這邊的網格Mesh,使用的是前一篇(Unity - 動態製作一張平面網格)所建立的平面網格來製作這個3D圖形化的效果。


  簡單的使用一個Mesh來顯示波形,使用一個List來保存舊資料,就可以達到這樣類似流動的效果了。

Unity - 動態製作一張平面網格 (Dynamically create plane mesh)

  簡單快速使用動態產生一張平面Mesh,跟之前的用Mesh來製作電流效果線段的方式一樣,不過這次只是單純的建立一張平面網格,跟內建的Plane物件類似,不過多了一些簡單操控的數值而已。



手做組木玩具 - DIY Wooden Puzzle(Crystal)


  試著自己做一個組木玩具玩玩,之前都在做貓走道,換個東西做做轉換一下心情,哈。之前就想做了,也在網路上找了一些製作圖,但是一直都懶的動就是了,這次就順便來做一個啦。


手做貓走道 - Part 3

  這幾個周末同樣繼續進行牆壁走道計畫,基本上升已經做完一部分了,接下來就有一段稍微比較空曠的地方可以用了,會先遇到我房間門口,預計在上面做一個小平台,接著就可以繼續往左方走,目前先打算做到小平台,之後再考慮看看要怎麼做吧。




北方之塔19

  差不多要開始準備開始遊戲的流程了,之前測試的時候一直都是直接把數值寫在Code裡面,也需要把這部分改為真實的讀取載入,不然一直用寫死在Code的方式也沒辦法確認檔案的讀取流程是沒問題的。

  果然早點處理這個部分還是比較好,在改為讀取真實的存檔後有遇到幾個建構先後順序的問題,不過還算容易處理,但是還是有一些地方需要重新改寫一下才行。


Oculus Rift DK2 簡易安裝使用心得

  拿到了新玩具,之前總以為一些Youtuber表現有點誇張,不過在親自用過後才知道真的滿有趣的,人眼透過這樣的裝置確實可以看到奇妙的東西,這也讓我想起以前知覺心理學的許多實驗,以及一些關於錯覺之類的東西,事實上人腦還滿容易被這樣欺騙。




Unity - 用Script動態取得內建SpritePacker製作的Atlas,並替換為不同的Sprite圖

  同樣是個使用Unity內建SpritePacker的狀況,想要動態取得該Sprite並且把這個圖替換成同一張Atlas上的另一個圖片,或是不同Atlas上的另一張圖片,該如何做,Unity專案中似乎找不到SpritePacking後的那張Atlas來用(Atlas資源在專案的\Library\AtlasCache但是不符合目前的需求)




測試測試
 



當Sprite資料設定了Packing Tag之後,Unity就會在Build的時候把設定的圖片全部打包成一張Atlas(在edit->project setting->editor當中可以設定spritePacker的運作)


這邊我把這六張圖的Tag都設定相同的Set1。


使用SpritePacker打包完的圖片成為一張Atlas,之後再使用這些Sprite製作物件的時候,就可以減少DrawCall的數量。


不過如果當有時候想要動態讀取某些圖片來使用的時候該怎麼辦,一般的用法就是把圖片資料放到Resources資料夾下面,這樣在遊戲運作就可以使用Resources.Load()來載入,但是如果在這個地方這樣使用的話會有問題。


因為這幾張圖片都是設定過PackingTag的,當SpritePacker重新打包(只是把圖片移動到Resources資料夾SpritePacker不會重新打包,可以隨意調整一下圖片的設定讓他強制更新),後會發現這幾張原本設定了Packing Tag的圖從Atlas中消失了。

  這是Unity正常的運作機制,當你把圖片放到Resources中就會在Build的時候把資源一起封裝起來,而原本的SpritePacker製作的Atlas就已經會Build在一起,所以這是Unity避免一個相同的圖片資源重複增加到Build裡面。

  同時這個狀況下原本想要節省的Draw call就產生不了作用了,放到Resources的那幾張圖片,即便有設定PackingTag也不會被放進Atlas。




  我目前雖然是沒找到Unity可以直接取得SpritePacker Atlas或相關Rect的方式,因此這邊使用了一個怪招,雖然有點小麻煩,但是可以達到相同的目的。


首先相關的圖片設定好Packing Tag然後使用這些Sprite來製作幾個Prefab,並且把Prefab放到Resources資料夾下。

  接下來要做的事情就是先從Resources載入Prefab,取得Prefab上面的Sprite資料,接著取得Sprite所屬的Atlas Texture名稱以及在Atlas中的Rect位置。

//取得Prefab所屬的Atlas
private void GetSpriteAtlas(string prefabName)
{
    //先從Resources中載入Prefab
    GameObject obj = Resources.Load(prefabName) as GameObject;
    if(obj == null)
        return null;

    //取得物件上的SpriteRenderer
    SpriteRenderer sr = obj.GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;
    //Texture2D spriteTex = sr.sprite.texture; //這個可以得到Sprite的Texture,如果是有設定PackingTag的則會取得Atlas的Texture
    //Debug.Log(sr.sprite.texture.name); //Atlas的名稱,例如SpriteAtlasTexture-1024x1024-fmt13
    int texID = sr.sprite.texture.GetNativeTextureID();

    //既然有了Atlas的ID,就可以從這個去Resources裡面讀取這張材質,因為SpritePack製作的Atlas會一起Build起來,但是一般在Project視窗中看不到。
    //先把Resources裡面所有的Texture2D載入,再來一個一個比對名稱,注意這個過程效率低、速度慢
    Texture2D[] allTexs = Resources.FindObjectsOfTypeAll();
    foreach(Texture2D tex in allTexs)
    {
        //如果使用Texture的name來比對會有名稱相同的問題
        if (tex.GetNativeTextureID() == texID)
            return tex;
    }
    return null;
}



  不過這樣只取得了該張Atlas,想要正確使用Atlas上面的Sprite圖還需要對應的Rect參數,不過Unity同樣也無法取得該Atlas上面所有Sprite的Rect的列表,也許是我沒找到方法,不過同樣也是可以自己製作一個列表,所以這次換一個方式一樣用怪招在Awake或Start的時候取得Atlas同時把相關的Sprite建立列表。


//使用一個簡單的方法把物件建立列表
public class SpritesList : MonoBehaviour
{
    public Dictionary<string, Dictionary<string, Rect>> allSpr = new Dictionary<string, Dictionary<string, Rect>>();
    public Dictionary<string, Texture2D> allTextures = new Dictionary<string, Texture2D>();

    public string folderPath = "";
    public SpriteRenderer targetSprite;

    void Awake ()
    {
        //先從Resources資料夾讀取所有的Prefab
        Object[] allObjs = Resources.LoadAll(folderPath, typeof(GameObject));
        foreach(Object obj in allObjs)
        {
            GameObject go = obj as GameObject;
            SpriteRenderer sr = go.GetComponent(typeof(SpriteRenderer)) as SpriteRenderer;

            if(sr != null && sr.sprite.packed) //只是確認該Sprite是有被SpritePacker打包過的
            {
                //取得材質的ID,如果這邊是用texture.name的話也是可以,但是有遇過名稱相同的問題
                string texID = sr.sprite.texture.GetNativeTextureID().ToString();

                if(!allTextures.ContainsKey(texID))
                {
                    allTextures.Add(texID, sr.sprite.texture);
                    allSpr.Add(texID, new Dictionary<string, Rect>());
                }
                //用Prefab的名稱來作為取得該Rect的Key,注意這邊的textureRect,根據官網的說明,如果SpritePacker是使用TightPack的話這邊就會造成例外
                Dictionary<string, Rect> spriteRect = allSpr[texID];
                spriteRect.Add (go.name, sr.sprite.textureRect);
            }
        }
    }
}


  所以把Rect建立列表後,就可以用物件的名稱來取得Rect並動態更換同一個Atlas上的Sprite物件,如果要替換不同Atlas的話,也可以用類似的方式先取得不同Atlas的參照保留下來,並把各個Atlas上的物件各自做列表。不過我這邊只是單純把材質跟圖片座邊標分別存Dictionary,還是要看自己的需求去做。

  使用這個方法,Prefab變成只是為了取得Sprite資料用的,當然也可以直接Load這些Prefab,直接用來建立物件設定好位置並把之前的物件刪除就好了,好像這樣比較輕鬆的樣子。

  不過有時候可能會有奇妙的用途,例如在NGUI裡面有個Unity 2D Sprite的物件,就可以用這個方法取得Atlas中的某些物件然後設定到Unity 2D Sprite上讓它顯示在UI上。


  如果有任何錯誤歡迎提出。

手做貓走道 - Part 2

  接續之前的進度繼續開工,這周末又剛好有連續假期,可以製作的進度我想應該會比較多一點,不過根據之前製作的經驗來看,時間其實都不是花在製作上,一小部分花在規劃跟測量上,大多的時間是花在打混摸魚、看電視、上網上吧XD

  這次的進度就是把上升階段完成一半,至少要到開始轉向的部分,接下來就可以到天花板附近了,這有些高的地方鑽孔之類的感覺有點危險,說不定會有懼高症之類的症狀,哈。



手做貓走道 - Part 1

  最近打算把很久以前就想做的貓走道開始製作,需要的工具基本上都算有,大概就只缺材料了。這個計畫拖了好久才終於要開始動作了,終於找了一個周末去附近的材料店買了幾塊木板、繩子、螺絲等等。

  材料買了回來但卻不能立刻做,因為當初想製作的東西也只是個簡單的概念,目前除了需要找一個可以利用的空間之外,還需要簡單的測量一下空間,雖然不想要全部詳細的規畫完後才開始做,但至少初期的部分要有些規劃,之後的部分就等初期完成再說。



手做貓抓板(Cat scratch board)

  好久以前做的貓抓板已經爛掉好久了,最近終於有時間,而且也剛好有材料,就來做一個新的吧,而且材料還很多,這次可以做一個大的。

  之前看過有一種是正方形中間中空的,這次就來做這種的,材料的大小我覺得也算剛好,做出來中間的空間不會太大讓整個結構不穩,也不會太小讓使用者在裡面抓起來不舒服。

  大致準備好一些工具就可以來玩啦!

  先把要用的瓦楞紙箱準備好,這次的品質比較好,上面也沒有印任何廣告花紋,監工已經在檢查了。


Unity - 使用2D Sprite時改變SortingLayer或SortingOrder造成Draw Call大量增加的問題

  為了稍微增進一點效能,節省Draw Call的情況下,使用Unity內建的Sprite Packer,這可以把設定相同Tag的圖片合成一張Atlas,這是個很方便的功能,也就可以省去使用外部軟體自己合成一張Atlas的步驟。

關於Sprite Packer的相關使用說明就請自行參考官網說明文件
http://docs.unity3d.com/Manual/SpritePacker.html


  不過有時候在使用這個功能卻發現DrawCall並沒有預期中的減少,反而是增加許多的情況,一開始其實覺得有些莫名其妙,稍微研究一下後才發現事有蹊俏。

Unity - 2D場景中使用ParticleSystem時,粒子被Sprite擋住的問題

  製作2D遊戲的時候免不了會想使用一些粒子系統,但是在某些狀況下使用的粒子系統發射的粒子會被2D的Sprite擋住無法呈現在Camera前面。基本上這只是2D的SortingLayer、SortingOrder造成的問題。

北方之塔18

  又過了好一陣子阿,這次的主介面又有稍作調整,不過並沒有調整太多,主要的部份還是在修改許多舊的Code,同時製作一個新的系統,簡單的場景互動系統,這個部分可能包含的像是物品拾取、啟動機關、移動物件之類的功能。

  主介面除了調整配置、更換一些圖,另外就是改變功能按鈕,原本功能按鈕只是簡單的一個按鈕背景加上文字,但是覺得有點太佔空間,尤其是顯示在畫面中的時候,而且文字也比較不容易看,所以更換成小圖示,不過要選擇哪種小圖示又是個煩惱的決定,因為使用的素材不是自己畫,所以要挑選有代表性的才行。