using System.IO; using UnityEditor; using UnityEngine; using BaseGames.Combat; using BaseGames.Player; namespace BaseGames.Editor.Combat { /// /// 向导:一键生成武器 HitBox Prefab(W-10)。 /// 菜单:BaseGames / Create / Weapon HitBox Prefab /// /// 生成路径规范:Assets/_Game/Prefabs/Weapons/WPN_{weaponId}_HitBox.prefab /// Prefab 结构: /// [WPN_{weaponId}_HitBox] ← WeaponHitBoxInstance /// ├── [HitBox_Ground] ← Collider2D(IsTrigger, 形状可选) + HitBox, Layer=PlayerHitBox /// ├── [HitBox_Up] /// ├── [HitBox_Down] /// └── [HitBox_Air] /// public class WeaponHitBoxWizard : ScriptableWizard { private const string OutputFolder = "Assets/_Game/Prefabs/Weapons"; /// 每个方向可选的 Collider 形状。 public enum ColliderShape { [Tooltip("BoxCollider2D — 矩形,适合水平/垂直扫击")] Box, [Tooltip("CapsuleCollider2D — 胶囊体,适合刺击或弧形")] Capsule, [Tooltip("PolygonCollider2D(菱形默认点)— 适合不规则斩击")] Polygon, } [MenuItem("BaseGames/Create/Weapon HitBox Prefab", priority = 200)] public static void Open() => DisplayWizard("Weapon HitBox Prefab 向导", "创建"); [Tooltip("武器唯一 ID,如 SkyBlade。Prefab 将命名为 WPN_{weaponId}_HitBox")] public string weaponId = ""; [Header("包含哪些攻击方向")] public bool includeGround = true; public bool includeUp = true; public bool includeDown = true; public bool includeAir = true; [Header("每个方向的 Collider 形状")] [Tooltip("Ground / 落地攻击的碰撞体形状")] public ColliderShape groundShape = ColliderShape.Box; [Tooltip("Up / 上挑攻击的碰撞体形状")] public ColliderShape upShape = ColliderShape.Capsule; [Tooltip("Down / 下砸攻击的碰撞体形状")] public ColliderShape downShape = ColliderShape.Box; [Tooltip("Air / 空中攻击的碰撞体形状")] public ColliderShape airShape = ColliderShape.Capsule; // ── 向导回调 ────────────────────────────────────────────────────────── private void OnWizardUpdate() { isValid = !string.IsNullOrWhiteSpace(weaponId); helpString = isValid ? $"将创建:{OutputFolder}/WPN_{weaponId}_HitBox.prefab" : "请输入 weaponId(武器唯一 ID,如 SkyBlade)。"; } private void OnWizardCreate() { if (string.IsNullOrWhiteSpace(weaponId)) { EditorUtility.DisplayDialog("错误", "weaponId 不能为空。", "确认"); return; } string prefabName = $"WPN_{weaponId}_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("[WeaponHitBoxWizard] 未找到 Physics Layer 'PlayerHitBox',子节点 Layer 将设为 Default。"); hitBoxLayer = 0; } // ── 构建 Prefab ──────────────────────────────────────────────── var root = new GameObject(prefabName); var instance = root.AddComponent(); var so = new SerializedObject(instance); void AddDirection(bool enabled, string nodeName, string fieldName, ColliderShape shape) { if (!enabled) return; var child = new GameObject(nodeName); child.transform.SetParent(root.transform, false); child.layer = hitBoxLayer; AddCollider(child, shape); var hb = child.AddComponent(); var prop = so.FindProperty(fieldName); if (prop != null) prop.objectReferenceValue = hb; } AddDirection(includeGround, "HitBox_Ground", "_hitBoxGround", groundShape); AddDirection(includeUp, "HitBox_Up", "_hitBoxUp", upShape); AddDirection(includeDown, "HitBox_Down", "_hitBoxDown", downShape); AddDirection(includeAir, "HitBox_Air", "_hitBoxAir", airShape); so.ApplyModifiedPropertiesWithoutUndo(); var prefab = PrefabUtility.SaveAsPrefabAsset(root, assetPath); Object.DestroyImmediate(root); AssetDatabase.Refresh(); if (prefab != null) { EditorScaffoldUtils.PingAndSelect(prefab); Debug.Log($"[WeaponHitBoxWizard] 已创建:{assetPath}"); } else { Debug.LogError($"[WeaponHitBoxWizard] Prefab 保存失败:{assetPath}"); } } // ── 辅助:按形状添加 2D 碰撞体 ──────────────────────────────────────── private static void AddCollider(GameObject go, ColliderShape shape) { switch (shape) { case ColliderShape.Box: { var c = go.AddComponent(); c.isTrigger = true; c.size = new Vector2(1f, 0.5f); break; } case ColliderShape.Capsule: { var c = go.AddComponent(); c.isTrigger = true; c.size = new Vector2(0.5f, 1f); break; } case ColliderShape.Polygon: { var c = go.AddComponent(); c.isTrigger = true; // 默认菱形点(0.5 × 0.5 单位) c.SetPath(0, new Vector2[] { new( 0f, 0.3f), new( 0.5f, 0f ), new( 0f, -0.3f), new(-0.5f, 0f ), }); break; } } } } }