Files
gold_dolphin/unity/Assets/Editor/UIBuilder.cs
T
2026-07-04 12:15:13 +08:00

461 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using UnityEditor;
using UnityEngine.UI;
using GameFramework;
/// <summary>
/// 一键搭建游戏 UIHUD Canvas + 游戏结果 Canvas + 主菜单标题。
/// 菜单路径:Tools > 搭建游戏UI
/// </summary>
public static class UIBuilder
{
[MenuItem("Tools/搭建游戏UI (HUD + ResultScreen + MainMenu)")]
public static void BuildAll()
{
BuildMainMenu();
BuildHUD();
BuildResultScreen();
Debug.Log("[UIBuilder] 全部 UI 搭建完成!请在 Inspector 中检查引用是否正确。");
}
/// <summary>
/// 在 MainMenu 场景中搭建主菜单 Canvas。
/// </summary>
[MenuItem("Tools/搭建 MainMenu Canvas")]
public static void BuildMainMenu()
{
// 加载美术资源
var bgSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/MainMenu/bg.jpg");
var titleSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/MainMenu/title.png");
var startSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/MainMenu/kaishiyouxi.png");
var replaySprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/MainMenu/juqinghuifang.png");
var creditsSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/MainMenu/zhizuorenyuan.png");
// 创建 MainMenu Canvas
var canvasObj = new GameObject("MainMenu_Canvas");
var canvas = canvasObj.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 0;
var scaler = canvasObj.AddComponent<CanvasScaler>();
scaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
scaler.referenceResolution = new Vector2(1920, 1080);
canvasObj.AddComponent<GraphicRaycaster>();
// 添加 MainMenuUIController 脚本
var controller = canvasObj.AddComponent<MainMenuUIController>();
// ===== 背景图 =====
if (bgSprite != null)
{
var bgObj = new GameObject("Background");
bgObj.transform.SetParent(canvasObj.transform, false);
var bgImg = bgObj.AddComponent<Image>();
bgImg.sprite = bgSprite;
bgImg.preserveAspect = false;
var bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
}
// ===== 标题图片 =====
if (titleSprite != null)
{
var titleObj = new GameObject("TitleImage");
titleObj.transform.SetParent(canvasObj.transform, false);
var titleImg = titleObj.AddComponent<Image>();
titleImg.sprite = titleSprite;
titleImg.preserveAspect = true;
var titleRect = titleObj.GetComponent<RectTransform>();
titleRect.anchorMin = new Vector2(0.5f, 0.65f);
titleRect.anchorMax = new Vector2(0.5f, 0.65f);
titleRect.pivot = new Vector2(0.5f, 0.5f);
titleRect.sizeDelta = new Vector2(titleSprite.rect.width * 0.5f, titleSprite.rect.height * 0.5f);
// 连接 titleImage 引用
var so = new SerializedObject(controller);
so.FindProperty("titleImage").objectReferenceValue = titleImg;
so.ApplyModifiedProperties();
}
// ===== 按钮区域(居中偏下) =====
var buttonArea = new GameObject("ButtonArea");
buttonArea.transform.SetParent(canvasObj.transform, false);
var areaRect = buttonArea.AddComponent<RectTransform>();
areaRect.anchorMin = new Vector2(0.5f, 0.15f);
areaRect.anchorMax = new Vector2(0.5f, 0.15f);
areaRect.pivot = new Vector2(0.5f, 0.5f);
var layout = buttonArea.AddComponent<VerticalLayoutGroup>();
layout.spacing = 20;
layout.childForceExpandWidth = false;
layout.childForceExpandHeight = false;
layout.childAlignment = TextAnchor.UpperCenter;
buttonArea.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// 开始游戏按钮
Button startBtn = null;
if (startSprite != null)
{
var btn = CreateImageButton(buttonArea.transform, "StartButton", startSprite);
startBtn = btn.GetComponent<Button>();
}
// 剧情回放按钮
Button replayBtn = null;
if (replaySprite != null)
{
var btn = CreateImageButton(buttonArea.transform, "StoryReplayButton", replaySprite);
replayBtn = btn.GetComponent<Button>();
}
// 制作人员按钮
Button creditsBtn = null;
if (creditsSprite != null)
{
var btn = CreateImageButton(buttonArea.transform, "CreditsButton", creditsSprite);
creditsBtn = btn.GetComponent<Button>();
}
// ===== 连接 MainMenuUIController 引用 =====
var cso = new SerializedObject(controller);
cso.FindProperty("startButton").objectReferenceValue = startBtn;
cso.FindProperty("storyReplayButton").objectReferenceValue = replayBtn;
cso.FindProperty("creditsButton").objectReferenceValue = creditsBtn;
cso.ApplyModifiedProperties();
Debug.Log("[UIBuilder] MainMenu Canvas 搭建完成");
}
/// <summary>
/// 在 Gameplay 场景中搭建 HUD Canvas。
/// </summary>
[MenuItem("Tools/搭建 HUD Canvas")]
public static void BuildHUD()
{
// 加载 HUD 美术资源(可选,没有则用占位)
var soulIconSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/HUD/soul_icon.png");
var lifeIconSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/HUD/life_icon.png");
var sprintIconSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/HUD/skill_sprint.png");
var bellIconSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/HUD/skill_bell.png");
var lanternIconSprite = AssetDatabase.LoadAssetAtPath<Sprite>("Assets/UI/Art/HUD/skill_lantern.png");
// 创建 HUD Canvas
var hudCanvasObj = new GameObject("HUD_Canvas");
var canvas = hudCanvasObj.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 10001;
hudCanvasObj.AddComponent<CanvasScaler>().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
hudCanvasObj.GetComponent<CanvasScaler>().referenceResolution = new Vector2(1920, 1080);
hudCanvasObj.AddComponent<GraphicRaycaster>();
// 添加 GameHUD 脚本
var hud = hudCanvasObj.AddComponent<GameHUD>();
var so = new SerializedObject(hud);
// ===== 左上角:魂灵计数 =====
var soulArea = CreateChild(hudCanvasObj, "SoulArea", new Vector2(20, -20));
var soulLayout = soulArea.AddComponent<HorizontalLayoutGroup>();
soulLayout.spacing = 8;
soulLayout.childForceExpandWidth = false;
soulLayout.childForceExpandHeight = true;
soulLayout.childAlignment = TextAnchor.MiddleLeft;
soulArea.AddComponent<ContentSizeFitter>().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
// 魂灵 Icon
var soulIconObj = CreateIcon(soulArea.transform, "SoulIcon", soulIconSprite, new Vector2(40, 40), Color.cyan);
so.FindProperty("soulIcon").objectReferenceValue = soulIconObj.GetComponent<Image>();
// 魂灵数字
var soulCountObj = CreateText(soulArea.transform, "SoulCount", "0", 28, TextAnchor.MiddleLeft);
soulCountObj.GetComponent<Text>().fontStyle = FontStyle.Bold;
soulCountObj.GetComponent<RectTransform>().sizeDelta = new Vector2(60, 40);
so.FindProperty("soulCountText").objectReferenceValue = soulCountObj.GetComponent<Text>();
// ===== 左下角:生命图标(5个) =====
var lifeArea = CreateChild(hudCanvasObj, "LifeArea", new Vector2(20, -20));
var lifeAreaRect = lifeArea.GetComponent<RectTransform>();
lifeAreaRect.anchorMin = new Vector2(0, 0);
lifeAreaRect.anchorMax = new Vector2(0, 0);
lifeAreaRect.pivot = new Vector2(0, 0);
lifeAreaRect.anchoredPosition = new Vector2(20, 20);
var lifeLayout = lifeArea.AddComponent<HorizontalLayoutGroup>();
lifeLayout.spacing = 5;
lifeLayout.childForceExpandWidth = false;
lifeLayout.childForceExpandHeight = true;
lifeLayout.childAlignment = TextAnchor.LowerLeft;
var lifeIconsProp = so.FindProperty("lifeIcons");
lifeIconsProp.arraySize = 5;
for (int i = 0; i < 5; i++)
{
var lifeIconObj = CreateIcon(lifeArea.transform, $"LifeIcon_{i}", lifeIconSprite, new Vector2(36, 36), Color.red);
lifeIconsProp.GetArrayElementAtIndex(i).objectReferenceValue = lifeIconObj.GetComponent<Image>();
}
// ===== 右下角:技能图标(冲刺/摇铃/灵灯) =====
var skillArea = CreateChild(hudCanvasObj, "SkillArea", new Vector2(-20, -20));
var skillAreaRect = skillArea.GetComponent<RectTransform>();
skillAreaRect.anchorMin = new Vector2(1, 0);
skillAreaRect.anchorMax = new Vector2(1, 0);
skillAreaRect.pivot = new Vector2(1, 0);
skillAreaRect.anchoredPosition = new Vector2(-20, 20);
var skillLayout = skillArea.AddComponent<HorizontalLayoutGroup>();
skillLayout.spacing = 12;
skillLayout.childForceExpandWidth = false;
skillLayout.childForceExpandHeight = true;
skillLayout.childAlignment = TextAnchor.LowerRight;
// 冲刺技能
var sprintObj = CreateSkillIcon(skillArea.transform, "SkillSprint", sprintIconSprite, new Color(0.3f, 0.8f, 1f));
so.FindProperty("skillSprintIcon").objectReferenceValue = sprintObj.transform.Find("Icon").GetComponent<Image>();
so.FindProperty("skillSprintCD").objectReferenceValue = sprintObj.transform.Find("CDOverlay").GetComponent<Image>();
// 摇铃技能
var bellObj = CreateSkillIcon(skillArea.transform, "SkillBell", bellIconSprite, new Color(1f, 0.85f, 0.2f));
so.FindProperty("skillBellIcon").objectReferenceValue = bellObj.transform.Find("Icon").GetComponent<Image>();
so.FindProperty("skillBellCD").objectReferenceValue = bellObj.transform.Find("CDOverlay").GetComponent<Image>();
// 灵灯技能
var lanternObj = CreateSkillIcon(skillArea.transform, "SkillLantern", lanternIconSprite, new Color(1f, 0.6f, 0.2f));
so.FindProperty("skillLanternIcon").objectReferenceValue = lanternObj.transform.Find("Icon").GetComponent<Image>();
so.FindProperty("skillLanternCD").objectReferenceValue = lanternObj.transform.Find("CDOverlay").GetComponent<Image>();
// ===== 右上角:暂停按钮 =====
var topRight = CreateChild(hudCanvasObj, "TopRight", new Vector2(-20, -20));
var topRightRect = topRight.GetComponent<RectTransform>();
topRightRect.anchorMin = new Vector2(1, 1);
topRightRect.anchorMax = new Vector2(1, 1);
topRightRect.pivot = new Vector2(1, 1);
var pauseObj = CreateButton(topRight.transform, "PauseButton", "||", new Vector2(50, 50));
so.FindProperty("pauseButton").objectReferenceValue = pauseObj.GetComponent<Button>();
so.ApplyModifiedProperties();
Debug.Log("[UIBuilder] HUD Canvas 搭建完成(魂灵+生命+技能CD");
}
/// <summary>
/// 在 Gameplay 场景中搭建游戏结果 Canvas。
/// </summary>
[MenuItem("Tools/搭建 ResultScreen Canvas")]
public static void BuildResultScreen()
{
// 创建 Result Canvas
var resultCanvasObj = new GameObject("ResultScreen_Canvas");
var canvas = resultCanvasObj.AddComponent<Canvas>();
canvas.renderMode = RenderMode.ScreenSpaceOverlay;
canvas.sortingOrder = 10002;
resultCanvasObj.AddComponent<CanvasScaler>().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
resultCanvasObj.GetComponent<CanvasScaler>().referenceResolution = new Vector2(1920, 1080);
resultCanvasObj.AddComponent<GraphicRaycaster>();
// 添加 GameResultScreen 脚本
var resultScreen = resultCanvasObj.AddComponent<GameResultScreen>();
// 半透明黑色背景
var bgObj = new GameObject("Overlay");
bgObj.transform.SetParent(resultCanvasObj.transform, false);
var bgImage = bgObj.AddComponent<Image>();
bgImage.color = new Color(0, 0, 0, 0.7f);
var bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
// ===== 胜利面板 =====
var winPanel = CreateCenterPanel(resultCanvasObj, "WinPanel", new Color(0.1f, 0.3f, 0.1f, 0.9f));
var winTitle = CreateText(winPanel.transform, "WinTitle", "胜利!", 40, TextAnchor.MiddleCenter);
winTitle.GetComponent<Text>().color = new Color(1f, 0.85f, 0.2f);
winTitle.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 60);
var winScore = CreateText(winPanel.transform, "WinScore", "分数: 0", 24, TextAnchor.MiddleCenter);
winScore.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 0);
var winBtn = CreateButton(winPanel.transform, "WinConfirmBtn", "确认", new Vector2(160, 50));
winBtn.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, -60);
// ===== 失败面板 =====
var losePanel = CreateCenterPanel(resultCanvasObj, "LosePanel", new Color(0.3f, 0.1f, 0.1f, 0.9f));
var loseTitle = CreateText(losePanel.transform, "LoseTitle", "游戏结束", 40, TextAnchor.MiddleCenter);
loseTitle.GetComponent<Text>().color = new Color(1f, 0.3f, 0.3f);
loseTitle.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 60);
var loseScore = CreateText(losePanel.transform, "LoseScore", "分数: 0", 24, TextAnchor.MiddleCenter);
loseScore.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, 0);
var loseBtn = CreateButton(losePanel.transform, "LoseConfirmBtn", "确认", new Vector2(160, 50));
loseBtn.GetComponent<RectTransform>().anchoredPosition = new Vector2(0, -60);
// 初始隐藏两个面板
winPanel.SetActive(false);
losePanel.SetActive(false);
// ===== 连接 GameResultScreen 引用 =====
var so = new SerializedObject(resultScreen);
so.FindProperty("resultCanvas").objectReferenceValue = canvas;
so.FindProperty("winPanel").objectReferenceValue = winPanel;
so.FindProperty("winConfirmButton").objectReferenceValue = winBtn.GetComponent<Button>();
so.FindProperty("winTitleText").objectReferenceValue = winTitle.GetComponent<Text>();
so.FindProperty("winScoreText").objectReferenceValue = winScore.GetComponent<Text>();
so.FindProperty("losePanel").objectReferenceValue = losePanel;
so.FindProperty("loseConfirmButton").objectReferenceValue = loseBtn.GetComponent<Button>();
so.FindProperty("loseTitleText").objectReferenceValue = loseTitle.GetComponent<Text>();
so.FindProperty("loseScoreText").objectReferenceValue = loseScore.GetComponent<Text>();
so.ApplyModifiedProperties();
Debug.Log("[UIBuilder] ResultScreen Canvas 搭建完成");
}
// ===== 辅助方法 =====
private static GameObject CreateChild(GameObject parent, string name, Vector2 pos)
{
var obj = new GameObject(name);
obj.transform.SetParent(parent.transform, false);
var rect = obj.AddComponent<RectTransform>();
rect.anchorMin = new Vector2(0, 1);
rect.anchorMax = new Vector2(0, 1);
rect.pivot = new Vector2(0, 1);
rect.anchoredPosition = pos;
return obj;
}
private static GameObject CreateText(Transform parent, string name, string content, int fontSize, TextAnchor alignment)
{
var obj = new GameObject(name);
obj.transform.SetParent(parent, false);
obj.AddComponent<RectTransform>();
var text = obj.AddComponent<Text>();
text.text = content;
text.fontSize = fontSize;
text.alignment = alignment;
text.color = Color.white;
text.font = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
if (text.font == null) text.font = Font.CreateDynamicFontFromOSFont("Arial", fontSize);
text.horizontalOverflow = HorizontalWrapMode.Overflow;
text.verticalOverflow = VerticalWrapMode.Overflow;
return obj;
}
private static GameObject CreateButton(Transform parent, string name, string label, Vector2 size)
{
var btnObj = new GameObject(name);
btnObj.transform.SetParent(parent, false);
var rect = btnObj.AddComponent<RectTransform>();
rect.sizeDelta = size;
var img = btnObj.AddComponent<Image>();
img.color = new Color(0.3f, 0.3f, 0.3f, 0.9f);
var btn = btnObj.AddComponent<Button>();
var textObj = CreateText(btnObj.transform, "Text", label, 18, TextAnchor.MiddleCenter);
var textRect = textObj.GetComponent<RectTransform>();
textRect.anchorMin = Vector2.zero;
textRect.anchorMax = Vector2.one;
textRect.sizeDelta = Vector2.zero;
return btnObj;
}
private static GameObject CreateIcon(Transform parent, string name, Sprite sprite, Vector2 size, Color placeholderColor)
{
var obj = new GameObject(name);
obj.transform.SetParent(parent, false);
obj.AddComponent<RectTransform>().sizeDelta = size;
var img = obj.AddComponent<Image>();
if (sprite != null)
{
img.sprite = sprite;
img.preserveAspect = true;
}
else
{
img.color = placeholderColor; // 占位色
}
return obj;
}
private static GameObject CreateSkillIcon(Transform parent, string name, Sprite sprite, Color cdColor)
{
var container = new GameObject(name);
container.transform.SetParent(parent, false);
container.AddComponent<RectTransform>().sizeDelta = new Vector2(56, 56);
// 背景
var bgObj = new GameObject("BG");
bgObj.transform.SetParent(container.transform, false);
var bgRect = bgObj.AddComponent<RectTransform>();
bgRect.anchorMin = Vector2.zero;
bgRect.anchorMax = Vector2.one;
bgRect.sizeDelta = Vector2.zero;
var bgImg = bgObj.AddComponent<Image>();
bgImg.color = new Color(0.1f, 0.1f, 0.1f, 0.7f);
// 技能图标
var iconObj = new GameObject("Icon");
iconObj.transform.SetParent(container.transform, false);
var iconRect = iconObj.AddComponent<RectTransform>();
iconRect.anchorMin = new Vector2(0.1f, 0.1f);
iconRect.anchorMax = new Vector2(0.9f, 0.9f);
iconRect.sizeDelta = Vector2.zero;
var iconImg = iconObj.AddComponent<Image>();
if (sprite != null)
{
iconImg.sprite = sprite;
iconImg.preserveAspect = true;
}
else
{
iconImg.color = cdColor; // 占位色
}
// CD 遮罩(覆盖在图标上方)
var cdObj = new GameObject("CDOverlay");
cdObj.transform.SetParent(container.transform, false);
var cdRect = cdObj.AddComponent<RectTransform>();
cdRect.anchorMin = Vector2.zero;
cdRect.anchorMax = Vector2.one;
cdRect.sizeDelta = Vector2.zero;
var cdImg = cdObj.AddComponent<Image>();
cdImg.color = new Color(0, 0, 0, 0.6f);
cdImg.type = Image.Type.Filled;
cdImg.fillMethod = Image.FillMethod.Radial360;
cdImg.fillOrigin = 2; // Top
cdImg.fillAmount = 0f;
cdObj.SetActive(false); // 初始无 CD
return container;
}
private static GameObject CreateImageButton(Transform parent, string name, Sprite sprite)
{
var btnObj = new GameObject(name);
btnObj.transform.SetParent(parent, false);
var rect = btnObj.AddComponent<RectTransform>();
// 根据 Sprite 原始尺寸设置按钮大小(缩小到合适尺寸)
float scale = 0.5f;
rect.sizeDelta = new Vector2(sprite.rect.width * scale, sprite.rect.height * scale);
var img = btnObj.AddComponent<Image>();
img.sprite = sprite;
img.preserveAspect = true;
var btn = btnObj.AddComponent<Button>();
// 设置按钮颜色过渡
var colors = btn.colors;
colors.normalColor = Color.white;
colors.highlightedColor = new Color(0.9f, 0.9f, 0.9f);
colors.pressedColor = new Color(0.7f, 0.7f, 0.7f);
btn.colors = colors;
return btnObj;
}
private static GameObject CreateCenterPanel(GameObject parent, string name, Color bgColor)
{
var panel = new GameObject(name);
panel.transform.SetParent(parent.transform, false);
var rect = panel.AddComponent<RectTransform>();
rect.anchorMin = new Vector2(0.5f, 0.5f);
rect.anchorMax = new Vector2(0.5f, 0.5f);
rect.pivot = new Vector2(0.5f, 0.5f);
rect.sizeDelta = new Vector2(400, 250);
var img = panel.AddComponent<Image>();
img.color = bgColor;
return panel;
}
}