添加工具,优化配置
This commit is contained in:
229
unity/Assets/Editor/ElementsAligner.cs
Normal file
229
unity/Assets/Editor/ElementsAligner.cs
Normal file
@@ -0,0 +1,229 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Elements底对齐工具 - 将Elements下所有物体底对齐到Y=0平面
|
||||
/// </summary>
|
||||
public class ElementsAligner : Editor
|
||||
{
|
||||
[MenuItem("Tools/Elements/底对齐到地面 (Y=0)")]
|
||||
public static void AlignElementsToGround()
|
||||
{
|
||||
GameObject elements = GameObject.Find("Elements");
|
||||
|
||||
if (elements == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "找不到场景中的 'Elements' 物体", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
Undo.SetCurrentGroupName("Align Elements to Ground");
|
||||
|
||||
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)
|
||||
{
|
||||
Debug.LogWarning($"跳过 {child.name} - 无法计算包围盒");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 计算需要移动的距离:让物体底部对齐到Y=0
|
||||
float bottomY = bounds.min.y;
|
||||
Vector3 newPosition = child.position;
|
||||
newPosition.y = child.position.y - bottomY;
|
||||
|
||||
child.position = newPosition;
|
||||
|
||||
count++;
|
||||
Debug.Log($"✓ 对齐 {child.name}: 底部Y从 {bottomY:F3} 到 0");
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
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>
|
||||
private static Bounds GetObjectBounds(Transform obj)
|
||||
{
|
||||
Renderer[] renderers = obj.GetComponentsInChildren<Renderer>();
|
||||
|
||||
if (renderers.Length == 0)
|
||||
{
|
||||
// 如果没有渲染器,使用碰撞体或transform位置
|
||||
Collider[] colliders = obj.GetComponentsInChildren<Collider>();
|
||||
if (colliders.Length > 0)
|
||||
{
|
||||
Bounds bounds = colliders[0].bounds;
|
||||
for (int i = 1; i < colliders.Length; i++)
|
||||
{
|
||||
bounds.Encapsulate(colliders[i].bounds);
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// 都没有,返回以物体为中心的小包围盒
|
||||
return new Bounds(obj.position, Vector3.one * 0.1f);
|
||||
}
|
||||
|
||||
// 使用第一个渲染器初始化
|
||||
Bounds result = renderers[0].bounds;
|
||||
|
||||
// 合并所有渲染器的包围盒
|
||||
for (int i = 1; i < renderers.Length; i++)
|
||||
{
|
||||
result.Encapsulate(renderers[i].bounds);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 简单的输入对话框(用于自定义高度)
|
||||
/// </summary>
|
||||
public class EditorInputDialog : EditorWindow
|
||||
{
|
||||
private string _title;
|
||||
private string _message;
|
||||
private string _inputText;
|
||||
private bool _confirmed = false;
|
||||
|
||||
public static string Show(string title, string message, string defaultValue)
|
||||
{
|
||||
EditorInputDialog window = CreateInstance<EditorInputDialog>();
|
||||
window.titleContent = new GUIContent(title);
|
||||
window._title = title;
|
||||
window._message = message;
|
||||
window._inputText = defaultValue;
|
||||
window._confirmed = false;
|
||||
window.minSize = new Vector2(300, 120);
|
||||
window.maxSize = new Vector2(300, 120);
|
||||
window.ShowModal();
|
||||
|
||||
return window._confirmed ? window._inputText : null;
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label(_message, EditorStyles.wordWrappedLabel);
|
||||
GUILayout.Space(5);
|
||||
|
||||
GUI.SetNextControlName("InputField");
|
||||
_inputText = GUILayout.TextField(_inputText);
|
||||
|
||||
if (Event.current.type == EventType.Repaint && GUI.GetNameOfFocusedControl() != "InputField")
|
||||
{
|
||||
EditorGUI.FocusTextInControl("InputField");
|
||||
}
|
||||
|
||||
// 支持回车确认
|
||||
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return)
|
||||
{
|
||||
_confirmed = true;
|
||||
Close();
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
// 支持ESC取消
|
||||
if (Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Escape)
|
||||
{
|
||||
_confirmed = false;
|
||||
Close();
|
||||
Event.current.Use();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
if (GUILayout.Button("确定", GUILayout.Width(80)))
|
||||
{
|
||||
_confirmed = true;
|
||||
Close();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("取消", GUILayout.Width(80)))
|
||||
{
|
||||
_confirmed = false;
|
||||
Close();
|
||||
}
|
||||
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
11
unity/Assets/Editor/ElementsAligner.cs.meta
Normal file
11
unity/Assets/Editor/ElementsAligner.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: DC5L4SmvAikxYp2FEhg5gm08qf2yJAfGzXIwjRrErdh5PGIXksqhon4=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
150
unity/Assets/Editor/SceneElementsYSorter.cs
Normal file
150
unity/Assets/Editor/SceneElementsYSorter.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// 场景Elements Y-Sort添加工具
|
||||
/// </summary>
|
||||
public class SceneElementsYSorter : Editor
|
||||
{
|
||||
[MenuItem("Tools/Y-Sort/给Test场景Elements添加YSorter")]
|
||||
public static void AddYSorterToTestSceneElements()
|
||||
{
|
||||
// 查找场景中的Elements物体
|
||||
GameObject elements = GameObject.Find("Elements");
|
||||
|
||||
if (elements == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("错误", "找不到场景中的 'Elements' 物体", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
Transform elementsTransform = elements.transform;
|
||||
int count = 0;
|
||||
List<string> processedObjects = new List<string>();
|
||||
|
||||
// 遍历所有直接子物体
|
||||
for (int i = 0; i < elementsTransform.childCount; i++)
|
||||
{
|
||||
Transform child = elementsTransform.GetChild(i);
|
||||
|
||||
// 检查是否已有YSorter
|
||||
if (child.GetComponent<YSorter>() != null)
|
||||
{
|
||||
Debug.Log($"跳过 {child.name} - 已有YSorter");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 添加YSorter组件
|
||||
YSorter ySorter = child.gameObject.AddComponent<YSorter>();
|
||||
|
||||
// 根据物体名称设置baseSortingOrder
|
||||
int baseOrder = DetermineBaseOrder(child.name);
|
||||
|
||||
// 通过SerializedObject设置
|
||||
SerializedObject so = new SerializedObject(ySorter);
|
||||
SerializedProperty prop = so.FindProperty("baseSortingOrder");
|
||||
if (prop != null)
|
||||
{
|
||||
prop.intValue = baseOrder;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
// 标记场景已修改
|
||||
EditorUtility.SetDirty(child.gameObject);
|
||||
|
||||
count++;
|
||||
processedObjects.Add($"{child.name} (baseSortingOrder: {baseOrder})");
|
||||
Debug.Log($"✓ 已添加 YSorter 到 {child.name} (baseSortingOrder: {baseOrder})");
|
||||
}
|
||||
|
||||
// 保存场景
|
||||
if (count > 0)
|
||||
{
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
|
||||
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()
|
||||
);
|
||||
|
||||
EditorUtility.DisplayDialog(
|
||||
"完成",
|
||||
$"已给 {count} 个物体添加YSorter\n\n" +
|
||||
string.Join("\n", processedObjects),
|
||||
"确定"
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "所有物体都已有YSorter", "确定");
|
||||
}
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Y-Sort/给选中物体添加YSorter")]
|
||||
public static void AddYSorterToSelected()
|
||||
{
|
||||
if (Selection.gameObjects.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "请先选择物体", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
foreach (GameObject go in Selection.gameObjects)
|
||||
{
|
||||
if (go.GetComponent<YSorter>() != null)
|
||||
{
|
||||
Debug.Log($"跳过 {go.name} - 已有YSorter");
|
||||
continue;
|
||||
}
|
||||
|
||||
YSorter ySorter = go.AddComponent<YSorter>();
|
||||
int baseOrder = DetermineBaseOrder(go.name);
|
||||
|
||||
SerializedObject so = new SerializedObject(ySorter);
|
||||
SerializedProperty prop = so.FindProperty("baseSortingOrder");
|
||||
if (prop != null)
|
||||
{
|
||||
prop.intValue = baseOrder;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
EditorUtility.SetDirty(go);
|
||||
count++;
|
||||
Debug.Log($"✓ 已添加 YSorter 到 {go.name} (baseSortingOrder: {baseOrder})");
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
{
|
||||
UnityEditor.SceneManagement.EditorSceneManager.MarkSceneDirty(
|
||||
UnityEditor.SceneManagement.EditorSceneManager.GetActiveScene()
|
||||
);
|
||||
Debug.Log($"完成!给 {count} 个物体添加了YSorter");
|
||||
}
|
||||
}
|
||||
|
||||
private static int DetermineBaseOrder(string objectName)
|
||||
{
|
||||
string nameLower = objectName.ToLower();
|
||||
|
||||
if (nameLower.Contains("ground") || nameLower.Contains("floor"))
|
||||
return 0;
|
||||
if (nameLower.Contains("shadow"))
|
||||
return 500;
|
||||
if (nameLower.Contains("grass") || nameLower.Contains("flower") ||
|
||||
nameLower.Contains("leaf") || nameLower.Contains("leaves"))
|
||||
return 800;
|
||||
if (nameLower.Contains("rock") || nameLower.Contains("bamboo") ||
|
||||
nameLower.Contains("barrel") || nameLower.Contains("box") ||
|
||||
nameLower.Contains("log") || nameLower.Contains("board") ||
|
||||
nameLower.Contains("bush") || nameLower.Contains("bonfire") ||
|
||||
nameLower.Contains("candle") || nameLower.Contains("carcasses") ||
|
||||
nameLower.Contains("lotus") || nameLower.Contains("pedestal") ||
|
||||
nameLower.Contains("scarecrow") || nameLower.Contains("skeleton") ||
|
||||
nameLower.Contains("skull") || nameLower.Contains("spear") ||
|
||||
nameLower.Contains("statue") || nameLower.Contains("tent") ||
|
||||
nameLower.Contains("tenticles") || nameLower.Contains("wooden") ||
|
||||
nameLower.Contains("bag") || nameLower.Contains("house"))
|
||||
return 1000;
|
||||
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
11
unity/Assets/Editor/SceneElementsYSorter.cs.meta
Normal file
11
unity/Assets/Editor/SceneElementsYSorter.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: CXId5y+uBnMV5xxn04I7kiFCq2y/PqaiVVbHtE0aEpDF+ppBPLZ1ETI=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
150
unity/Assets/Editor/YSortBatchAdder.cs
Normal file
150
unity/Assets/Editor/YSortBatchAdder.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.IO;
|
||||
|
||||
/// <summary>
|
||||
/// Y-Sort 批量添加工具
|
||||
///
|
||||
/// 使用方法:
|
||||
/// 1. 在Unity菜单栏点击 Tools -> Y-Sort -> 批量添加YSorter
|
||||
/// 2. 会自动给Player、Enemy和所有场景预制体添加YSorter组件
|
||||
/// 3. 根据物体类型自动设置baseSortingOrder
|
||||
/// </summary>
|
||||
public class YSortBatchAdder : Editor
|
||||
{
|
||||
[MenuItem("Tools/Y-Sort/批量添加YSorter到所有预制体")]
|
||||
public static void AddYSorterToAllPrefabs()
|
||||
{
|
||||
string prefabFolder = "Assets/2.5D Engine/Prefabs";
|
||||
|
||||
if (!Directory.Exists(prefabFolder))
|
||||
{
|
||||
Debug.LogError($"找不到预制体文件夹: {prefabFolder}");
|
||||
return;
|
||||
}
|
||||
|
||||
string[] prefabPaths = Directory.GetFiles(prefabFolder, "*.prefab", SearchOption.AllDirectories);
|
||||
int count = 0;
|
||||
|
||||
foreach (string path in prefabPaths)
|
||||
{
|
||||
string normalizedPath = path.Replace("\\", "/");
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(normalizedPath);
|
||||
|
||||
if (prefab == null) continue;
|
||||
|
||||
// 检查是否已经有YSorter
|
||||
if (prefab.GetComponentInChildren<YSorter>() != null)
|
||||
{
|
||||
Debug.Log($"跳过 {prefab.name} - 已有YSorter");
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据预制体名称判断类型并设置baseSortingOrder
|
||||
int baseOrder = DetermineBaseOrder(prefab.name);
|
||||
|
||||
// 添加到根物体
|
||||
AddYSorterToPrefab(prefab, baseOrder);
|
||||
count++;
|
||||
|
||||
Debug.Log($"已添加 YSorter 到 {prefab.name} (baseSortingOrder: {baseOrder})");
|
||||
}
|
||||
|
||||
AssetDatabase.SaveAssets();
|
||||
AssetDatabase.Refresh();
|
||||
|
||||
Debug.Log($"完成!共添加了 {count} 个YSorter");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Y-Sort/添加YSorter到Player")]
|
||||
public static void AddYSorterToPlayer()
|
||||
{
|
||||
string playerPath = "Assets/2.5D Engine/Player.prefab";
|
||||
GameObject player = AssetDatabase.LoadAssetAtPath<GameObject>(playerPath);
|
||||
|
||||
if (player == null)
|
||||
{
|
||||
Debug.LogError($"找不到Player预制体: {playerPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
AddYSorterToPrefab(player, 2000); // 角色层
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log("已添加YSorter到Player (baseSortingOrder: 2000)");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Y-Sort/添加YSorter到Enemy")]
|
||||
public static void AddYSorterToEnemy()
|
||||
{
|
||||
string enemyPath = "Assets/2.5D Engine/Enemy.prefab";
|
||||
GameObject enemy = AssetDatabase.LoadAssetAtPath<GameObject>(enemyPath);
|
||||
|
||||
if (enemy == null)
|
||||
{
|
||||
Debug.LogError($"找不到Enemy预制体: {enemyPath}");
|
||||
return;
|
||||
}
|
||||
|
||||
AddYSorterToPrefab(enemy, 2000); // 角色层
|
||||
AssetDatabase.SaveAssets();
|
||||
Debug.Log("已添加YSorter到Enemy (baseSortingOrder: 2000)");
|
||||
}
|
||||
|
||||
private static int DetermineBaseOrder(string prefabName)
|
||||
{
|
||||
string nameLower = prefabName.ToLower();
|
||||
|
||||
// 地面/地板类
|
||||
if (nameLower.Contains("ground") || nameLower.Contains("floor"))
|
||||
return 0;
|
||||
|
||||
// 阴影类
|
||||
if (nameLower.Contains("shadow"))
|
||||
return 500;
|
||||
|
||||
// 草、花、叶子等小物件
|
||||
if (nameLower.Contains("grass") || nameLower.Contains("flower") ||
|
||||
nameLower.Contains("leaf") || nameLower.Contains("leaves"))
|
||||
return 800;
|
||||
|
||||
// 场景道具(树、石头、箱子、桶等)
|
||||
if (nameLower.Contains("rock") || nameLower.Contains("bamboo") ||
|
||||
nameLower.Contains("barrel") || nameLower.Contains("box") ||
|
||||
nameLower.Contains("log") || nameLower.Contains("board") ||
|
||||
nameLower.Contains("bush") || nameLower.Contains("bonfire") ||
|
||||
nameLower.Contains("candle") || nameLower.Contains("carcasses") ||
|
||||
nameLower.Contains("lotus") || nameLower.Contains("pedestal") ||
|
||||
nameLower.Contains("scarecrow") || nameLower.Contains("skeleton") ||
|
||||
nameLower.Contains("skull") || nameLower.Contains("spear") ||
|
||||
nameLower.Contains("statue") || nameLower.Contains("tent") ||
|
||||
nameLower.Contains("tenticles") || nameLower.Contains("wooden") ||
|
||||
nameLower.Contains("bag") || nameLower.Contains("house"))
|
||||
return 1000;
|
||||
|
||||
// 默认
|
||||
return 1000;
|
||||
}
|
||||
|
||||
private static void AddYSorterToPrefab(GameObject prefab, int baseSortingOrder)
|
||||
{
|
||||
// 使用PrefabUtility打开预制体进行编辑
|
||||
string prefabPath = AssetDatabase.GetAssetPath(prefab);
|
||||
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(prefabPath);
|
||||
|
||||
// 添加YSorter组件到根物体
|
||||
YSorter ySorter = prefabRoot.AddComponent<YSorter>();
|
||||
|
||||
// 通过SerializedObject设置baseSortingOrder
|
||||
SerializedObject so = new SerializedObject(ySorter);
|
||||
SerializedProperty baseOrderProp = so.FindProperty("baseSortingOrder");
|
||||
if (baseOrderProp != null)
|
||||
{
|
||||
baseOrderProp.intValue = baseSortingOrder;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
// 保存预制体
|
||||
PrefabUtility.SaveAsPrefabAsset(prefabRoot, prefabPath);
|
||||
PrefabUtility.UnloadPrefabContents(prefabRoot);
|
||||
}
|
||||
}
|
||||
11
unity/Assets/Editor/YSortBatchAdder.cs.meta
Normal file
11
unity/Assets/Editor/YSortBatchAdder.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: XHscsS34ASkUIqS5+NMIlnFVzhdiTT4OWMFKV5plXmzuXkQ9iGNU2EA=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
131
unity/Assets/Editor/YSortDebugger.cs
Normal file
131
unity/Assets/Editor/YSortDebugger.cs
Normal file
@@ -0,0 +1,131 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
/// <summary>
|
||||
/// Y-Sort诊断工具
|
||||
/// </summary>
|
||||
public class YSortDebugger : Editor
|
||||
{
|
||||
[MenuItem("Tools/Y-Sort/诊断YSorter状态")]
|
||||
public static void DebugYSorter()
|
||||
{
|
||||
// 查找场景中所有YSorter
|
||||
YSorter[] allSorters = FindObjectsOfType<YSorter>();
|
||||
|
||||
if (allSorters.Length == 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("诊断结果", "场景中没有找到任何YSorter组件!\n\n请先使用菜单:\nTools → Y-Sort → 给Test场景Elements添加YSorter", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
string report = $"找到 {allSorters.Length} 个YSorter组件\n\n";
|
||||
report += "=== 详细信息 ===\n\n";
|
||||
|
||||
foreach (YSorter sorter in allSorters)
|
||||
{
|
||||
report += $"物体: {sorter.gameObject.name}\n";
|
||||
report += $" 位置: {sorter.transform.position}\n";
|
||||
|
||||
// 获取SpriteRenderer
|
||||
SpriteRenderer sr = sorter.GetComponentInChildren<SpriteRenderer>();
|
||||
if (sr != null)
|
||||
{
|
||||
report += $" SpriteRenderer: {sr.name}\n";
|
||||
report += $" SortingLayer: {sr.sortingLayerName}\n";
|
||||
report += $" SortingOrder: {sr.sortingOrder}\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
report += $" SpriteRenderer: 未找到!\n";
|
||||
}
|
||||
|
||||
report += "\n";
|
||||
}
|
||||
|
||||
// 检查是否都在同一个SortingLayer
|
||||
string[] layers = new string[allSorters.Length];
|
||||
for (int i = 0; i < allSorters.Length; i++)
|
||||
{
|
||||
SpriteRenderer sr = allSorters[i].GetComponentInChildren<SpriteRenderer>();
|
||||
if (sr != null)
|
||||
layers[i] = sr.sortingLayerName;
|
||||
}
|
||||
|
||||
bool allSameLayer = true;
|
||||
if (layers.Length > 0)
|
||||
{
|
||||
string firstLayer = layers[0];
|
||||
foreach (string layer in layers)
|
||||
{
|
||||
if (layer != firstLayer)
|
||||
{
|
||||
allSameLayer = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!allSameLayer)
|
||||
{
|
||||
report += "⚠️ 警告: 物体不在同一个SortingLayer!\n";
|
||||
report += "这可能导致排序不正确。\n";
|
||||
}
|
||||
|
||||
Debug.Log(report);
|
||||
EditorUtility.DisplayDialog("诊断完成", $"诊断信息已输出到控制台\n\n请查看Unity Console窗口", "确定");
|
||||
}
|
||||
|
||||
[MenuItem("Tools/Y-Sort/测试YSorter计算")]
|
||||
public static void TestYSorterCalculation()
|
||||
{
|
||||
GameObject selected = Selection.activeGameObject;
|
||||
|
||||
if (selected == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", "请先选择一个物体", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
YSorter sorter = selected.GetComponent<YSorter>();
|
||||
if (sorter == null)
|
||||
{
|
||||
EditorUtility.DisplayDialog("提示", $"选中的物体 {selected.name} 没有YSorter组件", "确定");
|
||||
return;
|
||||
}
|
||||
|
||||
// 通过反射获取私有字段
|
||||
var baseOrderField = typeof(YSorter).GetField("baseSortingOrder",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
var scaleFactorField = typeof(YSorter).GetField("scaleFactor",
|
||||
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance);
|
||||
|
||||
int baseOrder = (int)baseOrderField.GetValue(sorter);
|
||||
float scaleFactor = (float)scaleFactorField.GetValue(sorter);
|
||||
|
||||
Vector3 pos = selected.transform.position;
|
||||
int expectedOrder = baseOrder + Mathf.RoundToInt(pos.z * scaleFactor);
|
||||
|
||||
SpriteRenderer sr = sorter.GetComponentInChildren<SpriteRenderer>();
|
||||
int actualOrder = sr != null ? sr.sortingOrder : -1;
|
||||
|
||||
string msg = $"物体: {selected.name}\n";
|
||||
msg += $"位置: {pos}\n";
|
||||
msg += $"Z坐标: {pos.z:F3}\n";
|
||||
msg += $"baseSortingOrder: {baseOrder}\n";
|
||||
msg += $"scaleFactor: {scaleFactor}\n";
|
||||
msg += $"期望SortingOrder: {expectedOrder}\n";
|
||||
msg += $"实际SortingOrder: {actualOrder}\n\n";
|
||||
|
||||
if (expectedOrder == actualOrder)
|
||||
{
|
||||
msg += "✓ 排序正确";
|
||||
}
|
||||
else
|
||||
{
|
||||
msg += "✗ 排序不匹配!";
|
||||
}
|
||||
|
||||
EditorUtility.DisplayDialog("YSorter测试", msg, "确定");
|
||||
Debug.Log(msg);
|
||||
}
|
||||
}
|
||||
11
unity/Assets/Editor/YSortDebugger.cs.meta
Normal file
11
unity/Assets/Editor/YSortDebugger.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: CSkWsXyqUSrgcqGVp1KhCflPlY95SPuT+0n6ZO4FNbzx7EhZFVzsyec=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
173
unity/Assets/Editor/YSortEditorWindow.cs
Normal file
173
unity/Assets/Editor/YSortEditorWindow.cs
Normal file
@@ -0,0 +1,173 @@
|
||||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
/// <summary>
|
||||
/// Y-Sort 批量添加工具 - EditorWindow版本
|
||||
/// </summary>
|
||||
public class YSortEditorWindow : EditorWindow
|
||||
{
|
||||
private Vector2 scrollPosition;
|
||||
private List<string> logMessages = new List<string>();
|
||||
|
||||
[MenuItem("Tools/Y-Sort/Y-Sort管理工具")]
|
||||
public static void ShowWindow()
|
||||
{
|
||||
GetWindow<YSortEditorWindow>("Y-Sort管理工具");
|
||||
}
|
||||
|
||||
private void OnGUI()
|
||||
{
|
||||
GUILayout.Label("Y-Sort批量添加", EditorStyles.boldLabel);
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
if (GUILayout.Button("添加YSorter到Player预制体", GUILayout.Height(30)))
|
||||
{
|
||||
AddToPlayer();
|
||||
}
|
||||
|
||||
if (GUILayout.Button("添加YSorter到Enemy预制体", GUILayout.Height(30)))
|
||||
{
|
||||
AddToEnemy();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
if (GUILayout.Button("批量添加到所有场景预制体 (2.5D Engine/Prefabs)", GUILayout.Height(30)))
|
||||
{
|
||||
AddToAllPrefabs();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
|
||||
if (GUILayout.Button("清除日志", GUILayout.Height(20)))
|
||||
{
|
||||
logMessages.Clear();
|
||||
Repaint();
|
||||
}
|
||||
|
||||
GUILayout.Space(10);
|
||||
GUILayout.Label("日志输出:", EditorStyles.boldLabel);
|
||||
|
||||
scrollPosition = GUILayout.BeginScrollView(scrollPosition, GUILayout.ExpandHeight(true));
|
||||
foreach (string msg in logMessages)
|
||||
{
|
||||
GUILayout.Label(msg);
|
||||
}
|
||||
GUILayout.EndScrollView();
|
||||
}
|
||||
|
||||
private void AddLog(string msg)
|
||||
{
|
||||
logMessages.Add(msg);
|
||||
Debug.Log(msg);
|
||||
Repaint();
|
||||
}
|
||||
|
||||
private void AddToPlayer()
|
||||
{
|
||||
string path = "Assets/2.5D Engine/Player.prefab";
|
||||
AddYSorterToPrefab(path, 2000, "Player");
|
||||
}
|
||||
|
||||
private void AddToEnemy()
|
||||
{
|
||||
string path = "Assets/2.5D Engine/Enemy.prefab";
|
||||
AddYSorterToPrefab(path, 2000, "Enemy");
|
||||
}
|
||||
|
||||
private void AddToAllPrefabs()
|
||||
{
|
||||
string folder = "Assets/2.5D Engine/Prefabs";
|
||||
string[] files = System.IO.Directory.GetFiles(folder, "*.prefab", System.IO.SearchOption.AllDirectories);
|
||||
|
||||
int count = 0;
|
||||
foreach (string file in files)
|
||||
{
|
||||
string normalizedPath = file.Replace("\\", "/");
|
||||
string prefabName = System.IO.Path.GetFileNameWithoutExtension(file);
|
||||
int baseOrder = DetermineBaseOrder(prefabName);
|
||||
|
||||
if (AddYSorterToPrefab(normalizedPath, baseOrder, prefabName))
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
AddLog($"完成!共添加了 {count} 个YSorter");
|
||||
}
|
||||
|
||||
private bool AddYSorterToPrefab(string path, int baseSortingOrder, string displayName)
|
||||
{
|
||||
GameObject prefab = AssetDatabase.LoadAssetAtPath<GameObject>(path);
|
||||
if (prefab == null)
|
||||
{
|
||||
AddLog($"错误: 找不到预制体 {displayName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 检查是否已有YSorter
|
||||
if (prefab.GetComponentInChildren<YSorter>() != null)
|
||||
{
|
||||
AddLog($"跳过 {displayName} - 已有YSorter");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 打开预制体进行编辑
|
||||
string assetPath = AssetDatabase.GetAssetPath(prefab);
|
||||
GameObject prefabRoot = PrefabUtility.LoadPrefabContents(assetPath);
|
||||
|
||||
if (prefabRoot == null)
|
||||
{
|
||||
AddLog($"错误: 无法加载预制体内容 {displayName}");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 添加YSorter组件
|
||||
YSorter ySorter = prefabRoot.AddComponent<YSorter>();
|
||||
|
||||
// 设置baseSortingOrder
|
||||
SerializedObject so = new SerializedObject(ySorter);
|
||||
SerializedProperty prop = so.FindProperty("baseSortingOrder");
|
||||
if (prop != null)
|
||||
{
|
||||
prop.intValue = baseSortingOrder;
|
||||
so.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
// 保存
|
||||
PrefabUtility.SaveAsPrefabAsset(prefabRoot, assetPath);
|
||||
PrefabUtility.UnloadPrefabContents(prefabRoot);
|
||||
|
||||
AddLog($"✓ 已添加 YSorter 到 {displayName} (baseSortingOrder: {baseSortingOrder})");
|
||||
return true;
|
||||
}
|
||||
|
||||
private int DetermineBaseOrder(string prefabName)
|
||||
{
|
||||
string nameLower = prefabName.ToLower();
|
||||
|
||||
if (nameLower.Contains("ground") || nameLower.Contains("floor"))
|
||||
return 0;
|
||||
if (nameLower.Contains("shadow"))
|
||||
return 500;
|
||||
if (nameLower.Contains("grass") || nameLower.Contains("flower") ||
|
||||
nameLower.Contains("leaf") || nameLower.Contains("leaves"))
|
||||
return 800;
|
||||
if (nameLower.Contains("rock") || nameLower.Contains("bamboo") ||
|
||||
nameLower.Contains("barrel") || nameLower.Contains("box") ||
|
||||
nameLower.Contains("log") || nameLower.Contains("board") ||
|
||||
nameLower.Contains("bush") || nameLower.Contains("bonfire") ||
|
||||
nameLower.Contains("candle") || nameLower.Contains("carcasses") ||
|
||||
nameLower.Contains("lotus") || nameLower.Contains("pedestal") ||
|
||||
nameLower.Contains("scarecrow") || nameLower.Contains("skeleton") ||
|
||||
nameLower.Contains("skull") || nameLower.Contains("spear") ||
|
||||
nameLower.Contains("statue") || nameLower.Contains("tent") ||
|
||||
nameLower.Contains("tenticles") || nameLower.Contains("wooden") ||
|
||||
nameLower.Contains("bag") || nameLower.Contains("house"))
|
||||
return 1000;
|
||||
|
||||
return 1000;
|
||||
}
|
||||
}
|
||||
11
unity/Assets/Editor/YSortEditorWindow.cs.meta
Normal file
11
unity/Assets/Editor/YSortEditorWindow.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: DHhNtyKoVXmD0PMFCorGxxPHUaBpiHhn0MMdJ4PaV2ygEFgYTD9bPtk=
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user