Newer
Older
DungeonShooting / DungeonShooting_Godot / src / framework / generator / UiGenerator.cs
@小李xl 小李xl on 6 Jul 2023 12 KB 解决导出exe编译报错问题
  1. #if TOOLS
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Text.RegularExpressions;
  7. using Godot;
  8.  
  9. namespace Generator;
  10.  
  11. /// <summary>
  12. /// Ui类生成器
  13. /// </summary>
  14. public static class UiGenerator
  15. {
  16. private static Dictionary<string, int> _nodeNameMap = new Dictionary<string, int>();
  17.  
  18. /// <summary>
  19. /// 根据名称在编辑器中创建Ui, open 表示创建完成后是否在编辑器中打开这个ui
  20. /// </summary>
  21. public static bool CreateUi(string uiName, bool open = false)
  22. {
  23. try
  24. {
  25. //创建脚本代码
  26. var scriptPath = GameConfig.UiCodeDir + uiName.FirstToLower();
  27. var scriptFile = scriptPath + "/" + uiName + "Panel.cs";
  28. var scriptCode = $"using Godot;\n" +
  29. $"\n" +
  30. $"namespace UI.{uiName};\n" +
  31. $"\n" +
  32. $"public partial class {uiName}Panel : {uiName}\n" +
  33. $"{{\n" +
  34. $"\n" +
  35. $" public override void OnShowUi()\n" +
  36. $" {{\n" +
  37. $" \n" +
  38. $" }}\n" +
  39. $"\n" +
  40. $" public override void OnHideUi()\n" +
  41. $" {{\n" +
  42. $" \n" +
  43. $" }}\n" +
  44. $"\n" +
  45. $"}}\n";
  46. if (!Directory.Exists(scriptPath))
  47. {
  48. Directory.CreateDirectory(scriptPath);
  49. }
  50. File.WriteAllText(scriptFile, scriptCode);
  51.  
  52. //加载脚本资源
  53. var scriptRes = GD.Load<CSharpScript>("res://" + scriptFile);
  54.  
  55. //创建场景资源
  56. var prefabFile = GameConfig.UiPrefabDir + uiName + ".tscn";
  57. var prefabResPath = "res://" + prefabFile;
  58. if (!Directory.Exists(GameConfig.UiPrefabDir))
  59. {
  60. Directory.CreateDirectory(GameConfig.UiPrefabDir);
  61. }
  62. var uiNode = new Control();
  63. uiNode.Name = uiName;
  64. uiNode.SetAnchorsPreset(Control.LayoutPreset.FullRect, true);
  65. uiNode.SetScript(scriptRes);
  66. var scene = new PackedScene();
  67. scene.Pack(uiNode);
  68. ResourceSaver.Save(scene, prefabResPath);
  69. //生成Ui结构代码
  70. GenerateUiCode(uiNode, scriptPath + "/" + uiName + ".cs");
  71.  
  72. //生成 ResourcePath.cs 代码
  73. ResourcePathGenerator.Generate();
  74. //生成 UiManager_Methods.cs 代码
  75. UiManagerMethodsGenerator.Generate();
  76.  
  77. //打开ui
  78. if (open)
  79. {
  80. Plugin.Plugin.Instance.GetEditorInterface().OpenSceneFromPath(prefabResPath);
  81. }
  82.  
  83. }
  84. catch (Exception e)
  85. {
  86. GD.PrintErr(e.ToString());
  87. return false;
  88. }
  89.  
  90. return true;
  91. }
  92.  
  93. /// <summary>
  94. /// 根据指定ui节点生成相应的Ui类, 并保存到指定路径下
  95. /// </summary>
  96. public static void GenerateUiCode(Node control, string path)
  97. {
  98. _nodeNameMap.Clear();
  99. var uiNode = EachNode(control);
  100. var code = GenerateClassCode(uiNode);
  101. File.WriteAllText(path, code);
  102. }
  103.  
  104. /// <summary>
  105. /// 从编辑器中生成ui代码
  106. /// </summary>
  107. public static bool GenerateUiCodeFromEditor(Node control)
  108. {
  109. try
  110. {
  111. _nodeNameMap.Clear();
  112. var uiName = control.Name.ToString();
  113. var path = GameConfig.UiCodeDir + uiName.FirstToLower() + "/" + uiName + ".cs";
  114. GD.Print("重新生成ui代码: " + path);
  115.  
  116. var uiNode = EachNodeFromEditor(control);
  117. var code = GenerateClassCode(uiNode);
  118. File.WriteAllText(path, code);
  119. }
  120. catch (Exception e)
  121. {
  122. GD.PrintErr(e.ToString());
  123. return false;
  124. }
  125.  
  126. return true;
  127. }
  128.  
  129. private static string GenerateClassCode(UiNodeInfo uiNodeInfo)
  130. {
  131. return $"namespace UI.{uiNodeInfo.OriginName};\n\n" +
  132. $"/// <summary>\n" +
  133. $"/// Ui代码, 该类是根据ui场景自动生成的, 请不要手动编辑该类, 以免造成代码丢失\n" +
  134. $"/// </summary>\n" +
  135. $"public abstract partial class {uiNodeInfo.OriginName} : UiBase\n" +
  136. $"{{\n" +
  137. GeneratePropertyListClassCode("", uiNodeInfo.OriginName + ".", uiNodeInfo, " ") +
  138. $"\n" +
  139. $" public {uiNodeInfo.OriginName}() : base(nameof({uiNodeInfo.OriginName}))\n" +
  140. $" {{\n" +
  141. $" }}\n" +
  142. $"\n" +
  143. GenerateAllChildrenClassCode(uiNodeInfo.OriginName + ".", uiNodeInfo, " ") +
  144. $"}}\n";
  145. }
  146.  
  147. private static string GenerateAllChildrenClassCode(string parent, UiNodeInfo uiNodeInfo, string retraction)
  148. {
  149. var str = "";
  150. if (uiNodeInfo.Children != null)
  151. {
  152. for (var i = 0; i < uiNodeInfo.Children.Count; i++)
  153. {
  154. var item = uiNodeInfo.Children[i];
  155. str += GenerateAllChildrenClassCode(parent + item.OriginName + ".", item, retraction);
  156. str += GenerateChildrenClassCode(parent, item, retraction);
  157. }
  158. }
  159.  
  160. return str;
  161. }
  162. private static string GenerateChildrenClassCode(string parent, UiNodeInfo uiNodeInfo, string retraction)
  163. {
  164. return retraction + $"/// <summary>\n" +
  165. retraction + $"/// 类型: <see cref=\"{uiNodeInfo.TypeName}\"/>, 路径: {parent}{uiNodeInfo.OriginName}\n" +
  166. retraction + $"/// </summary>\n" +
  167. retraction + $"public class {uiNodeInfo.ClassName} : IUiNode<{uiNodeInfo.TypeName}, {uiNodeInfo.ClassName}>\n" +
  168. retraction + $"{{\n" +
  169. GeneratePropertyListClassCode("Instance.", parent, uiNodeInfo, retraction + " ") +
  170. retraction + $" public {uiNodeInfo.ClassName}({uiNodeInfo.TypeName} node) : base(node) {{ }}\n" +
  171. retraction + $" public override {uiNodeInfo.ClassName} Clone() => new (({uiNodeInfo.TypeName})Instance.Duplicate());\n" +
  172. retraction + $"}}\n\n";
  173. }
  174.  
  175. private static string GeneratePropertyListClassCode(string target, string parent, UiNodeInfo uiNodeInfo, string retraction)
  176. {
  177. var str = "";
  178. if (uiNodeInfo.Children != null)
  179. {
  180. for (var i = 0; i < uiNodeInfo.Children.Count; i++)
  181. {
  182. var item = uiNodeInfo.Children[i];
  183. str += GeneratePropertyCode(target, parent, item, retraction);
  184. }
  185. }
  186.  
  187. return str;
  188. }
  189. private static string GeneratePropertyCode(string target, string parent, UiNodeInfo uiNodeInfo, string retraction)
  190. {
  191. return retraction + $"/// <summary>\n" +
  192. retraction + $"/// 使用 Instance 属性获取当前节点实例对象, 节点类型: <see cref=\"{uiNodeInfo.TypeName}\"/>, 节点路径: {parent}{uiNodeInfo.OriginName}\n" +
  193. retraction + $"/// </summary>\n" +
  194. retraction + $"public {uiNodeInfo.ClassName} {uiNodeInfo.Name}\n" +
  195. retraction + $"{{\n" +
  196. retraction + $" get\n" +
  197. retraction + $" {{\n" +
  198. retraction + $" if (_{uiNodeInfo.Name} == null) _{uiNodeInfo.Name} = new {uiNodeInfo.ClassName}({target}GetNodeOrNull<{uiNodeInfo.TypeName}>(\"{uiNodeInfo.OriginName}\"));\n" +
  199. retraction + $" return _{uiNodeInfo.Name};\n" +
  200. retraction + $" }}\n" +
  201. retraction + $"}}\n" +
  202. retraction + $"private {uiNodeInfo.ClassName} _{uiNodeInfo.Name};\n\n";
  203. }
  204. /// <summary>
  205. /// 递归解析节点, 并生成 UiNodeInfo 数据
  206. /// </summary>
  207. private static UiNodeInfo EachNode(Node node)
  208. {
  209. var originName = Regex.Replace(node.Name, "[^\\w]", "");
  210. //类定义该图层的类名
  211. string className;
  212. if (_nodeNameMap.ContainsKey(originName)) //有同名图层, 为了防止类名冲突, 需要在 UiNode 后面加上索引
  213. {
  214. var count = _nodeNameMap[originName];
  215. className = "UiNode" + (count) + "_" + originName;
  216. _nodeNameMap[originName] = count + 1;
  217. }
  218. else
  219. {
  220. className = "UiNode" + "_" + originName;
  221. _nodeNameMap.Add(originName, 1);
  222. }
  223. var uiNode = new UiNodeInfo("L_" + originName, originName, className, node.GetType().FullName);
  224.  
  225. var childCount = node.GetChildCount();
  226. if (childCount > 0)
  227. {
  228. for (var i = 0; i < childCount; i++)
  229. {
  230. var children = node.GetChild(i);
  231. if (children != null)
  232. {
  233. if (uiNode.Children == null)
  234. {
  235. uiNode.Children = new List<UiNodeInfo>();
  236. }
  237.  
  238. uiNode.Children.Add(EachNode(children));
  239. }
  240. }
  241. }
  242.  
  243. return uiNode;
  244. }
  245.  
  246. /// <summary>
  247. /// 在编辑器下递归解析节点, 由于编辑器下绑定用户脚本的节点无法直接判断节点类型, 那么就只能获取节点的脚本然后解析名称和命名空间
  248. /// </summary>
  249. private static UiNodeInfo EachNodeFromEditor(Node node)
  250. {
  251. UiNodeInfo uiNode;
  252. //原名称
  253. var originName = Regex.Replace(node.Name, "[^\\w]", "");
  254. //字段名称
  255. var fieldName = "L_" + originName;
  256. //类定义该图层的类名
  257. string className;
  258. if (_nodeNameMap.ContainsKey(originName)) //有同名图层, 为了防止类名冲突, 需要在 UiNode 后面加上索引
  259. {
  260. var count = _nodeNameMap[originName];
  261. className = "UiNode" + (count) + "_" + originName;
  262. _nodeNameMap[originName] = count + 1;
  263. }
  264. else
  265. {
  266. className = "UiNode" + "_" + originName;
  267. _nodeNameMap.Add(originName, 1);
  268. }
  269.  
  270. var script = node.GetScript().As<CSharpScript>();
  271. if (script == null) //无脚本, 说明是内置节点
  272. {
  273. uiNode = new UiNodeInfo(fieldName, originName, className, node.GetType().FullName);
  274. }
  275. else //存在脚本
  276. {
  277. var index = script.ResourcePath.LastIndexOf("/", StringComparison.Ordinal);
  278. //文件名称
  279. var fileName = script.ResourcePath.Substring(index + 1, script.ResourcePath.Length - index - 3 - 1);
  280. //在源代码中寻找命名空间
  281. var match = Regex.Match(script.SourceCode, "(?<=namespace\\s+)[\\w\\.]+");
  282. if (match.Success) //存在命名空间
  283. {
  284. uiNode = new UiNodeInfo(fieldName, originName, className, match.Value + "." + fileName);
  285. }
  286. else //不存在命名空间
  287. {
  288. uiNode = new UiNodeInfo(fieldName, originName, className, fileName);
  289. }
  290. }
  291. var childCount = node.GetChildCount();
  292. if (childCount > 0)
  293. {
  294. for (var i = 0; i < childCount; i++)
  295. {
  296. var children = node.GetChild(i);
  297. if (children != null)
  298. {
  299. if (uiNode.Children == null)
  300. {
  301. uiNode.Children = new List<UiNodeInfo>();
  302. }
  303.  
  304. uiNode.Children.Add(EachNodeFromEditor(children));
  305. }
  306. }
  307. }
  308. return uiNode;
  309. }
  310.  
  311. private class UiNodeInfo
  312. {
  313. public string Name;
  314. public string OriginName;
  315. public string ClassName;
  316. public string TypeName;
  317. public List<UiNodeInfo> Children;
  318.  
  319. public UiNodeInfo(string name, string originName, string className, string typeName)
  320. {
  321. Name = name;
  322. OriginName = originName;
  323. ClassName = className;
  324. TypeName = typeName;
  325. }
  326. }
  327. }
  328.  
  329. #endif