269 lines
13 KiB
C#
269 lines
13 KiB
C#
using UnityEngine;
|
||
using UnityEditor;
|
||
using UnityEngine.UI;
|
||
using GameFramework;
|
||
|
||
/// <summary>
|
||
/// 一键搭建游戏 UI:HUD Canvas + 游戏结果 Canvas + 主菜单标题。
|
||
/// 菜单路径:Tools > 搭建游戏UI
|
||
/// </summary>
|
||
public static class UIBuilder
|
||
{
|
||
[MenuItem("Tools/搭建游戏UI (HUD + ResultScreen + MainMenu标题)")]
|
||
public static void BuildAll()
|
||
{
|
||
BuildHUD();
|
||
BuildResultScreen();
|
||
Debug.Log("[UIBuilder] 全部 UI 搭建完成!请在 Inspector 中检查引用是否正确。");
|
||
}
|
||
|
||
/// <summary>
|
||
/// 在 Gameplay 场景中搭建 HUD Canvas。
|
||
/// </summary>
|
||
[MenuItem("Tools/搭建 HUD Canvas")]
|
||
public static void BuildHUD()
|
||
{
|
||
// 创建 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 healthArea = CreateChild(hudCanvasObj, "HealthArea", new Vector2(20, -20));
|
||
var healthLayout = healthArea.AddComponent<VerticalLayoutGroup>();
|
||
healthLayout.spacing = 5;
|
||
healthLayout.childForceExpandWidth = false;
|
||
healthLayout.childForceExpandHeight = false;
|
||
healthArea.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||
|
||
// 血量 Slider
|
||
var sliderObj = new GameObject("HealthSlider");
|
||
sliderObj.transform.SetParent(healthArea.transform, false);
|
||
var slider = sliderObj.AddComponent<Slider>();
|
||
slider.minValue = 0;
|
||
slider.maxValue = 1;
|
||
slider.value = 1;
|
||
slider.interactable = false;
|
||
var sliderRect = sliderObj.GetComponent<RectTransform>();
|
||
sliderRect.sizeDelta = new Vector2(200, 20);
|
||
|
||
// Slider 背景
|
||
var sliderBg = new GameObject("Background");
|
||
sliderBg.transform.SetParent(sliderObj.transform, false);
|
||
var bgImage = sliderBg.AddComponent<Image>();
|
||
bgImage.color = new Color(0.2f, 0.2f, 0.2f, 0.8f);
|
||
var bgRect = sliderBg.GetComponent<RectTransform>();
|
||
bgRect.anchorMin = Vector2.zero;
|
||
bgRect.anchorMax = Vector2.one;
|
||
bgRect.sizeDelta = Vector2.zero;
|
||
|
||
// Slider 填充区域
|
||
var fillArea = new GameObject("Fill Area");
|
||
fillArea.transform.SetParent(sliderObj.transform, false);
|
||
var fillAreaRect = fillArea.AddComponent<RectTransform>();
|
||
fillAreaRect.anchorMin = new Vector2(0, 0);
|
||
fillAreaRect.anchorMax = new Vector2(1, 1);
|
||
fillAreaRect.sizeDelta = Vector2.zero;
|
||
|
||
var fill = new GameObject("Fill");
|
||
fill.transform.SetParent(fillArea.transform, false);
|
||
var fillImage = fill.AddComponent<Image>();
|
||
fillImage.color = new Color(0.8f, 0.1f, 0.1f, 0.9f);
|
||
var fillRect = fill.GetComponent<RectTransform>();
|
||
fillRect.anchorMin = Vector2.zero;
|
||
fillRect.anchorMax = Vector2.one;
|
||
fillRect.sizeDelta = Vector2.zero;
|
||
|
||
slider.fillRect = fillRect;
|
||
|
||
// 血量文字
|
||
var healthTextObj = CreateText(healthArea.transform, "HealthText", "100/100", 14, TextAnchor.MiddleLeft);
|
||
healthTextObj.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 20);
|
||
|
||
// ===== 左上角下方:灵灯区域 =====
|
||
var lanternArea = CreateChild(hudCanvasObj, "LanternArea", new Vector2(20, -80));
|
||
var lanternLayout = lanternArea.AddComponent<VerticalLayoutGroup>();
|
||
lanternLayout.spacing = 3;
|
||
lanternLayout.childForceExpandWidth = false;
|
||
lanternLayout.childForceExpandHeight = false;
|
||
lanternArea.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||
|
||
var lanternCountObj = CreateText(lanternArea.transform, "LanternCount", "灵灯: 5", 16, TextAnchor.MiddleLeft);
|
||
lanternCountObj.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 24);
|
||
|
||
var lanternCDObj = CreateText(lanternArea.transform, "LanternCD", "就绪", 14, TextAnchor.MiddleLeft);
|
||
lanternCDObj.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 20);
|
||
|
||
// ===== 右上角:分数 + 暂停 =====
|
||
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 topRightLayout = topRight.AddComponent<VerticalLayoutGroup>();
|
||
topRightLayout.spacing = 8;
|
||
topRightLayout.childForceExpandWidth = false;
|
||
topRightLayout.childForceExpandHeight = false;
|
||
topRightLayout.childAlignment = TextAnchor.UpperRight;
|
||
topRight.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
||
|
||
var scoreObj = CreateText(topRight.transform, "ScoreText", "分数: 0", 20, TextAnchor.MiddleRight);
|
||
scoreObj.GetComponent<RectTransform>().sizeDelta = new Vector2(200, 30);
|
||
|
||
// 暂停按钮
|
||
var pauseObj = CreateButton(topRight.transform, "PauseButton", "暂停", new Vector2(100, 35));
|
||
|
||
// ===== 连接 GameHUD 引用 =====
|
||
var so = new SerializedObject(hud);
|
||
so.FindProperty("healthSlider").objectReferenceValue = slider;
|
||
so.FindProperty("healthText").objectReferenceValue = healthTextObj.GetComponent<Text>();
|
||
so.FindProperty("lanternCountText").objectReferenceValue = lanternCountObj.GetComponent<Text>();
|
||
so.FindProperty("lanternCDText").objectReferenceValue = lanternCDObj.GetComponent<Text>();
|
||
so.FindProperty("scoreText").objectReferenceValue = scoreObj.GetComponent<Text>();
|
||
so.FindProperty("pauseButton").objectReferenceValue = pauseObj.GetComponent<Button>();
|
||
so.ApplyModifiedProperties();
|
||
|
||
Debug.Log("[UIBuilder] HUD Canvas 搭建完成");
|
||
}
|
||
|
||
/// <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 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;
|
||
}
|
||
}
|