通過一個樹幹模型和樹葉模型 隨機制作不同的預製體
Editor腳本:
//=====================================================
// - FileName: AutoCreateTreeModel.cs
// - Author: #AuthorName#
// - CreateTime: #CreateTime#
// - Email: #AuthorEmail#
// - Description:
// - (C) Copyright 2019, webeye,Inc.
// - All Rights Reserved.
//======================================================
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using Qarth;
using Random = UnityEngine.Random;
using Object = UnityEngine.Object;
namespace GameWish.Game
{
public class AutoCreateTreeModel
{
static int UpLevel = 1;
static int MidLevel = 3;
static int LowLevel = 2;
static float midR=1.5f;
static float topR = 0.1f;
static float botR = 1f;
static float lowHighIntevel = 0.4f;
[MenuItem("Tools/CreateTreePrefab %#J")]
private static void CreateTreePrefab()
{
GameObject go = PrefabUtility.InstantiatePrefab(Resources.Load("TreeCreateModel/scene_tree_model"))as GameObject;
Transform root = go.transform.GetChild(0).Find("LeaveRoot");
GameObject model = null;
if (!go)
{
Log.e("Select is null!Please select a treeModel");
return;
}
go.IterateGameObject((child)=>
{
if (child.name.Contains("leave_"))
{
GameObject.DestroyImmediate(child);
}
});
if (root)
{
model = root.Find("leave").gameObject;
}
if (model)
{
//樹的中段(空心圓生成)
for (int i = 0; i < MidLevel; i++)
{
GameObject clone1 = GameObject.Instantiate(model);
clone1.SetActive(true);
clone1.name = string.Format("leave_mid_{0}",i);
clone1.transform.SetParent(root);
Vector2 p = Random.insideUnitCircle * midR;
clone1.transform.localPosition = new Vector3(p.x, 0, p.y);
}
//樹的下段
for (int i = 0; i < LowLevel; i++)
{
GameObject clone2 = GameObject.Instantiate(model);
clone2.SetActive(true);
clone2.name = string.Format("leave_bot_{0}", i);
clone2.transform.SetParent(root);
Vector2 p = Random.insideUnitCircle * botR;
clone2.transform.localPosition = new Vector3(p.x, -lowHighIntevel, p.y);
}
//樹的上段
for (int i = 0; i < UpLevel; i++)
{
GameObject clone3 = GameObject.Instantiate(model);
clone3.SetActive(true);
clone3.name = string.Format("leave_top_{0}", i);
clone3.transform.SetParent(root);
Vector2 p = Random.insideUnitCircle * topR;
clone3.transform.localPosition = new Vector3(p.x, lowHighIntevel, p.y);
}
model.SetActive(false);
}
else
{
Log.e("選中的物體{0}無法制作樹木模型!", go.name);
return;
}
foreach (Transform child in root)
{
child.transform.localScale *= Random.Range(0.9f, 1.2f);
//child.transform.Rotate(new Vector3(RandomHelper.Range(0, 360), RandomHelper.Range(0, 360), RandomHelper.Range(0, 360)));
}
go.IterateGameObject((child) =>
{
child.isStatic = true;
});
go.name = string.Format("auto_tree_{0}",System.DateTime.Now.ToString("mmddss"));
//製作預製體到對應路徑
bool success = false;
Object tempPre = PrefabUtility.SaveAsPrefabAsset(go,string.Format("Assets/Res/FolderMode/Prefabs/AutoTree/{0}.prefab", go.name),out success);
if (success)
{
Log.i("Create {0} Prefab Successful!!", go.name);
}
else
{
Log.e("Create {0} Prefab Failed!!", go.name);
}
}
}
}
PS:由於生產的樹雖然勾選了static 但是在場景中的性能依舊很差,暫時不知道原因,所以爲了優化性能,採取了合併mesh的方法,將樹的mesh合併成一個整體。合併之後發現不能保存預製體,於是將mesh保存了下來,所以路徑到時候需要自己修改:
新的Editor
//=====================================================
// - FileName: AutoCreateTreeModel.cs
// - Author: #AuthorName#
// - CreateTime: #CreateTime#
// - Email: #AuthorEmail#
// - Description:
// - (C) Copyright 2019, webeye,Inc.
// - All Rights Reserved.
//======================================================
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using Qarth;
using Random = UnityEngine.Random;
using Object = UnityEngine.Object;
using UnityEditor.SceneManagement;
using System.IO;
namespace GameWish.Game
{
public class AutoCreateTreeModel
{
static int UpLevel = 1;
static int MidLevel = 3;
static int LowLevel = 2;
static float midR = 1.5f;
static float topR = 0.1f;
static float botR = 1f;
static float lowHighIntevel = 0.4f;
[MenuItem("Tools/CreateTreePrefab %#J")]
private static void CreateTreePrefab()
{
GameObject go = PrefabUtility.InstantiatePrefab(AssetDatabase.LoadAssetAtPath("Assets/Res/FileMode/TreeCreateModel/scene_tree_model.prefab",typeof(GameObject))) as GameObject;
Transform root = go.transform.GetChild(0).Find("LeaveRoot");
GameObject model = null;
if (!go)
{
Log.e("Select is null!Please select a treeModel");
return;
}
go.IterateGameObject((child) =>
{
if (child.name.Contains("leave_"))
{
GameObject.DestroyImmediate(child);
}
});
if (root)
{
model = root.Find("leave").gameObject;
}
if (model)
{
//樹的中段(空心圓生成)
for (int i = 0; i < MidLevel; i++)
{
GameObject clone1 = GameObject.Instantiate(model);
clone1.SetActive(true);
clone1.name = string.Format("leave_mid_{0}", i);
clone1.transform.SetParent(root);
Vector2 p = Random.insideUnitCircle * midR;
clone1.transform.localPosition = new Vector3(p.x, 0, p.y);
}
//樹的下段
for (int i = 0; i < LowLevel; i++)
{
GameObject clone2 = GameObject.Instantiate(model);
clone2.SetActive(true);
clone2.name = string.Format("leave_bot_{0}", i);
clone2.transform.SetParent(root);
Vector2 p = Random.insideUnitCircle * botR;
clone2.transform.localPosition = new Vector3(p.x, -lowHighIntevel, p.y);
}
//樹的上段
for (int i = 0; i < UpLevel; i++)
{
GameObject clone3 = GameObject.Instantiate(model);
clone3.SetActive(true);
clone3.name = string.Format("leave_top_{0}", i);
clone3.transform.SetParent(root);
Vector2 p = Random.insideUnitCircle * topR;
clone3.transform.localPosition = new Vector3(p.x, lowHighIntevel, p.y);
}
model.SetActive(false);
}
else
{
Log.e("選中的物體{0}無法制作樹木模型!", go.name);
return;
}
foreach (Transform child in root)
{
child.transform.localScale *= Random.Range(0.9f, 1.2f);
//child.transform.Rotate(new Vector3(RandomHelper.Range(0, 360), RandomHelper.Range(0, 360), RandomHelper.Range(0, 360)));
}
go.IterateGameObject((child) =>
{
child.isStatic = true;
});
go.name = string.Format("auto_tree_{0}", System.DateTime.Now.ToString("ddhhmmss"));
BoxCollider bc = go.AddMissingComponent<BoxCollider>();
bc.isTrigger = true;
bc.center = new Vector3(0, 1.31f, 0);
bc.size = new Vector3(1, 4, 1);
go.AddMissingComponent<Obstacle>();
go.SetAllLayer(LayerMask.NameToLayer(Define.LAYER_OBSTACLE));
go.SetAllTag(Define.TAG_OBSTACLE);
CombineMesh(go);
//SaveMesh(go);
//製作預製體到對應路徑
bool success = false;
Object tempPre = PrefabUtility.SaveAsPrefabAsset(go, string.Format("Assets/Res/FolderMode/Prefabs/AutoTree/{0}.prefab", go.name), out success);
if (success)
{
Log.i("Create {0} Prefab Successful!!", go.name);
}
else
{
Log.e("Create {0} Prefab Failed!!", go.name);
}
}
static void CombineMesh(GameObject go)
{
Transform tSelect = go.transform;
if (!tSelect)
{
Log.e("{0} is null! please check!", go.name);
return;
}
if (tSelect.childCount < 1)
{
return;
}
if (!tSelect.GetComponent<MeshFilter>())
{
tSelect.gameObject.AddComponent<MeshFilter>();
}
if (!tSelect.GetComponent<MeshRenderer>())
{
tSelect.gameObject.AddComponent<MeshRenderer>();
}
MeshFilter[] tFilters = tSelect.GetComponentsInChildren<MeshFilter>();
//根據所有MeshFilter組件的個數申請一個用於Mesh聯合的類存儲信息
CombineInstance[] tCombiners = new CombineInstance[tFilters.Length];
//遍歷所有子物體的網格信息進行存儲
for (int i = 0; i < tFilters.Length; i++)
{
//記錄網格
tCombiners[i].mesh = tFilters[i].sharedMesh;
//記錄位置
tCombiners[i].transform = tFilters[i].transform.localToWorldMatrix;
}
//新申請一個網格用於顯示組合後的遊戲物體
Mesh tFinalMesh = new Mesh();
//重命名Mesh
tFinalMesh.name = "AutoCombineMesh";
//調用Unity內置方法組合新Mesh網格
tFinalMesh.CombineMeshes(tCombiners);
//賦值組合後的Mesh網格給選中的物體
tSelect.GetComponent<MeshFilter>().sharedMesh = tFinalMesh;
//賦值新的材質
tSelect.GetComponent<MeshRenderer>().material = AssetDatabase.LoadAssetAtPath("Assets/Res/FolderMode/Models/Tree/treeMesh.mat", typeof(Material))as Material;
tSelect.GetChild(0).gameObject.SetActive(false);
//保存mesh
string fullPath = "Assets/Res/FolderMode/Models/Tree/AutoTreeMesh/";
if (!Directory.Exists(fullPath))
{
Directory.CreateDirectory(fullPath);
}
Mesh mesh = go.GetComponent<MeshFilter>().sharedMesh;
string path = Path.Combine(fullPath, go.name + ".asset");
AssetDatabase.CreateAsset(mesh, path);
AssetDatabase.Refresh();
EditorSceneManager.MarkAllScenesDirty();
}
}
}
附上IterateGameObject方法:
/// <summary>
/// 遍歷go c
/// </summary>
/// <param name="go"></param>
/// <param name="handle"></param>
static public void IterateGameObject(this GameObject go, Action<GameObject> handle)
{
Queue q = new Queue();
q.Enqueue(go);
while (q.Count != 0)
{
GameObject tmpGo = (GameObject)q.Dequeue();
foreach (Transform t in tmpGo.transform)
{
q.Enqueue(t.gameObject);
}
if (handle != null)
{
handle(tmpGo);
}
}
}
/// <summary>
/// 設置go層級關係 c
/// </summary>
/// <param name="go"></param>
/// <param name="layer"></param>
static public void SetAllLayer(this GameObject go, int layer)
{
IterateGameObject(go, (g) =>
{
g.layer = layer;
});
}
static public void SetAllTag(this GameObject go, string tag)
{
IterateGameObject(go, (g) =>
{
g.tag = tag;
});
}
接下來展示實際的效果:
樹的模板:
其實是一個樹幹加樹葉,然後我們要做的就是隨機生成樹葉在樹幹上,做成一個模型,這樣就有各種各樣的樹。
實現效果:
生成的模型以及mesh
實際效果:
Ps:實時陰影都是自己用shader實現的,沒有使用光源:可以參考我的上一篇文章~。
需要模型資源的 之後我會附上下載鏈接賺點積分~~,知道爲啥static不管用的可以告訴我,這樣可能就不需要自己去合併mesh了。
下載鏈接:https://download.csdn.net/download/qq_32225289/11336626