From 6e414a302276407bf5adf25ea0e3fc4cc39cde5e Mon Sep 17 00:00:00 2001 From: ksh999000 <740612340@qq.com> Date: Fri, 3 Jul 2026 16:06:18 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=80=AA=E7=89=A9=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=EF=BC=8C=E8=B0=83=E6=95=B4=E7=81=B5=E7=81=AF=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- unity/Assets/Editor/ElementsAligner.cs | 180 +++-- unity/Assets/Editor/EnemyBatchCreator.cs | 73 ++ unity/Assets/Editor/EnemyBatchCreator.cs.meta | 11 + .../Assets/Editor/EnemyPrefabBatchCreator.cs | 93 +++ .../Editor/EnemyPrefabBatchCreator.cs.meta | 11 + unity/Assets/Editor/UIBuilder.cs | 268 +++++++ unity/Assets/Editor/UIBuilder.cs.meta | 11 + .../shaders/IndianOcean_DarknessOverlay.mat | 2 +- unity/Assets/Scenes/Gameplay.unity | 737 ++++++++++++------ unity/Assets/UI/GameHUD.cs | 130 +++ unity/Assets/UI/GameHUD.cs.meta | 11 + unity/Assets/UI/GameManager.cs | 18 +- unity/Assets/UI/GameResultScreen.cs | 142 ++++ unity/Assets/UI/GameResultScreen.cs.meta | 11 + unity/Assets/UI/MainMenuUIController.cs | 7 +- unity/Assets/eco/Prefabs/SpiritLantern.prefab | 11 +- unity/Assets/eco/Prefabs/bomb.png | Bin 0 -> 385525 bytes unity/Assets/eco/Prefabs/bomb.png.meta | 175 +++++ unity/Assets/eco/Prefabs/point1.png | Bin 0 -> 614461 bytes unity/Assets/eco/Prefabs/point1.png.meta | 175 +++++ unity/Assets/eco/Prefabs/point2.png | Bin 0 -> 347751 bytes unity/Assets/eco/Prefabs/point2.png.meta | 175 +++++ unity/Assets/enemy/Prefabs.meta | 8 + unity/Assets/enemy/Prefabs/enemy_01.prefab | 582 ++++++++++++++ .../Assets/enemy/Prefabs/enemy_01.prefab.meta | 7 + unity/Assets/enemy/Prefabs/enemy_02.prefab | 582 ++++++++++++++ .../Assets/enemy/Prefabs/enemy_02.prefab.meta | 7 + unity/Assets/enemy/Prefabs/enemy_03.prefab | 582 ++++++++++++++ .../Assets/enemy/Prefabs/enemy_03.prefab.meta | 7 + unity/Assets/enemy/Prefabs/enemy_04.prefab | 582 ++++++++++++++ .../Assets/enemy/Prefabs/enemy_04.prefab.meta | 7 + unity/Assets/enemy/Prefabs/enemy_05.prefab | 582 ++++++++++++++ .../Assets/enemy/Prefabs/enemy_05.prefab.meta | 7 + unity/Assets/enemy/sprites.meta | 8 + unity/Assets/enemy/sprites/enemy1.png | Bin 0 -> 341463 bytes unity/Assets/enemy/sprites/enemy1.png.meta | 175 +++++ unity/Assets/enemy/sprites/enemy2.png | Bin 0 -> 310190 bytes unity/Assets/enemy/sprites/enemy2.png.meta | 175 +++++ unity/Assets/enemy/sprites/enemy3.png | Bin 0 -> 308457 bytes unity/Assets/enemy/sprites/enemy3.png.meta | 175 +++++ unity/Assets/enemy/sprites/enemy4.png | Bin 0 -> 308938 bytes unity/Assets/enemy/sprites/enemy4.png.meta | 175 +++++ unity/Assets/enemy/sprites/enemy5.png | Bin 0 -> 420353 bytes unity/Assets/enemy/sprites/enemy5.png.meta | 175 +++++ ...game-ui-planning-report.canvas.status.json | 3 + unity/game-ui-planning-report.canvas.tsx | 127 +++ 46 files changed, 5907 insertions(+), 300 deletions(-) create mode 100644 unity/Assets/Editor/EnemyBatchCreator.cs create mode 100644 unity/Assets/Editor/EnemyBatchCreator.cs.meta create mode 100644 unity/Assets/Editor/EnemyPrefabBatchCreator.cs create mode 100644 unity/Assets/Editor/EnemyPrefabBatchCreator.cs.meta create mode 100644 unity/Assets/Editor/UIBuilder.cs create mode 100644 unity/Assets/Editor/UIBuilder.cs.meta create mode 100644 unity/Assets/UI/GameHUD.cs create mode 100644 unity/Assets/UI/GameHUD.cs.meta create mode 100644 unity/Assets/UI/GameResultScreen.cs create mode 100644 unity/Assets/UI/GameResultScreen.cs.meta create mode 100644 unity/Assets/eco/Prefabs/bomb.png create mode 100644 unity/Assets/eco/Prefabs/bomb.png.meta create mode 100644 unity/Assets/eco/Prefabs/point1.png create mode 100644 unity/Assets/eco/Prefabs/point1.png.meta create mode 100644 unity/Assets/eco/Prefabs/point2.png create mode 100644 unity/Assets/eco/Prefabs/point2.png.meta create mode 100644 unity/Assets/enemy/Prefabs.meta create mode 100644 unity/Assets/enemy/Prefabs/enemy_01.prefab create mode 100644 unity/Assets/enemy/Prefabs/enemy_01.prefab.meta create mode 100644 unity/Assets/enemy/Prefabs/enemy_02.prefab create mode 100644 unity/Assets/enemy/Prefabs/enemy_02.prefab.meta create mode 100644 unity/Assets/enemy/Prefabs/enemy_03.prefab create mode 100644 unity/Assets/enemy/Prefabs/enemy_03.prefab.meta create mode 100644 unity/Assets/enemy/Prefabs/enemy_04.prefab create mode 100644 unity/Assets/enemy/Prefabs/enemy_04.prefab.meta create mode 100644 unity/Assets/enemy/Prefabs/enemy_05.prefab create mode 100644 unity/Assets/enemy/Prefabs/enemy_05.prefab.meta create mode 100644 unity/Assets/enemy/sprites.meta create mode 100644 unity/Assets/enemy/sprites/enemy1.png create mode 100644 unity/Assets/enemy/sprites/enemy1.png.meta create mode 100644 unity/Assets/enemy/sprites/enemy2.png create mode 100644 unity/Assets/enemy/sprites/enemy2.png.meta create mode 100644 unity/Assets/enemy/sprites/enemy3.png create mode 100644 unity/Assets/enemy/sprites/enemy3.png.meta create mode 100644 unity/Assets/enemy/sprites/enemy4.png create mode 100644 unity/Assets/enemy/sprites/enemy4.png.meta create mode 100644 unity/Assets/enemy/sprites/enemy5.png create mode 100644 unity/Assets/enemy/sprites/enemy5.png.meta create mode 100644 unity/game-ui-planning-report.canvas.status.json create mode 100644 unity/game-ui-planning-report.canvas.tsx diff --git a/unity/Assets/Editor/ElementsAligner.cs b/unity/Assets/Editor/ElementsAligner.cs index 51a68cc..4feb777 100644 --- a/unity/Assets/Editor/ElementsAligner.cs +++ b/unity/Assets/Editor/ElementsAligner.cs @@ -2,11 +2,123 @@ using UnityEngine; using UnityEditor; /// -/// Elements底对齐工具 - 将Elements下所有物体底对齐到Y=0平面 +/// Elements底对齐工具 - 将选中的物体底对齐到同一Y坐标 /// public class ElementsAligner : Editor { - [MenuItem("Tools/Elements/底对齐到地面 (Y=0)")] + [MenuItem("Tools/Elements/选中物体底对齐")] + public static void AlignSelectedToBottom() + { + var selected = Selection.gameObjects; + + if (selected == null || selected.Length < 2) + { + EditorUtility.DisplayDialog("提示", "请至少选中2个物体进行底对齐", "确定"); + return; + } + + // 找到所有选中物体中最低的底部Y坐标 + float lowestBottom = float.MaxValue; + var boundsData = new (Transform transform, Bounds bounds)[selected.Length]; + int validCount = 0; + + for (int i = 0; i < selected.Length; i++) + { + Transform t = selected[i].transform; + Bounds bounds = GetObjectBounds(t); + + if (bounds.size == Vector3.zero) + { + Debug.LogWarning($"跳过 {t.name} - 无法计算包围盒"); + continue; + } + + boundsData[validCount] = (t, bounds); + validCount++; + + if (bounds.min.y < lowestBottom) + lowestBottom = bounds.min.y; + } + + if (validCount < 2) + { + EditorUtility.DisplayDialog("提示", "有效物体不足2个,无法对齐", "确定"); + return; + } + + // 将所有物体的底部对齐到最低点 + Undo.SetCurrentGroupName("选中物体底对齐"); + int count = 0; + + for (int i = 0; i < validCount; i++) + { + var (t, bounds) = boundsData[i]; + Undo.RecordObject(t, $"Align {t.name}"); + + float bottomY = bounds.min.y; + Vector3 newPosition = t.position; + newPosition.y = t.position.y - bottomY + lowestBottom; + t.position = newPosition; + + count++; + Debug.Log($"✓ 对齐 {t.name}: 底部Y {bottomY:F3} → {lowestBottom:F3}"); + } + + if (count > 0) + { + UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty( + UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene() + ); + Debug.Log($"完成!底对齐了 {count} 个物体到 Y={lowestBottom:F3}"); + } + } + + [MenuItem("Tools/Elements/选中物体底对齐到Y=0")] + public static void AlignSelectedToGround() + { + var selected = Selection.gameObjects; + + if (selected == null || selected.Length == 0) + { + EditorUtility.DisplayDialog("提示", "请选中至少1个物体", "确定"); + return; + } + + int count = 0; + Undo.SetCurrentGroupName("选中物体底对齐到Y=0"); + + for (int i = 0; i < selected.Length; i++) + { + Transform t = selected[i].transform; + Undo.RecordObject(t, $"Align {t.name}"); + + Bounds bounds = GetObjectBounds(t); + + if (bounds.size == Vector3.zero) + { + Debug.LogWarning($"跳过 {t.name} - 无法计算包围盒"); + continue; + } + + float bottomY = bounds.min.y; + Vector3 newPosition = t.position; + newPosition.y = t.position.y - bottomY; + t.position = newPosition; + + count++; + Debug.Log($"✓ 对齐 {t.name}: 底部Y {bottomY:F3} → 0"); + } + + if (count > 0) + { + UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty( + UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene() + ); + Debug.Log($"完成!底对齐了 {count} 个物体到 Y=0"); + } + } + + [MenuItem("Tools/Elements/底对齐到地面 (Y=0) (Elements全部)")] public static void AlignElementsToGround() { GameObject elements = GameObject.Find("Elements"); @@ -23,11 +135,8 @@ public class ElementsAligner : Editor for (int i = 0; i < elements.transform.childCount; i++) { Transform child = elements.transform.GetChild(i); - - // 记录到Undo栈 Undo.RecordObject(child, $"Align {child.name}"); - // 获取物体的包围盒 Bounds bounds = GetObjectBounds(child); if (bounds.size == Vector3.zero) @@ -36,11 +145,9 @@ public class ElementsAligner : Editor continue; } - // 计算需要移动的距离:让物体底部对齐到Y=0 float bottomY = bounds.min.y; Vector3 newPosition = child.position; newPosition.y = child.position.y - bottomY; - child.position = newPosition; count++; @@ -52,69 +159,10 @@ public class ElementsAligner : Editor UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty( UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene() ); - - EditorUtility.DisplayDialog("完成", $"已对齐 {count} 个物体到地面 (Y=0)", "确定"); Debug.Log($"完成!对齐了 {count} 个物体"); } } - [MenuItem("Tools/Elements/底对齐到指定高度")] - public static void AlignElementsToCustomHeight() - { - string input = EditorInputDialog.Show("对齐到指定高度", "请输入目标Y坐标:", "0"); - - if (input == null) return; // 用户取消 - - if (!float.TryParse(input, out float targetY)) - { - EditorUtility.DisplayDialog("错误", "请输入有效的数字", "确定"); - return; - } - - GameObject elements = GameObject.Find("Elements"); - - if (elements == null) - { - EditorUtility.DisplayDialog("错误", "找不到场景中的 'Elements' 物体", "确定"); - return; - } - - int count = 0; - Undo.SetCurrentGroupName("Align Elements to Custom Height"); - - for (int i = 0; i < elements.transform.childCount; i++) - { - Transform child = elements.transform.GetChild(i); - Undo.RecordObject(child, $"Align {child.name}"); - - Bounds bounds = GetObjectBounds(child); - - if (bounds.size == Vector3.zero) - { - Debug.LogWarning($"跳过 {child.name} - 无法计算包围盒"); - continue; - } - - float bottomY = bounds.min.y; - Vector3 newPosition = child.position; - newPosition.y = child.position.y - bottomY + targetY; - - child.position = newPosition; - - count++; - Debug.Log($"✓ 对齐 {child.name}: 底部Y从 {bottomY:F3} 到 {targetY:F3}"); - } - - if (count > 0) - { - UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty( - UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene() - ); - - EditorUtility.DisplayDialog("完成", $"已对齐 {count} 个物体到 Y={targetY}", "确定"); - } - } - /// /// 获取物体的世界空间包围盒(包括所有子物体和渲染器) /// diff --git a/unity/Assets/Editor/EnemyBatchCreator.cs b/unity/Assets/Editor/EnemyBatchCreator.cs new file mode 100644 index 0000000..c751320 --- /dev/null +++ b/unity/Assets/Editor/EnemyBatchCreator.cs @@ -0,0 +1,73 @@ +using UnityEngine; +using UnityEditor; +using IndianOceanAssets.Engine2_5D; + +/// +/// 编辑器工具:在场景中批量创建敌人实例。 +/// 每个敌人使用对应的新预制体(enemy_01~05),保留各自的外观和参数。 +/// 菜单路径:Assets > 创建5个敌人 (enemy_01~05) +/// +public static class EnemyBatchCreator +{ + [MenuItem("Assets/创建5个敌人 (enemy_01~05)")] + public static void Create() + { + // 5个新预制体路径(对应各自的外观) + string[] prefabPaths = { + "Assets/enemy/Prefabs/enemy_01.prefab", + "Assets/enemy/Prefabs/enemy_02.prefab", + "Assets/enemy/Prefabs/enemy_03.prefab", + "Assets/enemy/Prefabs/enemy_04.prefab", + "Assets/enemy/Prefabs/enemy_05.prefab", + }; + string[] names = { "enemy_01", "enemy_02", "enemy_03", "enemy_04", "enemy_05" }; + + // 查找当前场景中的玩家位置,围绕玩家分布敌人 + var player = GameObject.FindWithTag("Player"); + Vector3 center = player != null ? player.transform.position : Vector3.zero; + + // 5个敌人的偏移位置(围绕玩家分布) + Vector3[] offsets = new Vector3[] + { + new Vector3(5f, 0f, 3f), + new Vector3(-5f, 0f, 3f), + new Vector3(5f, 0f, -3f), + new Vector3(-5f, 0f, -3f), + new Vector3(0f, 0f, 6f), + }; + + Undo.SetCurrentGroupName("创建5个敌人"); + + int created = 0; + for (int i = 0; i < 5; i++) + { + var prefab = AssetDatabase.LoadAssetAtPath(prefabPaths[i]); + if (prefab == null) + { + Debug.LogWarning($"[EnemyBatchCreator] 找不到预制体: {prefabPaths[i]},跳过"); + continue; + } + + // 实例化对应的 Prefab 到场景中 + GameObject enemy = (GameObject)PrefabUtility.InstantiatePrefab(prefab); + enemy.name = names[i]; + enemy.transform.position = center + offsets[i]; + + // 注册 Undo + Undo.RegisterCreatedObjectUndo(enemy, $"创建 {names[i]}"); + created++; + } + + // 选中刚创建的敌人 + var all = new GameObject[created]; + int idx = 0; + for (int i = 0; i < 5; i++) + { + var go = GameObject.Find(names[i]); + if (go != null) all[idx++] = go; + } + Selection.objects = all; + + Debug.Log($"[EnemyBatchCreator] 已创建 {created} 个敌人: enemy_01 ~ enemy_05,使用各自对应的新预制体"); + } +} diff --git a/unity/Assets/Editor/EnemyBatchCreator.cs.meta b/unity/Assets/Editor/EnemyBatchCreator.cs.meta new file mode 100644 index 0000000..0874659 --- /dev/null +++ b/unity/Assets/Editor/EnemyBatchCreator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: C3kYvH6qWy5FgJeh7I+nxvf1cNKzdmR6Bo0TQSyK6Lu2aV9lwrgFOmc= +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Editor/EnemyPrefabBatchCreator.cs b/unity/Assets/Editor/EnemyPrefabBatchCreator.cs new file mode 100644 index 0000000..e8142df --- /dev/null +++ b/unity/Assets/Editor/EnemyPrefabBatchCreator.cs @@ -0,0 +1,93 @@ +using UnityEngine; +using UnityEditor; +using IndianOceanAssets.Engine2_5D; + +/// +/// 编辑器工具:基于原始 Enemy Prefab 批量创建 5 个不同外观的敌人预制体。 +/// 每个预制体使用不同的 Sprite,保留所有原有组件和参数。 +/// 菜单路径:Assets > 创建5个敌人预制体 +/// +public static class EnemyPrefabBatchCreator +{ + [MenuItem("Assets/创建5个敌人预制体")] + public static void Create() + { + // 加载原始敌人 Prefab + string originalPath = "Assets/2.5D Engine/Enemy.prefab"; + var originalPrefab = AssetDatabase.LoadAssetAtPath(originalPath); + if (originalPrefab == null) + { + EditorUtility.DisplayDialog("错误", "找不到原始敌人 Prefab: " + originalPath, "确定"); + return; + } + + // 确保输出目录存在 + string outputDir = "Assets/enemy/Prefabs"; + if (!AssetDatabase.IsValidFolder(outputDir)) + { + string parent = "Assets/enemy"; + AssetDatabase.CreateFolder(parent, "Prefabs"); + } + + // 5个敌人的 Sprite 路径和名称 + string[] spritePaths = { + "Assets/enemy/sprites/enemy1.png", + "Assets/enemy/sprites/enemy2.png", + "Assets/enemy/sprites/enemy3.png", + "Assets/enemy/sprites/enemy4.png", + "Assets/enemy/sprites/enemy5.png", + }; + string[] prefabNames = { "enemy_01", "enemy_02", "enemy_03", "enemy_04", "enemy_05" }; + + int created = 0; + + for (int i = 0; i < 5; i++) + { + // 加载 Sprite + Sprite sprite = AssetDatabase.LoadAssetAtPath(spritePaths[i]); + if (sprite == null) + { + Debug.LogWarning($"[EnemyPrefabCreator] 找不到 Sprite: {spritePaths[i]},跳过"); + continue; + } + + // 实例化原始 Prefab + GameObject instance = Object.Instantiate(originalPrefab); + instance.name = prefabNames[i]; + + // 替换主 Sprite(查找根物体或 GFX 子物体的 SpriteRenderer) + SpriteRenderer mainSR = instance.GetComponentInChildren(); + if (mainSR != null) + { + mainSR.sprite = sprite; + } + else + { + // 如果没有 SpriteRenderer,添加一个 + mainSR = instance.AddComponent(); + mainSR.sprite = sprite; + mainSR.sortingOrder = 1; + } + + // 保存为新 Prefab + string prefabPath = $"{outputDir}/{prefabNames[i]}.prefab"; + PrefabUtility.SaveAsPrefabAsset(instance, prefabPath); + Object.DestroyImmediate(instance); + + created++; + Debug.Log($"[EnemyPrefabCreator] 创建: {prefabPath} (Sprite: {sprite.name})"); + } + + AssetDatabase.Refresh(); + + // 高亮输出目录 + var folder = AssetDatabase.LoadAssetAtPath(outputDir); + if (folder != null) + { + Selection.activeObject = folder; + EditorGUIUtility.PingObject(folder); + } + + Debug.Log($"[EnemyPrefabCreator] 完成!共创建 {created} 个敌人预制体于 {outputDir}"); + } +} diff --git a/unity/Assets/Editor/EnemyPrefabBatchCreator.cs.meta b/unity/Assets/Editor/EnemyPrefabBatchCreator.cs.meta new file mode 100644 index 0000000..34f49f1 --- /dev/null +++ b/unity/Assets/Editor/EnemyPrefabBatchCreator.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: W3IWvCytUC+01gvIXRyA8dEn6a7yxzldhbxdDpzTFOJskb8Bmxs1z1c= +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/unity/Assets/Editor/UIBuilder.cs b/unity/Assets/Editor/UIBuilder.cs new file mode 100644 index 0000000..74299b3 --- /dev/null +++ b/unity/Assets/Editor/UIBuilder.cs @@ -0,0 +1,268 @@ +using UnityEngine; +using UnityEditor; +using UnityEngine.UI; +using GameFramework; + +/// +/// 一键搭建游戏 UI:HUD Canvas + 游戏结果 Canvas + 主菜单标题。 +/// 菜单路径:Tools > 搭建游戏UI +/// +public static class UIBuilder +{ + [MenuItem("Tools/搭建游戏UI (HUD + ResultScreen + MainMenu标题)")] + public static void BuildAll() + { + BuildHUD(); + BuildResultScreen(); + Debug.Log("[UIBuilder] 全部 UI 搭建完成!请在 Inspector 中检查引用是否正确。"); + } + + /// + /// 在 Gameplay 场景中搭建 HUD Canvas。 + /// + [MenuItem("Tools/搭建 HUD Canvas")] + public static void BuildHUD() + { + // 创建 HUD Canvas + var hudCanvasObj = new GameObject("HUD_Canvas"); + var canvas = hudCanvasObj.AddComponent(); + canvas.renderMode = RenderMode.ScreenSpaceOverlay; + canvas.sortingOrder = 10001; + hudCanvasObj.AddComponent().uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize; + hudCanvasObj.GetComponent().referenceResolution = new Vector2(1920, 1080); + hudCanvasObj.AddComponent(); + + // 添加 GameHUD 脚本 + var hud = hudCanvasObj.AddComponent(); + + // ===== 左上角:血量区域 ===== + var healthArea = CreateChild(hudCanvasObj, "HealthArea", new Vector2(20, -20)); + var healthLayout = healthArea.AddComponent(); + healthLayout.spacing = 5; + healthLayout.childForceExpandWidth = false; + healthLayout.childForceExpandHeight = false; + healthArea.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; + + // 血量 Slider + var sliderObj = new GameObject("HealthSlider"); + sliderObj.transform.SetParent(healthArea.transform, false); + var slider = sliderObj.AddComponent(); + slider.minValue = 0; + slider.maxValue = 1; + slider.value = 1; + slider.interactable = false; + var sliderRect = sliderObj.GetComponent(); + sliderRect.sizeDelta = new Vector2(200, 20); + + // Slider 背景 + var sliderBg = new GameObject("Background"); + sliderBg.transform.SetParent(sliderObj.transform, false); + var bgImage = sliderBg.AddComponent(); + bgImage.color = new Color(0.2f, 0.2f, 0.2f, 0.8f); + var bgRect = sliderBg.GetComponent(); + 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(); + 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(); + fillImage.color = new Color(0.8f, 0.1f, 0.1f, 0.9f); + var fillRect = fill.GetComponent(); + 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().sizeDelta = new Vector2(200, 20); + + // ===== 左上角下方:灵灯区域 ===== + var lanternArea = CreateChild(hudCanvasObj, "LanternArea", new Vector2(20, -80)); + var lanternLayout = lanternArea.AddComponent(); + lanternLayout.spacing = 3; + lanternLayout.childForceExpandWidth = false; + lanternLayout.childForceExpandHeight = false; + lanternArea.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; + + var lanternCountObj = CreateText(lanternArea.transform, "LanternCount", "灵灯: 5", 16, TextAnchor.MiddleLeft); + lanternCountObj.GetComponent().sizeDelta = new Vector2(200, 24); + + var lanternCDObj = CreateText(lanternArea.transform, "LanternCD", "就绪", 14, TextAnchor.MiddleLeft); + lanternCDObj.GetComponent().sizeDelta = new Vector2(200, 20); + + // ===== 右上角:分数 + 暂停 ===== + var topRight = CreateChild(hudCanvasObj, "TopRight", new Vector2(-20, -20)); + var topRightRect = topRight.GetComponent(); + topRightRect.anchorMin = new Vector2(1, 1); + topRightRect.anchorMax = new Vector2(1, 1); + topRightRect.pivot = new Vector2(1, 1); + + var topRightLayout = topRight.AddComponent(); + topRightLayout.spacing = 8; + topRightLayout.childForceExpandWidth = false; + topRightLayout.childForceExpandHeight = false; + topRightLayout.childAlignment = TextAnchor.UpperRight; + topRight.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; + + var scoreObj = CreateText(topRight.transform, "ScoreText", "分数: 0", 20, TextAnchor.MiddleRight); + scoreObj.GetComponent().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(); + so.FindProperty("lanternCountText").objectReferenceValue = lanternCountObj.GetComponent(); + so.FindProperty("lanternCDText").objectReferenceValue = lanternCDObj.GetComponent(); + so.FindProperty("scoreText").objectReferenceValue = scoreObj.GetComponent(); + so.FindProperty("pauseButton").objectReferenceValue = pauseObj.GetComponent