Files
5.24BuYu/Assets/MyGame/Editor/ChouJiangItemBinder.cs
2026-10-20 18:30:03 +08:00

286 lines
12 KiB
C#
Raw Permalink 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 System.Collections.Generic;
using System.Text;
using TMPro;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// ChouJiangItem 批量绑定工具(编辑器专用)。
/// 把预制体拖进窗口(或 Project 里选中预制体/文件夹后点「从当前选中添加」),再点「开始绑定」:
/// 工具会找出所有名字以 ItemGrid 开头的格子,给每个格子挂上 ChouJiangItem,并按子物体名自动赋值:
///
/// Item → img_Icon(该节点上的 Image 组件)
/// Label → tmp_Count(该节点上是 TMP 时)/ tmp_Count_Old(该节点上是 UGUI Text 时)
/// 道具名字节点 → tmp_Name / tmp_Name_Old(节点名在窗口里配,留空则不处理)
/// GridMask → obj_Drawn
/// eff_xuan → obj_Selecting
/// eff_xuanzhong → obj_Landed
///
/// 节点名先在**直接子物体**里精确匹配(ItemGrid 下还有同名的 GridMask 子节点,
/// 所以必须优先取直接子物体),找不到再退到子树里找第一个同名节点。
/// itemId 不动,交给 ChouJiangToggle 按列表顺序自动编号。
/// </summary>
public class ChouJiangItemBinder : EditorWindow
{
const string CellPrefix = "ItemGrid"; // 格子节点名前缀
const string NodeIcon = "Item"; // 奖品图片
const string NodeLabel = "Label"; // 数量文本
const string NodeDrawn = "GridMask"; // 已抽中遮罩
const string NodeSelecting = "eff_xuan"; // 选中高亮
const string NodeLanded = "eff_xuanzhong"; // 抽中停留
[SerializeField] List<GameObject> targets = new List<GameObject>();
[SerializeField] string nameNode = ""; // 道具名字节点名,留空=不处理
[SerializeField] bool keepExisting = false; // 已有引用不覆盖
Vector2 scroll;
[MenuItem("Tools/抽奖/批量绑定 ChouJiangItem")]
static void Open()
{
ChouJiangItemBinder w = GetWindow<ChouJiangItemBinder>("抽奖 Item 绑定");
w.minSize = new Vector2(440f, 360f);
}
void OnGUI()
{
EditorGUILayout.LabelField("把预制体拖进来(或 Project 里选中后点下面的按钮)", EditorStyles.boldLabel);
Rect dropArea = GUILayoutUtility.GetRect(0f, 46f, GUILayout.ExpandWidth(true));
GUI.Box(dropArea, "拖预制体 / 文件夹到这里");
HandleDrag(dropArea);
EditorGUILayout.BeginHorizontal();
if (GUILayout.Button("从当前选中添加")) AddFromSelection();
if (GUILayout.Button("清空列表")) targets.Clear();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space();
nameNode = EditorGUILayout.TextField("道具名字节点名(留空=不处理)", nameNode);
keepExisting = EditorGUILayout.Toggle("保留已有引用(不覆盖)", keepExisting);
EditorGUILayout.LabelField($"待绑定预制体:{targets.Count} 个");
scroll = EditorGUILayout.BeginScrollView(scroll, GUILayout.Height(110f));
for (int i = targets.Count - 1; i >= 0; i--)
{
EditorGUILayout.BeginHorizontal();
targets[i] = (GameObject)EditorGUILayout.ObjectField(targets[i], typeof(GameObject), false);
if (GUILayout.Button("×", GUILayout.Width(24f))) targets.RemoveAt(i);
EditorGUILayout.EndHorizontal();
}
EditorGUILayout.EndScrollView();
EditorGUILayout.Space();
using (new EditorGUI.DisabledScope(targets.Count == 0))
{
if (GUILayout.Button("开始绑定", GUILayout.Height(30f))) Run();
}
}
// ---------------- 目标收集 ----------------
void HandleDrag(Rect area)
{
Event e = Event.current;
if (e == null || !area.Contains(e.mousePosition)) return;
if (e.type != EventType.DragUpdated && e.type != EventType.DragPerform) return;
DragAndDrop.visualMode = DragAndDropVisualMode.Copy;
if (e.type == EventType.DragPerform)
{
DragAndDrop.AcceptDrag();
foreach (Object o in DragAndDrop.objectReferences) AddTarget(o);
e.Use();
}
}
void AddFromSelection()
{
int before = targets.Count;
foreach (Object o in Selection.objects) AddTarget(o);
if (targets.Count == before) Debug.LogWarning("[抽奖绑定] 当前选中里没有预制体或含预制体的文件夹");
}
void AddTarget(Object o)
{
// 文件夹:递归收集里面的预制体
string path = AssetDatabase.GetAssetPath(o);
if (!string.IsNullOrEmpty(path) && AssetDatabase.IsValidFolder(path))
{
string[] guids = AssetDatabase.FindAssets("t:Prefab", new[] { path });
foreach (string g in guids) AddTarget(AssetDatabase.LoadAssetAtPath<GameObject>(AssetDatabase.GUIDToAssetPath(g)));
return;
}
GameObject go = o as GameObject;
if (go == null)
{
Debug.LogWarning("[抽奖绑定] 只支持预制体(GameObject),已跳过:" + o);
return;
}
if (string.IsNullOrEmpty(path) || !path.EndsWith(".prefab"))
{
Debug.LogWarning("[抽奖绑定] 不是预制体文件,已跳过:" + go.name);
return;
}
if (!targets.Contains(go)) targets.Add(go);
}
// ---------------- 执行绑定 ----------------
void Run()
{
List<string> paths = new List<string>();
foreach (GameObject go in targets)
{
string p = AssetDatabase.GetAssetPath(go);
if (!string.IsNullOrEmpty(p) && !paths.Contains(p)) paths.Add(p);
}
StringBuilder sb = new StringBuilder();
int cellAll = 0, missAll = 0;
for (int i = 0; i < paths.Count; i++)
{
string path = paths[i];
EditorUtility.DisplayProgressBar("绑定 ChouJiangItem", path, (float)i / paths.Count);
try
{
int cells, miss;
BindOne(path, out cells, out miss);
cellAll += cells;
missAll += miss;
sb.AppendLine($"{System.IO.Path.GetFileName(path)}:格子 {cells} 个" +
(miss > 0 ? $",{miss} 个有缺失节点(见 Console 警告)" : ",全部绑定完成"));
}
catch (System.Exception ex)
{
sb.AppendLine($"{System.IO.Path.GetFileName(path)}:失败 {ex.Message}");
Debug.LogError($"[抽奖绑定] {path} 处理失败:{ex}");
}
}
EditorUtility.ClearProgressBar();
targets.Clear();
Debug.Log($"[抽奖绑定] 完成,共 {cellAll} 个格子,{missAll} 个有缺失节点\n{sb}");
}
void BindOne(string path, out int cellCount, out int missingCount)
{
cellCount = 0;
missingCount = 0;
GameObject root = PrefabUtility.LoadPrefabContents(path);
try
{
Transform[] all = root.GetComponentsInChildren<Transform>(true);
for (int i = 0; i < all.Length; i++)
{
Transform t = all[i];
if (!t.name.StartsWith(CellPrefix)) continue;
// 防止把同名前缀的容器误当格子:必须带 Item 或 GridMask 直接子物体
if (t.Find(NodeIcon) == null && t.Find(NodeDrawn) == null) continue;
cellCount++;
ChouJiangItem item = t.GetComponent<ChouJiangItem>();
if (item == null) item = t.gameObject.AddComponent<ChouJiangItem>();
int missing = 0;
// 奖品图片
Image icon = FindComp<Image>(t, NodeIcon);
if (icon != null) { if (!keepExisting || item.img_Icon == null) item.img_Icon = icon; }
else missing++;
// 数量文本:TMP 优先,没有 TMP 再用 UGUI Text(两版互斥)
Transform label = FindNode(t, NodeLabel);
TextMeshProUGUI tmp = label != null ? label.GetComponent<TextMeshProUGUI>() : null;
Text txt = label != null ? label.GetComponent<Text>() : null;
if (tmp != null)
{
if (!keepExisting || item.tmp_Count == null) { item.tmp_Count = tmp; item.tmp_Count_Old = null; }
}
else if (txt != null)
{
if (!keepExisting || item.tmp_Count_Old == null) { item.tmp_Count_Old = txt; item.tmp_Count = null; }
}
else missing++;
// 道具名字(节点名可配,留空不处理)
if (!string.IsNullOrEmpty(nameNode))
{
Transform nameT = FindNode(t, nameNode);
TextMeshProUGUI nameTmp = nameT != null ? nameT.GetComponent<TextMeshProUGUI>() : null;
Text nameTxt = nameT != null ? nameT.GetComponent<Text>() : null;
if (nameTmp != null)
{
if (!keepExisting || item.tmp_Name == null) { item.tmp_Name = nameTmp; item.tmp_Name_Old = null; }
}
else if (nameTxt != null)
{
if (!keepExisting || item.tmp_Name_Old == null) { item.tmp_Name_Old = nameTxt; item.tmp_Name = null; }
}
}
// 三个状态 GameObject
Transform drawn = FindNode(t, NodeDrawn);
if (drawn != null) { if (!keepExisting || item.obj_Drawn == null) item.obj_Drawn = drawn.gameObject; }
else missing++;
Transform selecting = FindNode(t, NodeSelecting);
if (selecting != null) { if (!keepExisting || item.obj_Selecting == null) item.obj_Selecting = selecting.gameObject; }
else missing++;
Transform landed = FindNode(t, NodeLanded);
if (landed != null) { if (!keepExisting || item.obj_Landed == null) item.obj_Landed = landed.gameObject; }
else missing++;
if (missing > 0)
{
missingCount++;
Debug.LogWarning($"[抽奖绑定] {System.IO.Path.GetFileName(path)} → 格子 {t.name} 缺 {missing} 个节点" +
"(Item / Label / GridMask / eff_xuan / eff_xuanzhong)");
}
}
if (cellCount == 0)
{
Debug.LogWarning($"[抽奖绑定] {path} 里没找到名字以 {CellPrefix} 开头、且带 Item 或 GridMask 的格子");
}
else
{
PrefabUtility.SaveAsPrefabAsset(root, path);
}
}
finally
{
PrefabUtility.UnloadPrefabContents(root);
}
}
// ---------------- 查找 ----------------
/// <summary>先找直接子物体(精确同名),找不到再在整个子树里找第一个同名节点</summary>
static Transform FindNode(Transform cell, string name)
{
Transform direct = cell.Find(name);
if (direct != null) return direct;
Transform[] all = cell.GetComponentsInChildren<Transform>(true);
for (int i = 0; i < all.Length; i++)
{
if (all[i] != cell && all[i].name == name) return all[i];
}
return null;
}
static T FindComp<T>(Transform cell, string name) where T : Component
{
Transform n = FindNode(cell, name);
return n != null ? n.GetComponent<T>() : null;
}
}