增加怪物功能,调整灵灯参数
This commit is contained in:
@@ -2,11 +2,123 @@ using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Elements底对齐工具 - 将Elements下所有物体底对齐到Y=0平面
|
||||
/// Elements底对齐工具 - 将选中的物体底对齐到同一Y坐标
|
||||
/// </summary>
|
||||
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}", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取物体的世界空间包围盒(包括所有子物体和渲染器)
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using IndianOceanAssets.Engine2_5D;
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器工具:在场景中批量创建敌人实例。
|
||||
/// 每个敌人使用对应的新预制体(enemy_01~05),保留各自的外观和参数。
|
||||
/// 菜单路径:Assets > 创建5个敌人 (enemy_01~05)
|
||||
/// </summary>
|
||||
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<GameObject>(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,使用各自对应的新预制体");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: C3kYvH6qWy5FgJeh7I+nxvf1cNKzdmR6Bo0TQSyK6Lu2aV9lwrgFOmc=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,93 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using IndianOceanAssets.Engine2_5D;
|
||||
|
||||
/// <summary>
|
||||
/// 编辑器工具:基于原始 Enemy Prefab 批量创建 5 个不同外观的敌人预制体。
|
||||
/// 每个预制体使用不同的 Sprite,保留所有原有组件和参数。
|
||||
/// 菜单路径:Assets > 创建5个敌人预制体
|
||||
/// </summary>
|
||||
public static class EnemyPrefabBatchCreator
|
||||
{
|
||||
[MenuItem("Assets/创建5个敌人预制体")]
|
||||
public static void Create()
|
||||
{
|
||||
// 加载原始敌人 Prefab
|
||||
string originalPath = "Assets/2.5D Engine/Enemy.prefab";
|
||||
var originalPrefab = AssetDatabase.LoadAssetAtPath<GameObject>(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<Sprite>(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<SpriteRenderer>();
|
||||
if (mainSR != null)
|
||||
{
|
||||
mainSR.sprite = sprite;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果没有 SpriteRenderer,添加一个
|
||||
mainSR = instance.AddComponent<SpriteRenderer>();
|
||||
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<Object>(outputDir);
|
||||
if (folder != null)
|
||||
{
|
||||
Selection.activeObject = folder;
|
||||
EditorGUIUtility.PingObject(folder);
|
||||
}
|
||||
|
||||
Debug.Log($"[EnemyPrefabCreator] 完成!共创建 {created} 个敌人预制体于 {outputDir}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: W3IWvCytUC+01gvIXRyA8dEn6a7yxzldhbxdDpzTFOJskb8Bmxs1z1c=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,268 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: CnkbsCKvVy1NO58LD374An8U/q938/UfjeS0Awm+graLqugFj/c9WeQ=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user