Unity 自动制作LowPoly随机形态的树预制体工具

通过一个树干模型和树叶模型 随机制作不同的预制体

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;
            });
        }

接下来展示实际的效果:

树的模板:

Unity 自动制作LowPoly随机形态的树预制体工具_第1张图片

Unity 自动制作LowPoly随机形态的树预制体工具_第2张图片

其实是一个树干加树叶,然后我们要做的就是随机生成树叶在树干上,做成一个模型,这样就有各种各样的树。

实现效果:

Unity 自动制作LowPoly随机形态的树预制体工具_第3张图片

生成的模型以及mesh

Unity 自动制作LowPoly随机形态的树预制体工具_第4张图片

Unity 自动制作LowPoly随机形态的树预制体工具_第5张图片

实际效果:

Unity 自动制作LowPoly随机形态的树预制体工具_第6张图片

 

Ps:实时阴影都是自己用shader实现的,没有使用光源:可以参考我的上一篇文章~。

需要模型资源的 之后我会附上下载链接赚点积分~~,知道为啥static不管用的可以告诉我,这样可能就不需要自己去合并mesh了。

下载链接:https://download.csdn.net/download/qq_32225289/11336626

你可能感兴趣的:(Unity)