Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / generator / UiGenerator.cs
@lijincheng lijincheng on 18 Mar 2023 8 KB EditorTools 显示 tips
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.IO;
  5. using System.Text.RegularExpressions;
  6. using Godot;
  7.  
  8. namespace Generator;
  9.  
  10. /// <summary>
  11. /// Ui类生成器
  12. /// </summary>
  13. public static class UiGenerator
  14. {
  15. private static Dictionary<string, int> _nodeNameMap = new Dictionary<string, int>();
  16.  
  17. /// <summary>
  18. /// 根据指定ui节点生成相应的Ui类, 并保存到指定路径下
  19. /// </summary>
  20. public static void GenerateUi(Node control, string path)
  21. {
  22. _nodeNameMap.Clear();
  23. var uiNode = EachNode(control);
  24. var code = GenerateClassCode(uiNode);
  25. File.WriteAllText(path, code);
  26. }
  27.  
  28. /// <summary>
  29. /// 从编辑器中生成ui代码
  30. /// </summary>
  31. public static void GenerateUiFromEditor(Node control, string path)
  32. {
  33. _nodeNameMap.Clear();
  34. var uiNode = EachNodeFromEditor(control);
  35. var code = GenerateClassCode(uiNode);
  36. File.WriteAllText(path, code);
  37. }
  38.  
  39. private static string GenerateClassCode(UiNodeInfo uiNodeInfo)
  40. {
  41. return $"namespace UI.{uiNodeInfo.OriginName};\n\n" +
  42. $"/// <summary>\n" +
  43. $"/// Ui代码, 该类是根据ui场景自动生成的, 请不要手动编辑该类, 以免造成代码丢失\n" +
  44. $"/// </summary>\n" +
  45. $"public abstract partial class {uiNodeInfo.OriginName} : UiBase\n" +
  46. $"{{\n" +
  47. GeneratePropertyListClassCode("", uiNodeInfo.OriginName + ".", uiNodeInfo, " ") +
  48. $"\n\n" +
  49. GenerateAllChildrenClassCode(uiNodeInfo.OriginName + ".", uiNodeInfo, " ") +
  50. $"}}\n";
  51. }
  52.  
  53. private static string GenerateAllChildrenClassCode(string parent, UiNodeInfo uiNodeInfo, string retraction)
  54. {
  55. var str = "";
  56. if (uiNodeInfo.Children != null)
  57. {
  58. for (var i = 0; i < uiNodeInfo.Children.Count; i++)
  59. {
  60. var item = uiNodeInfo.Children[i];
  61. str += GenerateAllChildrenClassCode(parent + item.OriginName + ".", item, retraction);
  62. str += GenerateChildrenClassCode(parent, item, retraction);
  63. }
  64. }
  65.  
  66. return str;
  67. }
  68. private static string GenerateChildrenClassCode(string parent, UiNodeInfo uiNodeInfo, string retraction)
  69. {
  70. return retraction + $"/// <summary>\n" +
  71. retraction + $"/// 类型: <see cref=\"{uiNodeInfo.TypeName}\"/>, 路径: {parent}{uiNodeInfo.OriginName}\n" +
  72. retraction + $"/// </summary>\n" +
  73. retraction + $"public class {uiNodeInfo.ClassName} : IUiNode<{uiNodeInfo.TypeName}, {uiNodeInfo.ClassName}>\n" +
  74. retraction + $"{{\n" +
  75. GeneratePropertyListClassCode("Instance.", parent, uiNodeInfo, retraction + " ") +
  76. retraction + $" public {uiNodeInfo.ClassName}({uiNodeInfo.TypeName} node) : base(node) {{ }}\n" +
  77. retraction + $" public override {uiNodeInfo.ClassName} Clone() => new (({uiNodeInfo.TypeName})Instance.Duplicate());\n" +
  78. retraction + $"}}\n\n";
  79. }
  80.  
  81. private static string GeneratePropertyListClassCode(string target, string parent, UiNodeInfo uiNodeInfo, string retraction)
  82. {
  83. var str = "";
  84. if (uiNodeInfo.Children != null)
  85. {
  86. for (var i = 0; i < uiNodeInfo.Children.Count; i++)
  87. {
  88. var item = uiNodeInfo.Children[i];
  89. str += GeneratePropertyCode(target, parent, item, retraction);
  90. }
  91. }
  92.  
  93. return str;
  94. }
  95. private static string GeneratePropertyCode(string target, string parent, UiNodeInfo uiNodeInfo, string retraction)
  96. {
  97. return retraction + $"/// <summary>\n" +
  98. retraction + $"/// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref=\"{uiNodeInfo.TypeName}\"/>, 节点路径: {parent}{uiNodeInfo.OriginName}\n" +
  99. retraction + $"/// </summary>\n" +
  100. retraction + $"public {uiNodeInfo.ClassName} {uiNodeInfo.Name}\n" +
  101. retraction + $"{{\n" +
  102. retraction + $" get\n" +
  103. retraction + $" {{\n" +
  104. retraction + $" if (_{uiNodeInfo.Name} == null) _{uiNodeInfo.Name} = new {uiNodeInfo.ClassName}({target}GetNodeOrNull<{uiNodeInfo.TypeName}>(\"{uiNodeInfo.OriginName}\"));\n" +
  105. retraction + $" return _{uiNodeInfo.Name};\n" +
  106. retraction + $" }}\n" +
  107. retraction + $"}}\n" +
  108. retraction + $"private {uiNodeInfo.ClassName} _{uiNodeInfo.Name};\n\n";
  109. }
  110. /// <summary>
  111. /// 递归解析节点, 并生成 UiNodeInfo 数据
  112. /// </summary>
  113. private static UiNodeInfo EachNode(Node node)
  114. {
  115. var originName = Regex.Replace(node.Name, "[^\\w]", "");
  116. //类定义该图层的类名
  117. string className;
  118. if (_nodeNameMap.ContainsKey(originName)) //有同名图层, 为了防止类名冲突, 需要在 UiNode 后面加上索引
  119. {
  120. var count = _nodeNameMap[originName];
  121. className = "UiNode" + (count) + "_" + originName;
  122. _nodeNameMap[originName] = count + 1;
  123. }
  124. else
  125. {
  126. className = "UiNode" + "_" + originName;
  127. _nodeNameMap.Add(originName, 1);
  128. }
  129. var uiNode = new UiNodeInfo("L_" + originName, originName, className, node.GetType().FullName);
  130.  
  131. var childCount = node.GetChildCount();
  132. if (childCount > 0)
  133. {
  134. for (var i = 0; i < childCount; i++)
  135. {
  136. var children = node.GetChild(i);
  137. if (children != null)
  138. {
  139. if (uiNode.Children == null)
  140. {
  141. uiNode.Children = new List<UiNodeInfo>();
  142. }
  143.  
  144. uiNode.Children.Add(EachNode(children));
  145. }
  146. }
  147. }
  148.  
  149. return uiNode;
  150. }
  151.  
  152. /// <summary>
  153. /// 在编辑器下递归解析节点, 由于编辑器下绑定用户脚本的节点无法直接判断节点类型, 那么就只能获取节点的脚本然后解析名称和命名空间
  154. /// </summary>
  155. private static UiNodeInfo EachNodeFromEditor(Node node)
  156. {
  157. UiNodeInfo uiNode;
  158. //原名称
  159. var originName = Regex.Replace(node.Name, "[^\\w]", "");
  160. //字段名称
  161. var fieldName = "L_" + originName;
  162. //类定义该图层的类名
  163. string className;
  164. if (_nodeNameMap.ContainsKey(originName)) //有同名图层, 为了防止类名冲突, 需要在 UiNode 后面加上索引
  165. {
  166. var count = _nodeNameMap[originName];
  167. className = "UiNode" + (count) + "_" + originName;
  168. _nodeNameMap[originName] = count + 1;
  169. }
  170. else
  171. {
  172. className = "UiNode" + "_" + originName;
  173. _nodeNameMap.Add(originName, 1);
  174. }
  175.  
  176. var script = node.GetScript().As<CSharpScript>();
  177. if (script == null) //无脚本, 说明是内置节点
  178. {
  179. uiNode = new UiNodeInfo(fieldName, originName, className, node.GetType().FullName);
  180. }
  181. else //存在脚本
  182. {
  183. var index = script.ResourcePath.LastIndexOf("/", StringComparison.Ordinal);
  184. //文件名称
  185. var fileName = script.ResourcePath.Substring(index + 1, script.ResourcePath.Length - index - 3 - 1);
  186. //在源代码中寻找命名空间
  187. var match = Regex.Match(script.SourceCode, "(?<=namespace\\s+)[\\w\\.]+");
  188. if (match.Success) //存在命名空间
  189. {
  190. uiNode = new UiNodeInfo(fieldName, originName, className, match.Value + "." + fileName);
  191. }
  192. else //不存在命名空间
  193. {
  194. uiNode = new UiNodeInfo(fieldName, originName, className, fileName);
  195. }
  196. }
  197. var childCount = node.GetChildCount();
  198. if (childCount > 0)
  199. {
  200. for (var i = 0; i < childCount; i++)
  201. {
  202. var children = node.GetChild(i);
  203. if (children != null)
  204. {
  205. if (uiNode.Children == null)
  206. {
  207. uiNode.Children = new List<UiNodeInfo>();
  208. }
  209.  
  210. uiNode.Children.Add(EachNodeFromEditor(children));
  211. }
  212. }
  213. }
  214. return uiNode;
  215. }
  216.  
  217. private class UiNodeInfo
  218. {
  219. public string Name;
  220. public string OriginName;
  221. public string ClassName;
  222. public string TypeName;
  223. public List<UiNodeInfo> Children;
  224.  
  225. public UiNodeInfo(string name, string originName, string className, string typeName)
  226. {
  227. Name = name;
  228. OriginName = originName;
  229. ClassName = className;
  230. TypeName = typeName;
  231. }
  232. }
  233. }