Unity3D – Convert Terrain Tree to GameObject

簡介

這邊要介紹一個好工具,當你覺得你在Terrain上種植了許多樹木,但在執行場景時的效能不佳的時候,又必須使用到Terrain來顯示地形,這篇提供了將Terrain中用筆刷種植的樹木單獨每個轉換成GameObject方法,並且又可以讓你的場景多多少少省了一些資源。此篇會講到透過程式來轉換並加上了TerrainToMesh的套件應用。

Convert Terrain Tree to GameObject

首先,這個功能在Unity3D中沒有範例程式,範例功能可以使用。這邊會使用到程式碼來轉換Terrain Data中的樹木的參數成為GameObject。我們先講解整個新增Convert Terrain Tree to GameObject的工具,請看下方流程 :
Step1 : 新增一個資料夾在Project Window中的Assets資料夾,並命名為「Editor」。

Step2 : 在剛剛新增的「Editor」資料夾中新增一個「C# Script」,並命名為「 TreeReplacerS 」。

Step3 : 大家可以看下方的Tool程式碼區,把它複製整份內文貼上剛剛新增的「TreeReplacerS.cs」程式碼中。(有在程式碼中註解解釋程式架構)。

Step4 : 可以看到下圖,點選Window -> ConvertTerrainTool -> TreeReplacerS 。

Step5 : 將Terrain拖曳到剛剛新增的Tool第一個欄位中,下圖中的1.是將Terrain中的樹木獨立出來成為GameObject,2.是假如你已把TerrainTree拆出來了話,按下這按鈕就可以把他移除。

Step6 : 拆出來的樹木將會歸整在「TREES_GENERATED」父GameObject中。(這個名稱可以在程式碼中修改」。

Terrain to Mesh

這時我們就不需要再重新發明輪子了,大家可以到Asset Store搜尋「Mesh Terrain Editor Free」。裡面其實包含很多功能,但我們這邊只先講述到轉換Terrain為Mesh來看看效能有沒有提升。

Step1 : 請到Window -> Mesh Terrain Editor -> Converter,並會跳出Editor視窗。

Step2 : 這邊要注意的是自己的Terrain上的Texture Layer不能超過四個(付費才可以超過四張)。可以看到MTE轉換器視窗,名稱是轉換後的物件名稱,下方的文件格式可以選取.obj與.Asset。下方式選擇輸出的資料夾(建議放在Asset中)。接著的區塊會影響到轉換出來Mesh的精細度,當調整頂點數的數值時,下方會及時運算細部數面數。

Step3 : 上方都設定好之後請按下「開始轉換」,會可以看到在Hierarchy有多出一個Terrain的GameObject(Prefab)。下圖是要說明,標示1.的是原始我們已經擁有的Terrain(包含樹木),2.是我們透過程式碼拔出Terrain中的樹木與TerrianToMesh所轉換的物件。

小結

這篇提供了兩個方法來將Terrain做一個不同方式的優化,可以先看到下面兩張圖,左邊是原始的Terrain Batch : 2686,而右邊是整理過的兩物件的GameObject Batch : 2633,看來是優化了一些,而且這樣拆開物件有好有壞,可以讓Terrain的自由度更高,且可以把不需要的Terrain部分刪除、下上Tag等等。假如想要在右圖更省資源一點可以將物件Static化 ( Terrain Batch : 2400左右)。

Tool 程式碼

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
// Replaces Unity terrain trees with prefab GameObject.

//下方的class名稱「TreeReplacerS 」要跟在Unity Editor中新增的名稱要相同
//並可以看到public class要繼承「EditorWindow」,並配合要在public class上方寫上「[ExecuteInEditMode]」,才可以把這工具鑲嵌在UnityEditor Window中

[ExecuteInEditMode]
public class TreeReplacerS : EditorWindow
{
    //在Tool Window中的顯示的Title方法
    [Header("References")]
    public Terrain _terrain;
    //在Window中可以看到的MenuItem名稱
    [MenuItem("Window/TerrainConvertTool/TreeReplacer")]
    static void Init()
    {
        TreeReplacerS window = (TreeReplacerS)GetWindow(typeof(TreeReplacerS));
    }

    //顯示在Tool Window的功能GUI
    void OnGUI()
    {
        _terrain = (Terrain)EditorGUILayout.ObjectField(_terrain, typeof(Terrain), true);
        if (GUILayout.Button("Convert to objects"))
        {
            Convert();
        }
        if (GUILayout.Button("Clear generated trees"))
        {
            Clear();
        }
    }
    //"Convert to objects"按鈕按下去之後的功能process
    public void Convert()
    {
        TerrainData data = _terrain.terrainData;
        float width = data.size.x;
        float height = data.size.z;
        float y = data.size.y;
        // Create parent
        GameObject parent = GameObject.Find("TREES_GENERATED");
        if (parent == null)
        {
            parent = new GameObject("TREES_GENERATED");
        }
        // Create trees
        foreach (TreeInstance tree in data.treeInstances)
        {
            if (tree.prototypeIndex >= data.treePrototypes.Length)
                continue;
            var _tree = data.treePrototypes[tree.prototypeIndex].prefab;
            Vector3 position = new Vector3(
                tree.position.x * width,
                tree.position.y * y,
                tree.position.z * height) + _terrain.transform.position;
            Vector3 scale = new Vector3(tree.widthScale, tree.heightScale, tree.widthScale);
            GameObject go = Instantiate(_tree, position, Quaternion.Euler(0f, Mathf.Rad2Deg * tree.rotation, 0f), parent.transform) as GameObject;
            go.transform.localScale = scale;
        }
    }
    //"Clear generated trees"按鈕按下去之後的功能process
    public void Clear()
    {
        DestroyImmediate(GameObject.Find("TREES_GENERATED"));
    }
}

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *