在Unity中使用工具生成GameObject节点的FullPath

GameObject上面右键生成配置信息

在Unity中使用工具生成GameObject节点的FullPath

生成CS文件

namespace Code.AutoGenerate { public class GUIPath { public class Demo { public const string NameTF = "a0"; public const string InfoBox = "a1"; public const string Info01 = "a2"; public const string InfoBox_Info01_MsgTF = "a3"; public const string InfoBox_Info01_Icon = "a4"; public const string InfoBox_Info02 = "a5"; public const string InfoBox_Info02_MsgTF = "a6"; public const string InfoBox_Info03_MsgTF = "a7"; } } }

运行时候通过生成的文件查找对应节点的FullPath

public class Test01 : MonoBehaviour { private void Start() { var go = transform.Find(GameGUI.FullPath(GUIPath.Demo.InfoBox_Info01_Icon)); var img = go.GetComponent<Image>(); img.color = Color.green; } } 问题

对于在项目中是否使用 public 属性然后在Inspector中进行赋值 的方式进行 GUI界面的制作.一直存在两个声音. 有人支持,有人反对.

各有各的道理吧,因为各自项目不同 所以也不好一概而论.

Private 赋值方式的优点

就我个人而言,目前来说 我还是比较倾向于用 private 声明然后再通过transform.Find()方式赋值的方法.

我认为这种方法有几个优点

对混淆友好: 以Inspector进行拖拽赋值的方法 肯定是无法混淆的

抗灾能力强: 尤其是对于研发周期在半年以上的中大型项目,且是多人同时提交代码. 当出现一两次Missing Reference的事故时候,你会更加深入理解这个问题的:)

易于模块化开发: 对于复杂的面板,我一般会拆分多个组件,然后主GUI上挂载Manger类进行其内部组件的协调. 如果采用public赋值方法,所有组件的变量都需要在Manger类内部进行引用切赋值.导致这个类内部很乱.并且所有子组件都需要和Manger进行交叉引用.增大了组件之间的耦合度.

Private 赋值方式的缺点

缺点就我现在的体会来说 就是觉得赋值很麻烦.

在Unity中使用工具生成GameObject节点的FullPath

比如像上面这段,编写期间要不停的Unity和Rider之间切换然后Copy字符串. 并且如果出现结构上的重构时候.也很麻烦.

解决方案

我还是按照之前处理在Unity项目中隐藏StringConst方式一样.

写GUI工具,同时生成一份CS文件和一个二进制文件.

然后代码中直接通过CS文件中定义的Key去找二进制文件所放置的FullPath即可

核心代码 将工具入口放置在右键菜单中 [MenuItem("GameObject/UI/${你自定义的子目录}")]

详情参考Unity Editor Extensions – Menu Items

使用内部类模板 namespace Code.AutoGenerate { public class GUIPath { public class Demo { public const string NameTF = "a0"; public const string InfoBox = "a1"; public const string Info01 = "a2"; public const string InfoBox_Info01_MsgTF = "a3"; public const string InfoBox_Info01_Icon = "a4"; public const string InfoBox_Info02 = "a5"; public const string InfoBox_Info02_MsgTF = "a6"; public const string InfoBox_Info03_MsgTF = "a7"; } public class Demo_2 { public const string NameTF = "a8"; public const string InfoBox = "a9"; public const string Info01 = "a10"; public const string InfoBox_Info01_MsgTF = "a11"; public const string InfoBox_Info01_Icon = "a12"; public const string InfoBox_Info02 = "a13"; public const string InfoBox_Info02_MsgTF = "a14"; public const string InfoBox_Info03_MsgTF = "a15"; } } }

以需要构建的GameObject的Name作为内部类的类名去构建自动生成的CS文件. 这样在代码中会更加清晰.

在Unity中使用工具生成GameObject节点的FullPath

使用名称约束

在Unity中使用工具生成GameObject节点的FullPath

以S_开头的节点会生成短名称,比如上图的InfoBox

以F_开头的节点会生成长名称(用以区分同名节点).比如上图的InfoBox_Info01_MsgTF

不以S_和F_开头的节点则不导出.(代码中不会直接操作该节点)

遍历一个GameObject取得其FullPath private GUIFullPathNode GetNewNode(GameObject _go) { var rootNode = new GUIFullPathNode(); rootNode.nodeList = new List<GUIFullPathGameObjectWrap>(); LoopCreateFullPath(_go.transform, _go.transform, rootNode); return rootNode; } private void LoopCreateFullPath(Transform _currentTrans, Transform _rootTrans, GUIFullPathNode _rootNode) { var nodeName = _currentTrans.name; if (nodeName.StartsWith(SHORT_NAME_PR) || nodeName.StartsWith(FULL_NAME_PR)) { var path = AnimationUtility.CalculateTransformPath(_currentTrans, _rootTrans); if (!string.IsNullOrEmpty(path)) { var goWarp = new GUIFullPathGameObjectWrap(); goWarp.fullPath = path; goWarp.realKey = GetFullPathNodeRealName(path, _currentTrans.name); _rootNode.nodeList.Add(goWarp); Debug.Log($"Go {_currentTrans.name} ==>> RealName: {goWarp.realKey} Path: {goWarp.fullPath} "); } } var childNum = _currentTrans.childCount; for (var i = 0; i < childNum; i++) { LoopCreateFullPath(_currentTrans.GetChild(i), _rootTrans, _rootNode); } } private string GetFullPathNodeRealName(string _path, string _nodeName) { var splitStringList = _path.Split('http://www.likecs.com/'); if (_nodeName.StartsWith(SHORT_NAME_PR)) { return DoGetRealName(splitStringList[splitStringList.Length - 1]); } var realName = ""; for (var i = 0; i < splitStringList.Length; i++) { realName += DoGetRealName(splitStringList[i]) + "_"; } //去掉尾部的"_" return realName.Substring(0, realName.Length - 1); } private string DoGetRealName(string _name) { if (_name.StartsWith(SHORT_NAME_PR) || _name.StartsWith(FULL_NAME_PR)) { return _name.Substring(2, _name.Length - 2); } return _name; }

这部分代码同时也包括了对S_和F_开头节点的解析. 其核心的遍历逻辑是调用

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpygjd.html