Unity 自定义工具之 预制体的Layer查找/替换工具

目录

1.工具制作的起因

2.工具的制作

(1).首先在Assets->Editor 目录下创建脚本

(2).创建一个工具窗口

(3).在OnGUI()中draw出来一些按钮/label 等

(4).查询方法

(5).替换方法

3.效果展示:

4.拓展:

5.完整代码:


1.工具制作的起因

现在大多数公司做游戏为了压缩时间和成本,基本都会直接拿以前或者是直接从网上找一些开源的游戏代码,在此基础上进行二次创作,在这种代码环境下做一些新的逻辑,难免会遇到一些困难,比如:

我在开发过程中需要加一个新的layer层来完成我的新代码逻辑 ,但是发现旧的代码已经把全部的layer用掉了我们都知道layer层是没办法添加的。
不明白为什么不能添加的同学可以看下:

Unity layer的介绍与使用以及只有32层的原因

因此我就只能写一个查找预制体layer的工具 以此来找到使用次数最少的layer层,以此来减轻更改layer层对原逻辑的影响。

2.工具的制作

(1).首先在Assets->Editor 目录下创建脚本

(2).创建一个工具窗口

代码如下:

public class ReadOrWriteLayer : EditorWindow{
static string checkPath;
[MenuItem("SelfTools / 打开layer查找窗口")]
public static void OpenWindow() {
        ReadOrWriteLayer readOrWriteLayer = EditorWindow.GetWindow<ReadOrWriteLayer>();
        readOrWriteLayer.Show();
        checkPath = "Assets";
    }
}

其中 :ReadOrWriteLayer 是创建类名
            checkPath 是查找的路径
注意:我们的类要继承EditorWindow

(3).在OnGUI()中draw出来一些按钮/label 等
 

代码如下: 

        GUILayout.BeginHorizontal();
        checkPath = EditorGUILayout.TextField("输入当前查找/替换的路径:", checkPath);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

动态修改checkPath 即 修改查找或者替换的路径 

        GUILayout.BeginHorizontal();
        chechLayer = EditorGUILayout.TextField("输入当前查找的layer层:", chechLayer);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        replaceLayer = EditorGUILayout.TextField("输入替换的layer层:", replaceLayer);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

分别输入要查找的layer层和要替换的layer层其中 chechLayer是要查找的layer层 replaceLayer是要替换成的layer层 

GUILayout.BeginHorizontal();
        if(GUILayout.Button("开始查找", "buttonleft", GUILayout.Width(250),            GUILayout.Height(50))) {
            if(!int.TryParse(chechLayer, out layer)) {
                chechLayer = "";
            } else
                CheckAllGameObjectByLayer();
        }

GUILayout.EndHorizontal();
GUILayout.Space(10); 

添加按钮
点击后执行查找方法 CheckAllGameObjectByLayer(); 检查输出所有查询的layer预制体路径和子节点路径

GUILayout.BeginHorizontal();
        if(GUILayout.Button("开始替换", "buttonleft", GUILayout.Width(250), GUILayout.Height(50))) {
            if(!int.TryParse(chechLayer, out layer) || !int.TryParse(replaceLayer, out replacelayer)) {
                chechLayer = "";
            } else
                ReplaceAllGameObjectLayer();
        }
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

添加按钮
点击后执行查找方法 ReplaceAllGameObjectLayer(); 替换所有查询的layer层为替换layer层

(4).查询方法

private void CheckAllGameObjectByLayer() {
    //写入进度条的当前值
        startVal = 0f;
        //输出文件保存路径
        savePath = Application.dataPath + "/Editor/LayerSetting.text";
        if(File.Exists(savePath))
            File.Delete(savePath);
        string allStr = "";
        //查找checkPath路径下的所有文件路径
        string[] allPath = Directory.GetFiles(checkPath, "*.prefab", SearchOption.AllDirectories);
        maxValue = allPath.Length;
        if(allPath.Length > 0) CreateFile();
        //把预制体和预制体里面的子物体写入到文件LayerSetting.text中
        using(FileStream fs = new FileStream(savePath, FileMode.Open)) {
            StreamWriter sw = new StreamWriter(new BufferedStream(fs), Encoding.UTF8);
            for(int i = 0; i < allPath.Length; i++) {
                if(File.Exists(allPath[i])) {
                    //通过路径获取游戏obj
                    GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(allPath[i]);
 		      if (obj == null) continue;
                    if(obj.layer == layer) {
                        allStr += string.Format("{0}\r\n", allPath[i]);
                        IterationAllGameObject(obj.transform, obj.name, ref allStr);
                        allStr += "\r\n";
                    } else {
                        IterationAllGameObject(obj.transform, obj.name, ref allStr);
                    }
                }
                startVal = i + 1;
                //刷新当前读取进度
                float slider = (float)startVal / maxValue;
                //参数1 为标题,参数2为提示,参数3为 进度百分比 0~1 之间
                EditorUtility.DisplayProgressBar("Loading", "写入中......", slider);
                //写入文件
                sw.Write(allStr);
                allStr = "";
            }
            sw.Close();
            fs.Close();
            fs.Dispose();
        }
        //全部结束关闭进度条
        EditorUtility.ClearProgressBar();
    }

   //如果保存文件不存在 创建一个
   private static void CreateFile() {
        if(!File.Exists(savePath)) {
            FileStream file = new FileStream(savePath, FileMode.Create);
            file.Dispose();
        }
    }

    //迭代obj的所有子物体
     private static void IterationAllGameObject(Transform obj, string path, ref string allStr, bool       writeLayer = false) {
        for(int i = 0; i < obj.childCount; i++) {
            GameObject child = obj.GetChild(i).gameObject;
            string realPath = path;
            if(!writeLayer) {
                if(child.layer == layer) {
                    realPath += string.Format("->{0}\r\n", child.name);
                    allStr += realPath;
                }
            } else {
                realPath += string.Format("->{0}    ::Layer -> {1}\r\n", child.name, child.layer);
                allStr += realPath;
            }

            string namePath = string.Format("{0} ->{1}", path, child.name);
            IterationAllGameObject(obj.GetChild(i), namePath, ref allStr, writeLayer);
        }
    }

(5).替换方法

private void ReplaceAllGameObjectLayer(bool checkLayer = true) {
        startVal = 0f;
        string[] allPath = Directory.GetFiles(checkPath, "*.prefab", SearchOption.AllDirectories);
        maxValue = allPath.Length;
        for(int i = 0; i < allPath.Length; i++) {
            if(File.Exists(allPath[i])) {
                GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(allPath[i]);
	         if (obj == null) continue;
                if(obj.layer == layer) {
                    obj.layer = replacelayer;
                } else if(!checkLayer) {
                    obj.layer = replacelayer;
                }
                IterationReplace(obj.transform, checkLayer);
            }
            startVal = i + 1;
            float slider = (float)startVal / maxValue;
            //参数1 为标题,参数2为提示,参数3为 进度百分比 0~1 之间
            EditorUtility.DisplayProgressBar("Loading", "写入中......", slider);
        }
        EditorUtility.ClearProgressBar();
        //保存全部修改
        AssetDatabase.SaveAssets();
    }

    private static void IterationReplace(Transform obj, bool checkLayer = true) {
        for(int i = 0; i < obj.childCount; i++) {
            GameObject child = obj.GetChild(i).gameObject;
            if(child.layer == layer) {
                child.layer = replacelayer;
            } else if(!checkLayer)
                child.layer = replacelayer;

            IterationReplace(obj.GetChild(i), checkLayer);
        }
    }

至此一个完整的layer层查询/替换工具诞生了

3.效果展示:


这个文件里就是查询的layer层的所有预制体的路径信息

点击替换 输入替换layer层就可以把文件中的所有预制体都替换掉

4.拓展:

由于每次都要手动填写查询目录 我觉得十分烦 想到unity有一个选中逻辑 于是添加了一条鼠标选中后路径更新
代码如下:

    static bool isRun = false;
    bool toggle;

    GUILayout.BeginHorizontal();
        toggle = EditorGUILayout.BeginToggleGroup("选择路径", toggle);
        if(Selection.assetGUIDs.Length > 0 && !isRun && toggle) {
            string selectPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            checkPath = selectPath;
        }
        EditorGUILayout.EndToggleGroup();
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

另外在替换或者查询中关闭isRun
效果如下:

勾选选择路径之后 查找/替换路径会自动更换成鼠标选择的路径
注:鼠标选择之后需要移到此面板 此面板才会动态更新路径

5.完整代码:

using System.IO;
using System.Text;
using UnityEditor;
using UnityEngine;

public class ReadOrWriteLayer : EditorWindow {
    static string checkPath;
    static string savePath;

    string chechLayer;
    string replaceLayer;

    static int layer;
    static int replacelayer;

    float startVal;
    float maxValue;

    bool toggle;
    static bool isRun = false;

    [MenuItem("SelfTools / 打开layer查找窗口")]
    public static void OpenWindow() {
        ReadOrWriteLayer readOrWriteLayer = EditorWindow.GetWindow<ReadOrWriteLayer>();
        readOrWriteLayer.Show();
        checkPath = "Assets";
    }
    void  OnGUI() {

        GUILayout.BeginHorizontal();
        toggle = EditorGUILayout.BeginToggleGroup("选择路径", toggle);
        if (Selection.assetGUIDs.Length > 0 && !isRun && toggle)
        {
            string selectPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]);
            checkPath = selectPath;
        }
        EditorGUILayout.EndToggleGroup();
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        checkPath = EditorGUILayout.TextField("输入当前查找/替换的路径:", checkPath);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        chechLayer = EditorGUILayout.TextField("输入当前查找的layer层:", chechLayer);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        replaceLayer = EditorGUILayout.TextField("输入替换的layer层:", replaceLayer);
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        if(GUILayout.Button("开始查找", "buttonleft", GUILayout.Width(250), GUILayout.Height(50))) {
            if(!int.TryParse(chechLayer, out layer)) {
                chechLayer = "";
            } else
                CheckAllGameObjectByLayer();
        }
        GUILayout.EndHorizontal();
        GUILayout.Space(10);

        GUILayout.BeginHorizontal();
        if(GUILayout.Button("开始替换", "buttonleft", GUILayout.Width(250), GUILayout.Height(50))) {
            if(!int.TryParse(chechLayer, out layer) || !int.TryParse(replaceLayer, out replacelayer)) {
                chechLayer = "";
            } else
                ReplaceAllGameObjectLayer();
        }
        GUILayout.EndHorizontal();
        GUILayout.Space(10);
    }


    private void CheckAllGameObjectByLayer() {
        isRun = true;
        //写入进度条的当前值
        startVal = 0f;
        //输出文件保存路径
        savePath = Application.dataPath + "/Editor/LayerSetting.text";
        if(File.Exists(savePath))
            File.Delete(savePath);
        string allStr = "";
        //查找checkPath路径下的所有文件路径
        string[] allPath = Directory.GetFiles(checkPath, "*.prefab", SearchOption.AllDirectories);
        maxValue = allPath.Length;
        if(allPath.Length > 0) CreateFile();
        //把预制体和预制体里面的子物体写入到文件LayerSetting.text中
        using(FileStream fs = new FileStream(savePath, FileMode.Open)) {
            StreamWriter sw = new StreamWriter(new BufferedStream(fs), Encoding.UTF8);
            for(int i = 0; i < allPath.Length; i++) {
                if(File.Exists(allPath[i])) {
                    //通过路径获取游戏obj
                    GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(allPath[i]);
                    if (obj == null) continue;
                    if(obj.layer == layer) {
                        allStr += string.Format("{0}\r\n", allPath[i]);
                        IterationAllGameObject(obj.transform, obj.name, ref allStr);
                        allStr += "\r\n";
                    } else {
                        IterationAllGameObject(obj.transform, obj.name, ref allStr);
                    }
                }
                startVal = i + 1;
                //刷新当前读取进度
                float slider = (float)startVal / maxValue;
                //参数1 为标题,参数2为提示,参数3为 进度百分比 0~1 之间
                EditorUtility.DisplayProgressBar("Loading", "写入中......", slider);
                //写入文件
                sw.Write(allStr);
                allStr = "";
            }
            sw.Close();
            fs.Close();
            fs.Dispose();
        }
        //全部结束关闭进度条
        EditorUtility.ClearProgressBar();
        isRun = false;
    }

    //如果保存文件不存在 创建一个
    private static void CreateFile() {
        if(!File.Exists(savePath)) {
            FileStream file = new FileStream(savePath, FileMode.Create);
            file.Dispose();
        }
    }

    //迭代obj的所有子物体
    private static void IterationAllGameObject(Transform obj, string path, ref string allStr, bool writeLayer = false) {
        for(int i = 0; i < obj.childCount; i++) {
            GameObject child = obj.GetChild(i).gameObject;
            string realPath = path;
            if(!writeLayer) {
                if(child.layer == layer) {
                    realPath += string.Format("->{0}\r\n", child.name);
                    allStr += realPath;
                }
            } else {
                realPath += string.Format("->{0}    ::Layer -> {1}\r\n", child.name, child.layer);
                allStr += realPath;
            }

            string namePath = string.Format("{0} ->{1}", path, child.name);
            IterationAllGameObject(obj.GetChild(i), namePath, ref allStr, writeLayer);
        }
    }

    private void ReplaceAllGameObjectLayer(bool checkLayer = true) {
        isRun = true;
        startVal = 0f;
        string[] allPath = Directory.GetFiles(checkPath, "*.prefab", SearchOption.AllDirectories);
        maxValue = allPath.Length;
        for(int i = 0; i < allPath.Length; i++) {
            if(File.Exists(allPath[i])) {
                GameObject obj = AssetDatabase.LoadAssetAtPath<GameObject>(allPath[i]);
                if (obj == null) continue;
                if (obj.layer == layer) {
                    obj.layer = replacelayer;
                } else if(!checkLayer) {
                    obj.layer = replacelayer;
                }
                IterationReplace(obj.transform, checkLayer);
            }
            startVal = i + 1;
            float slider = (float)startVal / maxValue;
            //参数1 为标题,参数2为提示,参数3为 进度百分比 0~1 之间
            EditorUtility.DisplayProgressBar("Loading", "写入中......", slider);
        }
        EditorUtility.ClearProgressBar();
        //保存全部修改
        AssetDatabase.SaveAssets();

        isRun = false;
    }

    private static void IterationReplace(Transform obj, bool checkLayer = true) {
        for(int i = 0; i < obj.childCount; i++) {
            GameObject child = obj.GetChild(i).gameObject;
            if(child.layer == layer) {
                child.layer = replacelayer;
            } else if(!checkLayer)
                child.layer = replacelayer;

            IterationReplace(obj.GetChild(i), checkLayer);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一梭键盘任平生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值