feat(tools): 添加UI工具脚本及部署文档
新增内容: 1. 创建 06_工具脚本/UI工具/ 目录,包含4个核心工具脚本 - ExternalCommandListener.cs - 外部命令监听器 - RemoteCommand.cs - 远程命令接口 - UIPrefabGenerator.cs - JSON转预制体生成器 - UIPrefabToJson.cs - 预制体转JSON导出器 2. 更新 UI界面拼接设计指南.md - 新增8.9节工具脚本部署说明 - 包含手动部署和自动部署两种方式 - 添加部署规则和验证步骤 3. 更新 README.md - 添加06_工具脚本章节索引 部署方法: - 手动复制:复制工具脚本到项目 Assets/Editor/Command/ 目录 - 自动部署:使用 DeployUITools.bat 脚本
This commit is contained in:
624
06_工具脚本/UI工具/UIPrefabGenerator.cs
Normal file
624
06_工具脚本/UI工具/UIPrefabGenerator.cs
Normal file
@@ -0,0 +1,624 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityEditor;
|
||||
using TMPro;
|
||||
|
||||
public static class UIPrefabGenerator
|
||||
{
|
||||
[MenuItem("Tools/UI/Generate BagWindow Prefab")]
|
||||
public static void GenerateBagWindowPrefab()
|
||||
{
|
||||
string jsonPath = "Assets/Editor/UI/BagWindow.json";
|
||||
string prefabPath = "Assets/Game/GameHFResource/CN/UIWindows/BagWindow/BagWindow.prefab";
|
||||
|
||||
if (!File.Exists(jsonPath))
|
||||
{
|
||||
Debug.LogError("JSON file not found: " + jsonPath);
|
||||
return;
|
||||
}
|
||||
|
||||
string jsonContent = File.ReadAllText(jsonPath);
|
||||
UILayoutData layoutData = JsonUtility.FromJson<UILayoutData>(jsonContent);
|
||||
|
||||
GameObject root = new GameObject(layoutData.name);
|
||||
root.layer = 5;
|
||||
|
||||
Canvas canvas = root.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
canvas.pixelPerfect = layoutData.canvas.pixelPerfect;
|
||||
|
||||
root.AddComponent<CanvasScaler>();
|
||||
root.AddComponent<GraphicRaycaster>();
|
||||
|
||||
RectTransform rootRect = root.GetComponent<RectTransform>();
|
||||
SetRectTransform(rootRect, layoutData.rectTransform);
|
||||
|
||||
foreach (var childData in layoutData.children)
|
||||
{
|
||||
CreateUIElement(childData, root.transform);
|
||||
}
|
||||
|
||||
CreatePrefab(root, prefabPath);
|
||||
GameObject.DestroyImmediate(root);
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("UI Prefab generated successfully: " + prefabPath);
|
||||
}
|
||||
|
||||
public static void GeneratePrefabFromJson(string jsonPath, string prefabPath = null)
|
||||
{
|
||||
if (!File.Exists(jsonPath))
|
||||
{
|
||||
Debug.LogError("JSON file not found: " + jsonPath);
|
||||
return;
|
||||
}
|
||||
|
||||
string jsonContent = File.ReadAllText(jsonPath);
|
||||
UILayoutData layoutData = JsonUtility.FromJson<UILayoutData>(jsonContent);
|
||||
|
||||
if (layoutData == null)
|
||||
{
|
||||
Debug.LogError("Failed to parse JSON file: " + jsonPath);
|
||||
return;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(prefabPath))
|
||||
{
|
||||
prefabPath = "Assets/Game/GameHFResource/CN/UIWindows/" + layoutData.name + "/" + layoutData.name + ".prefab";
|
||||
}
|
||||
|
||||
GameObject root = new GameObject(layoutData.name);
|
||||
root.layer = layoutData.layer;
|
||||
|
||||
Canvas canvas = root.AddComponent<Canvas>();
|
||||
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
|
||||
if (layoutData.canvas != null)
|
||||
{
|
||||
canvas.pixelPerfect = layoutData.canvas.pixelPerfect;
|
||||
}
|
||||
|
||||
root.AddComponent<CanvasScaler>();
|
||||
root.AddComponent<GraphicRaycaster>();
|
||||
|
||||
RectTransform rootRect = root.GetComponent<RectTransform>();
|
||||
SetRectTransform(rootRect, layoutData.rectTransform);
|
||||
|
||||
foreach (var childData in layoutData.children)
|
||||
{
|
||||
CreateUIElement(childData, root.transform);
|
||||
}
|
||||
|
||||
CreatePrefab(root, prefabPath);
|
||||
GameObject.DestroyImmediate(root);
|
||||
|
||||
AssetDatabase.Refresh();
|
||||
Debug.Log("UI Prefab generated successfully: " + prefabPath);
|
||||
}
|
||||
|
||||
private static void CreateUIElement(UIElementData data, Transform parent)
|
||||
{
|
||||
GameObject obj = new GameObject(data.name);
|
||||
obj.transform.SetParent(parent, false);
|
||||
|
||||
RectTransform rect = obj.AddComponent<RectTransform>();
|
||||
SetRectTransform(rect, data.rectTransform);
|
||||
|
||||
switch (data.type)
|
||||
{
|
||||
case "Image":
|
||||
case "SFUI_Image":
|
||||
CreateImage(obj, data.image);
|
||||
break;
|
||||
case "Button":
|
||||
case "SFUI_Button":
|
||||
CreateButton(obj, data.button, data.image);
|
||||
break;
|
||||
case "TextMeshProUGUI":
|
||||
case "SFUI_TextMeshProUGUI":
|
||||
CreateTextMeshPro(obj, data.text);
|
||||
break;
|
||||
case "RectTransform":
|
||||
case "SFUI_Viewport":
|
||||
case "SFUI_Content":
|
||||
CreateRectTransform(obj, data.layoutGroup);
|
||||
break;
|
||||
case "ScrollRect":
|
||||
case "SFUI_ScrollRect":
|
||||
CreateScrollRect(obj, data.scrollRect);
|
||||
break;
|
||||
case "GridLayoutGroup":
|
||||
CreateGridLayoutGroup(obj, data.layoutGroup);
|
||||
break;
|
||||
case "VerticalLayoutGroup":
|
||||
CreateVerticalLayoutGroup(obj, data.layoutGroup);
|
||||
break;
|
||||
case "HorizontalLayoutGroup":
|
||||
CreateHorizontalLayoutGroup(obj, data.layoutGroup);
|
||||
break;
|
||||
case "Toggle":
|
||||
case "SFUI_Toggle":
|
||||
CreateToggle(obj, data.toggle);
|
||||
break;
|
||||
case "Slider":
|
||||
case "SFUI_Slider":
|
||||
CreateSlider(obj, data.slider);
|
||||
break;
|
||||
case "InputField":
|
||||
case "SFUI_InputField":
|
||||
CreateInputField(obj, data.inputField);
|
||||
break;
|
||||
case "CanvasScaler":
|
||||
case "GraphicRaycaster":
|
||||
break;
|
||||
case "SFUI_ListViewItem":
|
||||
CreateListViewItemPlaceholder(obj);
|
||||
break;
|
||||
case "Canvas":
|
||||
case "SFUI_Window":
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (data.children != null)
|
||||
{
|
||||
foreach (var childData in data.children)
|
||||
{
|
||||
CreateUIElement(childData, obj.transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetRectTransform(RectTransform rect, RectTransformData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
rect.anchorMin = new Vector2(data.anchorMin[0], data.anchorMin[1]);
|
||||
rect.anchorMax = new Vector2(data.anchorMax[0], data.anchorMax[1]);
|
||||
rect.anchoredPosition3D = new Vector3(data.position[0], data.position[1], data.position[2]);
|
||||
rect.sizeDelta = new Vector2(data.sizeDelta[0], data.sizeDelta[1]);
|
||||
rect.pivot = new Vector2(data.pivot[0], data.pivot[1]);
|
||||
|
||||
if (data.localScale != null && data.localScale.Length >= 3)
|
||||
{
|
||||
rect.localScale = new Vector3(data.localScale[0], data.localScale[1], data.localScale[2]);
|
||||
}
|
||||
|
||||
if (data.localRotation != null && data.localRotation.Length >= 3)
|
||||
{
|
||||
rect.localEulerAngles = new Vector3(data.localRotation[0], data.localRotation[1], data.localRotation[2]);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateImage(GameObject obj, ImageData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
Image image = obj.AddComponent<Image>();
|
||||
if (!string.IsNullOrEmpty(data.sprite))
|
||||
{
|
||||
Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(data.sprite);
|
||||
if (sprite != null)
|
||||
{
|
||||
image.sprite = sprite;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
image.type = (Image.Type)Enum.Parse(typeof(Image.Type), data.type);
|
||||
}
|
||||
catch
|
||||
{
|
||||
image.type = Image.Type.Simple;
|
||||
}
|
||||
|
||||
image.color = new Color(data.color[0], data.color[1], data.color[2], data.color[3]);
|
||||
image.raycastTarget = data.raycastTarget;
|
||||
image.preserveAspect = data.preserveAspect;
|
||||
}
|
||||
|
||||
private static void CreateButton(GameObject obj, ButtonData buttonData, ImageData imageData)
|
||||
{
|
||||
Button button = obj.AddComponent<Button>();
|
||||
if (buttonData != null)
|
||||
{
|
||||
button.interactable = buttonData.interactable;
|
||||
try
|
||||
{
|
||||
button.transition = (Selectable.Transition)Enum.Parse(typeof(Selectable.Transition), buttonData.transition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
button.transition = Selectable.Transition.ColorTint;
|
||||
}
|
||||
}
|
||||
|
||||
if (imageData != null)
|
||||
{
|
||||
CreateImage(obj, imageData);
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateTextMeshPro(GameObject obj, TextData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
TextMeshProUGUI text = obj.AddComponent<TextMeshProUGUI>();
|
||||
text.text = data.text;
|
||||
text.fontSize = data.fontSize;
|
||||
text.color = new Color(data.color[0], data.color[1], data.color[2], data.color[3]);
|
||||
|
||||
if (!string.IsNullOrEmpty(data.font))
|
||||
{
|
||||
TMP_FontAsset fontAsset = AssetDatabase.LoadAssetAtPath<TMP_FontAsset>(data.font);
|
||||
if (fontAsset != null)
|
||||
{
|
||||
text.font = fontAsset;
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(data.materialPreset))
|
||||
{
|
||||
Material material = AssetDatabase.LoadAssetAtPath<Material>(data.materialPreset);
|
||||
if (material != null)
|
||||
{
|
||||
text.material = material;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
text.alignment = (TextAlignmentOptions)Enum.Parse(typeof(TextAlignmentOptions), data.alignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
text.alignment = TextAlignmentOptions.Center;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
text.verticalAlignment = (VerticalAlignmentOptions)Enum.Parse(typeof(VerticalAlignmentOptions), data.verticalAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
text.verticalAlignment = VerticalAlignmentOptions.Middle;
|
||||
}
|
||||
|
||||
text.richText = data.richText;
|
||||
|
||||
try
|
||||
{
|
||||
text.overflowMode = (TextOverflowModes)Enum.Parse(typeof(TextOverflowModes), data.overflowMode);
|
||||
}
|
||||
catch
|
||||
{
|
||||
text.overflowMode = TextOverflowModes.Truncate;
|
||||
}
|
||||
|
||||
text.enableWordWrapping = data.enableWordWrapping;
|
||||
}
|
||||
|
||||
private static void CreateRectTransform(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData != null)
|
||||
{
|
||||
switch (layoutData.type)
|
||||
{
|
||||
case "GridLayoutGroup":
|
||||
CreateGridLayoutGroup(obj, layoutData);
|
||||
break;
|
||||
case "VerticalLayoutGroup":
|
||||
CreateVerticalLayoutGroup(obj, layoutData);
|
||||
break;
|
||||
case "HorizontalLayoutGroup":
|
||||
CreateHorizontalLayoutGroup(obj, layoutData);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateGridLayoutGroup(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData == null) return;
|
||||
|
||||
GridLayoutGroup grid = obj.AddComponent<GridLayoutGroup>();
|
||||
grid.cellSize = new Vector2(layoutData.cellSize[0], layoutData.cellSize[1]);
|
||||
grid.spacing = new Vector2(layoutData.spacing[0], layoutData.spacing[1]);
|
||||
grid.constraint = (GridLayoutGroup.Constraint)layoutData.constraint;
|
||||
|
||||
try
|
||||
{
|
||||
grid.childAlignment = (TextAnchor)Enum.Parse(typeof(TextAnchor), layoutData.childAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
grid.childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
grid.padding = new RectOffset(layoutData.padding[0], layoutData.padding[1], layoutData.padding[2], layoutData.padding[3]);
|
||||
}
|
||||
|
||||
private static void CreateVerticalLayoutGroup(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData == null) return;
|
||||
|
||||
VerticalLayoutGroup vertical = obj.AddComponent<VerticalLayoutGroup>();
|
||||
vertical.spacing = layoutData.spacing[1];
|
||||
|
||||
try
|
||||
{
|
||||
vertical.childAlignment = (TextAnchor)Enum.Parse(typeof(TextAnchor), layoutData.childAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
vertical.childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
vertical.padding = new RectOffset(layoutData.padding[0], layoutData.padding[1], layoutData.padding[2], layoutData.padding[3]);
|
||||
}
|
||||
|
||||
private static void CreateHorizontalLayoutGroup(GameObject obj, LayoutGroupData layoutData)
|
||||
{
|
||||
if (layoutData == null) return;
|
||||
|
||||
HorizontalLayoutGroup horizontal = obj.AddComponent<HorizontalLayoutGroup>();
|
||||
horizontal.spacing = layoutData.spacing[0];
|
||||
|
||||
try
|
||||
{
|
||||
horizontal.childAlignment = (TextAnchor)Enum.Parse(typeof(TextAnchor), layoutData.childAlignment);
|
||||
}
|
||||
catch
|
||||
{
|
||||
horizontal.childAlignment = TextAnchor.UpperLeft;
|
||||
}
|
||||
|
||||
horizontal.padding = new RectOffset(layoutData.padding[0], layoutData.padding[1], layoutData.padding[2], layoutData.padding[3]);
|
||||
}
|
||||
|
||||
private static void CreateScrollRect(GameObject obj, ScrollRectData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
ScrollRect scrollRect = obj.AddComponent<ScrollRect>();
|
||||
scrollRect.horizontal = data.horizontal;
|
||||
scrollRect.vertical = data.vertical;
|
||||
|
||||
try
|
||||
{
|
||||
scrollRect.movementType = (ScrollRect.MovementType)Enum.Parse(typeof(ScrollRect.MovementType), data.movementType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateToggle(GameObject obj, ToggleData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
Toggle toggle = obj.AddComponent<Toggle>();
|
||||
toggle.isOn = data.isOn;
|
||||
|
||||
try
|
||||
{
|
||||
toggle.toggleTransition = (Toggle.ToggleTransition)Enum.Parse(typeof(Toggle.ToggleTransition), data.toggleTransition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
toggle.toggleTransition = Toggle.ToggleTransition.Fade;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
toggle.transition = (Selectable.Transition)Enum.Parse(typeof(Selectable.Transition), data.transition);
|
||||
}
|
||||
catch
|
||||
{
|
||||
toggle.transition = Selectable.Transition.ColorTint;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateSlider(GameObject obj, SliderData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
Slider slider = obj.AddComponent<Slider>();
|
||||
slider.minValue = data.minValue;
|
||||
slider.maxValue = data.maxValue;
|
||||
slider.value = data.value;
|
||||
slider.wholeNumbers = data.wholeNumbers;
|
||||
|
||||
try
|
||||
{
|
||||
slider.direction = (Slider.Direction)Enum.Parse(typeof(Slider.Direction), data.direction);
|
||||
}
|
||||
catch
|
||||
{
|
||||
slider.direction = Slider.Direction.LeftToRight;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateInputField(GameObject obj, InputFieldData data)
|
||||
{
|
||||
if (data == null) return;
|
||||
|
||||
InputField inputField = obj.AddComponent<InputField>();
|
||||
inputField.text = data.text;
|
||||
inputField.characterLimit = data.characterLimit;
|
||||
|
||||
try
|
||||
{
|
||||
inputField.contentType = (InputField.ContentType)Enum.Parse(typeof(InputField.ContentType), data.contentType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
inputField.contentType = InputField.ContentType.Standard;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
inputField.inputType = (InputField.InputType)Enum.Parse(typeof(InputField.InputType), data.inputType);
|
||||
}
|
||||
catch
|
||||
{
|
||||
inputField.inputType = InputField.InputType.Standard;
|
||||
}
|
||||
}
|
||||
|
||||
private static void CreateListViewItemPlaceholder(GameObject obj)
|
||||
{
|
||||
}
|
||||
|
||||
private static void CreatePrefab(GameObject root, string path)
|
||||
{
|
||||
string directory = Path.GetDirectoryName(path);
|
||||
if (!Directory.Exists(directory))
|
||||
{
|
||||
Directory.CreateDirectory(directory);
|
||||
}
|
||||
|
||||
PrefabUtility.SaveAsPrefabAsset(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UILayoutData
|
||||
{
|
||||
public string name;
|
||||
public string type;
|
||||
public int layer;
|
||||
public RectTransformData rectTransform;
|
||||
public CanvasData canvas;
|
||||
public List<UIElementData> children;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class UIElementData
|
||||
{
|
||||
public string name;
|
||||
public string type;
|
||||
public RectTransformData rectTransform;
|
||||
public ImageData image;
|
||||
public ButtonData button;
|
||||
public TextData text;
|
||||
public LayoutGroupData layoutGroup;
|
||||
public ScrollRectData scrollRect;
|
||||
public ToggleData toggle;
|
||||
public SliderData slider;
|
||||
public InputFieldData inputField;
|
||||
public List<CustomComponentData> customComponents;
|
||||
public List<UIElementData> children;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class RectTransformData
|
||||
{
|
||||
public float[] anchorMin;
|
||||
public float[] anchorMax;
|
||||
public float[] position;
|
||||
public float[] sizeDelta;
|
||||
public float[] pivot;
|
||||
public float[] localScale;
|
||||
public float[] localRotation;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CanvasData
|
||||
{
|
||||
public string renderMode;
|
||||
public bool pixelPerfect;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ImageData
|
||||
{
|
||||
public string sprite;
|
||||
public string type;
|
||||
public float[] color;
|
||||
public bool raycastTarget;
|
||||
public bool preserveAspect;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ButtonData
|
||||
{
|
||||
public bool interactable;
|
||||
public string transition;
|
||||
public string navigationMode;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class TextData
|
||||
{
|
||||
public string text;
|
||||
public float fontSize;
|
||||
public string font;
|
||||
public string materialPreset;
|
||||
public string alignment;
|
||||
public string verticalAlignment;
|
||||
public float[] color;
|
||||
public bool richText;
|
||||
public string overflowMode;
|
||||
public bool enableWordWrapping;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class LayoutGroupData
|
||||
{
|
||||
public string type;
|
||||
public float[] cellSize;
|
||||
public float[] spacing;
|
||||
public int constraint;
|
||||
public string childAlignment;
|
||||
public int[] padding;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ScrollRectData
|
||||
{
|
||||
public bool horizontal;
|
||||
public bool vertical;
|
||||
public string movementType;
|
||||
public string horizontalScrollbarVisibility;
|
||||
public string verticalScrollbarVisibility;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class ToggleData
|
||||
{
|
||||
public bool isOn;
|
||||
public string toggleTransition;
|
||||
public string transition;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class SliderData
|
||||
{
|
||||
public float minValue;
|
||||
public float maxValue;
|
||||
public float value;
|
||||
public bool wholeNumbers;
|
||||
public string direction;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class InputFieldData
|
||||
{
|
||||
public string text;
|
||||
public string placeholder;
|
||||
public int characterLimit;
|
||||
public string contentType;
|
||||
public string inputType;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class CustomComponentData
|
||||
{
|
||||
public string typeName;
|
||||
}
|
||||
Reference in New Issue
Block a user