using System.IO; using UnityEditor; using UnityEngine; using BaseGames.Combat; namespace BaseGames.Editor.Skills { /// /// 向导:一键生成技能 HitBox Prefab(W-11)。 /// 菜单:BaseGames / Create / Skill HitBox Prefab /// /// 生成路径规范:Assets/_Game/Prefabs/Skills/SKL_{skillId}_HitBox.prefab /// Prefab 结构(以 hitBoxCount=1 为例): /// [SKL_{skillId}_HitBox] ← SkillHitBoxInstance (_hitBoxes 自动赋值) /// └── [HitBox_0] ← PolygonCollider2D(IsTrigger) + HitBox, Layer=PlayerHitBox /// public class SkillHitBoxWizard : ScriptableWizard { private const string OutputFolder = "Assets/_Game/Prefabs/Skills"; [MenuItem("BaseGames/Create/Skill HitBox Prefab", priority = 201)] public static void Open() => DisplayWizard("Skill HitBox Prefab 向导", "创建"); [Tooltip("技能唯一 ID,如 SkySlash。Prefab 将命名为 SKL_{skillId}_HitBox")] public string skillId = ""; [Tooltip("多段伤害时可设置 >1(每段一个 HitBox 子节点)")] [Range(1, 4)] public int hitBoxCount = 1; // ── 向导回调 ────────────────────────────────────────────────────────── private void OnWizardUpdate() { isValid = !string.IsNullOrWhiteSpace(skillId); helpString = isValid ? $"将创建:{OutputFolder}/SKL_{skillId}_HitBox.prefab({hitBoxCount} 个 HitBox)" : "请输入 skillId(技能唯一 ID,如 SkySlash)。"; } private void OnWizardCreate() { if (string.IsNullOrWhiteSpace(skillId)) { EditorUtility.DisplayDialog("错误", "skillId 不能为空。", "确认"); return; } string prefabName = $"SKL_{skillId}_HitBox"; string assetPath = $"{OutputFolder}/{prefabName}.prefab"; string fullPath = Path.Combine( Path.GetDirectoryName(Application.dataPath)!, assetPath.Replace('/', Path.DirectorySeparatorChar)); if (File.Exists(fullPath)) { if (!EditorUtility.DisplayDialog("已存在", $"{assetPath}\n\n该 Prefab 已存在,是否覆盖?", "覆盖", "取消")) return; } EditorScaffoldUtils.EnsureFolder(OutputFolder); int hitBoxLayer = LayerMask.NameToLayer("PlayerHitBox"); if (hitBoxLayer < 0) { Debug.LogWarning("[SkillHitBoxWizard] 未找到 Physics Layer 'PlayerHitBox',子节点 Layer 将设为 Default。"); hitBoxLayer = 0; } // ── 构建 Prefab ──────────────────────────────────────────────── var root = new GameObject(prefabName); var instance = root.AddComponent(); var so = new SerializedObject(instance); var hbRefs = new HitBox[hitBoxCount]; for (int i = 0; i < hitBoxCount; i++) { var child = new GameObject($"HitBox_{i}"); child.transform.SetParent(root.transform, false); child.layer = hitBoxLayer; // PolygonCollider2D 默认菱形(1×0.5),策划在 Scene 中调整具体形状 var poly = child.AddComponent(); poly.isTrigger = true; poly.SetPath(0, new Vector2[] { new(-0.5f, -0.25f), new(0.5f, -0.25f), new(0.5f, 0.25f), new(-0.5f, 0.25f), }); hbRefs[i] = child.AddComponent(); } // 将 HitBox[] 赋值给 _hitBoxes var arrayProp = so.FindProperty("_hitBoxes"); if (arrayProp != null && arrayProp.isArray) { arrayProp.arraySize = hitBoxCount; for (int i = 0; i < hitBoxCount; i++) arrayProp.GetArrayElementAtIndex(i).objectReferenceValue = hbRefs[i]; so.ApplyModifiedPropertiesWithoutUndo(); } else { Debug.LogWarning("[SkillHitBoxWizard] 未找到 SkillHitBoxInstance._hitBoxes 字段,请手动赋值。"); } var prefab = PrefabUtility.SaveAsPrefabAsset(root, assetPath); Object.DestroyImmediate(root); AssetDatabase.Refresh(); if (prefab != null) { EditorScaffoldUtils.PingAndSelect(prefab); Debug.Log($"[SkillHitBoxWizard] 已创建:{assetPath}"); } else { Debug.LogError($"[SkillHitBoxWizard] Prefab 保存失败:{assetPath}"); } } } }