通过一个树干模型和树叶模型 随机制作不同的预制体
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();
bc.isTrigger = true;
bc.center = new Vector3(0, 1.31f, 0);
bc.size = new Vector3(1, 4, 1);
go.AddMissingComponent();
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())
{
tSelect.gameObject.AddComponent();
}
if (!tSelect.GetComponent())
{
tSelect.gameObject.AddComponent();
}
MeshFilter[] tFilters = tSelect.GetComponentsInChildren();
//根据所有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().sharedMesh = tFinalMesh;
//赋值新的材质
tSelect.GetComponent().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().sharedMesh;
string path = Path.Combine(fullPath, go.name + ".asset");
AssetDatabase.CreateAsset(mesh, path);
AssetDatabase.Refresh();
EditorSceneManager.MarkAllScenesDirty();
}
}
}
附上IterateGameObject方法:
///
/// 遍历go c
///
///
///
static public void IterateGameObject(this GameObject go, Action 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);
}
}
}
///
/// 设置go层级关系 c
///
///
///
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