摄像机区域的架构改动

This commit is contained in:
2026-05-15 14:47:24 +08:00
parent 1b37297585
commit f264329751
3591 changed files with 1687228 additions and 446503 deletions

View File

@@ -0,0 +1,157 @@
using System;
using UnityEditor;
using UnityEngine;
using BaseGames.Combat;
namespace BaseGames.Editor.Combat
{
/// <summary>
/// DamageSourceSO 增强 InspectorW-09
/// 在默认字段下方追加:
/// ① 伤害预览BaseDamage × DamageMultiplier
/// ② BreakLevel 颜色标签
/// ③ DamageFlags / DamageTags 逐位 Toggle 组(可编辑,替代默认位掩码数字)
/// </summary>
[CustomEditor(typeof(DamageSourceSO))]
public class DamageSourceInspector : UnityEditor.Editor
{
private static readonly Color[] _breakColors =
{
new Color(0.55f, 0.55f, 0.55f), // None — 灰
new Color(0.25f, 0.80f, 0.25f), // Light — 绿
new Color(0.25f, 0.55f, 0.95f), // Medium — 蓝
new Color(1.00f, 0.60f, 0.10f), // Heavy — 橙
new Color(0.90f, 0.15f, 0.15f), // Breaker — 红
};
private static readonly string[] _breakLabels = { "None", "Light", "Medium", "Heavy", "Breaker" };
// ── DamageFlags 枚举值(排除 None=0
private static readonly DamageFlags[] _allFlags =
(DamageFlags[])Enum.GetValues(typeof(DamageFlags));
// ── DamageTags 枚举值(排除 None=0
private static readonly DamageTags[] _allTags =
(DamageTags[])Enum.GetValues(typeof(DamageTags));
public override void OnInspectorGUI()
{
DrawDefaultInspector();
var src = (DamageSourceSO)target;
EditorGUILayout.Space(10);
// ── ① 伤害预览 ────────────────────────────────────────────────────
EditorGUILayout.LabelField("── 伤害预览", EditorStyles.boldLabel);
using (new EditorGUI.DisabledGroupScope(true))
{
int calculated = Mathf.RoundToInt(src.BaseDamage * src.DamageMultiplier);
EditorGUILayout.IntField(
new GUIContent("计算伤害 (Base × Multi)", "= BaseDamage × DamageMultiplier四舍五入"),
calculated);
}
// ── ② BreakLevel 颜色标签 ─────────────────────────────────────────
int idx = Mathf.Clamp((int)src.BreakLevel, 0, _breakColors.Length - 1);
EditorGUILayout.BeginHorizontal();
EditorGUILayout.PrefixLabel(new GUIContent("破霸体等级(颜色预览)"));
var colorStyle = new GUIStyle(EditorStyles.helpBox)
{
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold,
normal = { textColor = Color.white },
};
Color prev = GUI.backgroundColor;
GUI.backgroundColor = _breakColors[idx];
GUILayout.Box(_breakLabels[idx], colorStyle, GUILayout.Width(72), GUILayout.Height(18));
GUI.backgroundColor = prev;
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(8);
// ── ③ DamageFlags Toggle 组 ───────────────────────────────────────
EditorGUILayout.LabelField("── DamageFlags位标志可直接勾选", EditorStyles.boldLabel);
var flagsProp = serializedObject.FindProperty("Flags");
serializedObject.Update();
int flagsVal = flagsProp.intValue;
bool flagsChanged = false;
var validFlags = System.Array.FindAll(_allFlags, f => f != DamageFlags.None);
EditorGUILayout.BeginHorizontal();
int col = 0;
for (int fi = 0; fi < validFlags.Length; fi++)
{
var flag = validFlags[fi];
bool wasSet = (flagsVal & (int)flag) != 0;
bool nowSet = GUILayout.Toggle(wasSet, flag.ToString(),
GUI.skin.button, GUILayout.Height(18), GUILayout.MinWidth(90));
if (nowSet != wasSet)
{
flagsVal = nowSet ? flagsVal | (int)flag : flagsVal & ~(int)flag;
flagsChanged = true;
}
col++;
if (col % 4 == 0 && fi < validFlags.Length - 1)
{
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
}
}
EditorGUILayout.EndHorizontal();
if (flagsChanged)
{
flagsProp.intValue = flagsVal;
serializedObject.ApplyModifiedProperties();
}
// 不安全 Flags 警告
if ((src.Flags & DamageFlags.Unblockable) != 0)
EditorGUILayout.HelpBox("Unblockable玩家无法格挡此伤害请确认设计意图。", MessageType.Warning);
EditorGUILayout.Space(6);
// ── ④ DamageTags Toggle 组 ────────────────────────────────────────
EditorGUILayout.LabelField("── DamageTags交互标签可直接勾选", EditorStyles.boldLabel);
var tagsProp = serializedObject.FindProperty("Tags");
serializedObject.Update();
uint tagsVal = (uint)tagsProp.longValue;
bool tagsChanged = false;
var validTags = System.Array.FindAll(_allTags, t => t != DamageTags.None);
EditorGUILayout.BeginHorizontal();
col = 0;
for (int ti = 0; ti < validTags.Length; ti++)
{
var tag = validTags[ti];
bool wasSet = (tagsVal & (uint)tag) != 0;
bool nowSet = GUILayout.Toggle(wasSet, tag.ToString(),
GUI.skin.button, GUILayout.Height(18), GUILayout.MinWidth(90));
if (nowSet != wasSet)
{
tagsVal = nowSet ? tagsVal | (uint)tag : tagsVal & ~(uint)tag;
tagsChanged = true;
}
col++;
if (col % 4 == 0 && ti < validTags.Length - 1)
{
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
}
}
EditorGUILayout.EndHorizontal();
if (tagsChanged)
{
tagsProp.longValue = tagsVal;
serializedObject.ApplyModifiedProperties();
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 2562d64f9a5ca764981b69d3c62d1c17
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,64 @@
#if UNITY_EDITOR
using UnityEditor;
using UnityEngine;
using BaseGames.Combat;
namespace BaseGames.Editor
{
/// <summary>
/// HurtBox 运行时注入状态可视化面板。
/// 通过 HurtBox 上的 Editor* 属性读取注入状态,以颜色区分是否注入成功。
/// 绿色 = 注入完成;橙色 = 未注入(该能力静默不生效);灰色 = 非 PlayMode。
/// </summary>
[CustomEditor(typeof(HurtBox))]
public class HurtBoxEditor : UnityEditor.Editor
{
// (属性访问器, 标签, 缺席说明)
private static readonly (System.Func<HurtBox, object> getter, string label, string absentNote)[] _fields =
{
(hb => hb.EditorOwner, "Owner (IDamageable)", "— 注入失败ReceiveDamage 将无效"),
(hb => hb.EditorShieldable, "Shieldable", "— 未注入(玩家专属,敌人无需)"),
(hb => hb.EditorParrySystem, "ParrySystem", "— 未注入(弹反静默不生效)"),
(hb => hb.EditorPoiseSource, "PoiseSource", "— 未注入(霸体静默不生效)"),
(hb => hb.EditorStatusEffectable, "StatusEffectable", "— 未注入(状态效果静默不生效)"),
};
public override void OnInspectorGUI()
{
DrawDefaultInspector();
EditorGUILayout.Space(4);
EditorGUILayout.LabelField("── 运行时注入状态 ──", EditorStyles.boldLabel);
if (!Application.isPlaying)
{
EditorGUILayout.HelpBox("进入 PlayMode 后查看注入状态。", MessageType.Info);
return;
}
var hurtBox = (HurtBox)target;
foreach (var (getter, label, absentNote) in _fields)
{
var value = getter(hurtBox);
bool present = value != null;
var savedColor = GUI.contentColor;
GUI.contentColor = present
? new Color(0.3f, 0.9f, 0.4f) // 绿
: new Color(1.0f, 0.6f, 0.1f); // 橙
string displayValue = present
? $"✓ {value.GetType().Name}"
: $"✗ null {absentNote}";
EditorGUILayout.LabelField(label, displayValue);
GUI.contentColor = savedColor;
}
// 持续刷新(避免只显示初始状态)
if (Application.isPlaying) Repaint();
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 8650ccc7960fe304a95be1c629ef7b1e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,339 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Animancer;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using BaseGames.Combat;
using BaseGames.Player;
namespace BaseGames.Editor.Combat
{
/// <summary>
/// 武器数据管理窗口W-02
/// 技术UI Toolkit TwoPaneSplitView。
/// 菜单BaseGames / Data / Weapon Editor
///
/// 左栏:可搜索的 WeaponSO 列表 + [新建] 按钮。
/// 右栏:选中武器的完整属性编辑 + HitBox Prefab 结构校验 + 快速操作。
/// </summary>
public class WeaponEditorWindow : EditorWindow
{
private static readonly StyleSheet _sharedUSS;
static WeaponEditorWindow()
{
_sharedUSS = AssetDatabase.LoadAssetAtPath<StyleSheet>(
"Assets/_Game/Scripts/Editor/UIToolkit/Editor.uss");
}
[MenuItem("BaseGames/Data/Weapon Editor", priority = 100)]
public static void Open()
{
var wnd = GetWindow<WeaponEditorWindow>();
wnd.titleContent = new GUIContent("Weapon Editor");
wnd.minSize = new Vector2(680, 400);
}
// ── 状态 ─────────────────────────────────────────────────────────────
private List<WeaponSO> _weapons = new();
private List<WeaponSO> _filtered = new();
private ListView _listView;
private VisualElement _detailRoot;
private string _searchText = "";
private InspectorElement _currentInspector;
// ── 生命周期 ──────────────────────────────────────────────────────────
public void CreateGUI()
{
if (_sharedUSS != null)
rootVisualElement.styleSheets.Add(_sharedUSS);
// Toolbar
var toolbar = new Toolbar();
var searchField = new ToolbarSearchField { style = { flexGrow = 1 } };
searchField.RegisterValueChangedCallback(e =>
{
_searchText = e.newValue;
RefreshFilter();
});
toolbar.Add(searchField);
var btnCreate = new ToolbarButton(CreateNewWeapon) { text = "+ 新建武器" };
toolbar.Add(btnCreate);
var btnRefresh = new ToolbarButton(RefreshAll) { text = "↺" };
btnRefresh.tooltip = "重新扫描 Project 中的 WeaponSO 资产";
toolbar.Add(btnRefresh);
rootVisualElement.Add(toolbar);
// Split view
var split = new TwoPaneSplitView(0, 220, TwoPaneSplitViewOrientation.Horizontal);
// ── 左栏 ──────────────────────────────────────────────────────
var leftPane = new VisualElement { style = { minWidth = 140 } };
_listView = new ListView
{
selectionType = SelectionType.Single,
fixedItemHeight = 22,
makeItem = MakeListItem,
bindItem = BindListItem,
style = { flexGrow = 1 },
};
_listView.selectionChanged += OnSelectionChanged;
leftPane.Add(_listView);
split.Add(leftPane);
// ── 右栏 ──────────────────────────────────────────────────────
_detailRoot = new ScrollView { style = { flexGrow = 1 } };
_detailRoot.AddToClassList("detail-panel");
split.Add(_detailRoot);
rootVisualElement.Add(split);
RefreshAll();
}
private void OnFocus() => RefreshAll();
// ── 列表构建 ──────────────────────────────────────────────────────────
private void RefreshAll()
{
_weapons = EditorScaffoldUtils.FindAllAssetsOfType<WeaponSO>();
_weapons.Sort((a, b) => string.Compare(
a.weaponId, b.weaponId, StringComparison.OrdinalIgnoreCase));
RefreshFilter();
}
private void RefreshFilter()
{
if (string.IsNullOrEmpty(_searchText))
{
_filtered = new List<WeaponSO>(_weapons);
}
else
{
string s = _searchText;
_filtered = _weapons.Where(w => w != null &&
(w.weaponId?.Contains(s, StringComparison.OrdinalIgnoreCase) == true ||
w.displayName?.Contains(s, StringComparison.OrdinalIgnoreCase) == true)).ToList();
}
_listView.itemsSource = _filtered;
_listView.Rebuild();
}
private static VisualElement MakeListItem()
{
var label = new Label();
label.AddToClassList("list-item");
return label;
}
private void BindListItem(VisualElement element, int index)
{
var label = (Label)element;
var weapon = _filtered.Count > index ? _filtered[index] : null;
if (weapon == null) { label.text = "(null)"; return; }
label.text = string.IsNullOrEmpty(weapon.displayName)
? weapon.weaponId
: $"{weapon.weaponId} <color=#888>({weapon.displayName})</color>";
}
// ── 详情面板 ──────────────────────────────────────────────────────────
private void OnSelectionChanged(IEnumerable<object> items)
{
_detailRoot.Clear();
_currentInspector = null;
var weapon = items.FirstOrDefault() as WeaponSO;
if (weapon == null) return;
// 标题
var title = new Label(
string.IsNullOrEmpty(weapon.displayName) ? weapon.weaponId : $"{weapon.weaponId} · {weapon.displayName}")
{
style =
{
fontSize = 14,
unityFontStyleAndWeight = FontStyle.Bold,
marginBottom = 6,
}
};
_detailRoot.Add(title);
// HitBox Prefab 状态
BuildHitBoxStatus(weapon);
// 连击链预览
BuildComboPreview(weapon);
// Inspector 完整属性编辑
_currentInspector = new InspectorElement(weapon);
_detailRoot.Add(_currentInspector);
// 操作按钮
var btnRow = new VisualElement();
btnRow.AddToClassList("action-buttons");
var btnSelect = new Button(() => EditorScaffoldUtils.PingAndSelect(weapon))
{ text = "在 Project 中定位" };
var btnInspector = new Button(() => Selection.activeObject = weapon)
{ text = "在 Inspector 中打开" };
var btnWizard = new Button(WeaponHitBoxWizard.Open)
{ text = "HitBox Prefab 向导…" };
btnRow.Add(btnSelect);
btnRow.Add(btnInspector);
btnRow.Add(btnWizard);
_detailRoot.Add(btnRow);
}
/// <summary>attack1 → attack2 → attack3 连击链数值横排预览。</summary>
private void BuildComboPreview(WeaponSO weapon)
{
// 只在有至少一个连击数据时显示
if (weapon.attack1Source == null && weapon.attack2Source == null && weapon.attack3Source == null)
return;
var section = new Label("连击链预览") { style = { unityFontStyleAndWeight = FontStyle.Bold, marginBottom = 4 } };
_detailRoot.Add(section);
var chain = new VisualElement();
chain.AddToClassList("stats-preview");
void AddSegment(string label, ClipTransition clip, DamageSourceSO src, bool addArrow)
{
var cell = new VisualElement
{
style =
{
alignItems = Align.Center,
marginRight = 4,
paddingLeft = 6,
paddingRight = 6,
paddingTop = 3,
paddingBottom = 3,
backgroundColor = new Color(0.25f, 0.25f, 0.28f, 1f),
borderTopLeftRadius = 3,
borderTopRightRadius = 3,
borderBottomLeftRadius = 3,
borderBottomRightRadius = 3,
}
};
// 段名
cell.Add(new Label(label)
{
style = { fontSize = 10, color = new Color(0.65f, 0.65f, 0.65f) }
});
// Clip 名称
string clipName = clip?.Clip != null ? clip.Clip.name : "<无动画>";
cell.Add(new Label(clipName)
{
style = { fontSize = 11, unityFontStyleAndWeight = FontStyle.Bold }
});
// 伤害数值
if (src != null)
{
int dmg = Mathf.RoundToInt(src.BaseDamage * src.DamageMultiplier);
cell.Add(new Label($"伤害 {dmg} [{src.BreakLevel}]")
{
style = { fontSize = 10, color = new Color(1f, 0.7f, 0.3f) }
});
}
else
{
cell.Add(new Label("(无 DamageSource)")
{
style = { fontSize = 10, color = new Color(0.8f, 0.3f, 0.3f) }
});
}
chain.Add(cell);
if (addArrow)
chain.Add(new Label("→") { style = { alignSelf = Align.Center, marginLeft = 2, marginRight = 2 } });
}
AddSegment("攻击1", weapon.attack1Clip, weapon.attack1Source, true);
AddSegment("攻击2", weapon.attack2Clip, weapon.attack2Source, true);
AddSegment("攻击3", weapon.attack3Clip, weapon.attack3Source, false);
_detailRoot.Add(chain);
// 追加空中/上/下攻击的简要行
var extraRow = new VisualElement
{
style = { flexDirection = FlexDirection.Row, flexWrap = Wrap.Wrap, marginBottom = 6, paddingLeft = 6 }
};
void ExtraStat(string label, DamageSourceSO src)
{
if (src == null) return;
int dmg = Mathf.RoundToInt(src.BaseDamage * src.DamageMultiplier);
extraRow.Add(new Label($"{label}{dmg} [{src.BreakLevel}]")
{
style = { marginRight = 14, fontSize = 11, color = new Color(0.7f, 0.7f, 0.7f) }
});
}
ExtraStat("空中", weapon.airAttackSource);
ExtraStat("上挑", weapon.upAttackSource);
ExtraStat("下砸", weapon.downAttackSource);
if (extraRow.childCount > 0)
_detailRoot.Add(extraRow);
}
private void BuildHitBoxStatus(WeaponSO weapon)
{
HelpBoxMessageType msgType;
string msg;
if (weapon.hitBoxPrefab == null)
{
msgType = HelpBoxMessageType.Warning;
msg = "hitBoxPrefab 未赋值!请创建并关联武器 HitBox Prefab。";
}
else if (weapon.hitBoxPrefab.GetComponent<WeaponHitBoxInstance>() == null)
{
msgType = HelpBoxMessageType.Error;
msg = $"hitBoxPrefab「{weapon.hitBoxPrefab.name}」缺少 WeaponHitBoxInstance 组件!";
}
else
{
msgType = HelpBoxMessageType.Info;
msg = $"HitBox Prefab 结构正常:{weapon.hitBoxPrefab.name}";
}
_detailRoot.Add(new HelpBox(msg, msgType) { style = { marginBottom = 6 } });
}
// ── 新建武器 ──────────────────────────────────────────────────────────
private void CreateNewWeapon()
{
var asset = EditorScaffoldUtils.CreateSOAsset<WeaponSO>(
"Assets/_Game/Data/Player/Weapons", "WeaponSO_New");
if (asset != null)
{
RefreshAll();
int idx = _filtered.IndexOf(asset);
if (idx >= 0)
_listView.SetSelection(idx);
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 7cc9d2828e2d3f9458e74befbb0e2b4e
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -0,0 +1,181 @@
using System.IO;
using UnityEditor;
using UnityEngine;
using BaseGames.Combat;
using BaseGames.Player;
namespace BaseGames.Editor.Combat
{
/// <summary>
/// 向导:一键生成武器 HitBox PrefabW-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]
/// </summary>
public class WeaponHitBoxWizard : ScriptableWizard
{
private const string OutputFolder = "Assets/_Game/Prefabs/Weapons";
/// <summary>每个方向可选的 Collider 形状。</summary>
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<WeaponHitBoxWizard>("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<WeaponHitBoxInstance>();
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<HitBox>();
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<BoxCollider2D>();
c.isTrigger = true;
c.size = new Vector2(1f, 0.5f);
break;
}
case ColliderShape.Capsule:
{
var c = go.AddComponent<CapsuleCollider2D>();
c.isTrigger = true;
c.size = new Vector2(0.5f, 1f);
break;
}
case ColliderShape.Polygon:
{
var c = go.AddComponent<PolygonCollider2D>();
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;
}
}
}
}
}

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 85778ca4e33d8d441abe05d60450d1ab
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: