IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> Unity 自定义工具之 预制体的Layer查找/替换工具 -> 正文阅读

[游戏开发]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);
        }
    }
}
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-09-15 02:18:45  更:2022-09-15 02:19:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/17 3:41:30-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码