feat: Add HurtBoxOwnerGuard to prevent multiple damage registrations from the same HitBox activation
- Implemented HurtBoxOwnerGuard to ensure that multiple HurtBoxes on the same character do not register damage multiple times during a single HitBox activation. - Added custom editor for HitBox to facilitate the creation of shape colliders with HitBoxColliderProxy. - Introduced PhysicsPerceptionSystem for enemy perception, supporting multiple detection modes including RangeCircle, BatchLOS, FanCast, and BoxCast. - Created EnemyPatrolZone to define patrol and chase areas for enemies, allowing for shared zones among multiple enemies. - Added BD_IsOutsideZone conditional task for Behavior Designer to check if an enemy or player is outside a defined patrol zone.
This commit is contained in:
115
Assets/_Game/Scripts/Editor/Combat/HitBoxEditor.cs
Normal file
115
Assets/_Game/Scripts/Editor/Combat/HitBoxEditor.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using BaseGames.Combat;
|
||||
|
||||
namespace BaseGames.Editor.Combat
|
||||
{
|
||||
/// <summary>
|
||||
/// HitBox 自定义 Inspector。
|
||||
/// 在"多形状碰撞体"面板中提供快捷按钮,一键创建带 HitBoxColliderProxy 的子形状节点。
|
||||
///
|
||||
/// 生成子节点结构:
|
||||
/// [HitBoxParent] ← HitBox 组件(可无 Collider2D)
|
||||
/// ├── [Shape_Box] ← BoxCollider2D(isTrigger) + HitBoxColliderProxy
|
||||
/// ├── [Shape_Circle] ← CircleCollider2D(isTrigger) + HitBoxColliderProxy
|
||||
/// ├── [Shape_Capsule] ← CapsuleCollider2D(isTrigger) + HitBoxColliderProxy
|
||||
/// └── [Shape_Polygon] ← PolygonCollider2D(isTrigger) + HitBoxColliderProxy
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(HitBox))]
|
||||
public class HitBoxEditor : UnityEditor.Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
EditorGUILayout.Space(6);
|
||||
EditorGUILayout.LabelField("── 多形状碰撞体 ──", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox(
|
||||
"在子节点添加 HitBoxColliderProxy + Collider2D 以组合多形状判定盒。\n" +
|
||||
"子节点 Layer 自动继承本节点;Collider2D.IsTrigger 自动设为 true。",
|
||||
MessageType.Info);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("+ Box"))
|
||||
AddShapeChild((HitBox)target, ShapeKind.Box);
|
||||
if (GUILayout.Button("+ Circle"))
|
||||
AddShapeChild((HitBox)target, ShapeKind.Circle);
|
||||
if (GUILayout.Button("+ Capsule"))
|
||||
AddShapeChild((HitBox)target, ShapeKind.Capsule);
|
||||
if (GUILayout.Button("+ Polygon"))
|
||||
AddShapeChild((HitBox)target, ShapeKind.Polygon);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
if (Application.isPlaying)
|
||||
{
|
||||
EditorGUILayout.Space(2);
|
||||
int proxyCount = ((HitBox)target).GetComponentsInChildren<HitBoxColliderProxy>(true).Length;
|
||||
EditorGUILayout.LabelField($"子代 HitBoxColliderProxy 数:{proxyCount}", EditorStyles.miniLabel);
|
||||
Repaint();
|
||||
}
|
||||
}
|
||||
|
||||
// ── 内部工具 ──────────────────────────────────────────────────────────
|
||||
|
||||
private enum ShapeKind { Box, Circle, Capsule, Polygon }
|
||||
|
||||
private static void AddShapeChild(HitBox hitBox, ShapeKind kind)
|
||||
{
|
||||
var child = new GameObject($"[Shape_{kind}]");
|
||||
Undo.RegisterCreatedObjectUndo(child, $"Add HitBox {kind} Shape");
|
||||
child.transform.SetParent(hitBox.transform, false);
|
||||
child.layer = hitBox.gameObject.layer;
|
||||
|
||||
Collider2D col = kind switch
|
||||
{
|
||||
ShapeKind.Box => CreateBox(child),
|
||||
ShapeKind.Circle => CreateCircle(child),
|
||||
ShapeKind.Capsule => CreateCapsule(child),
|
||||
ShapeKind.Polygon => CreatePolygon(child),
|
||||
_ => CreateBox(child),
|
||||
};
|
||||
col.isTrigger = true;
|
||||
|
||||
Undo.AddComponent<HitBoxColliderProxy>(child);
|
||||
|
||||
Selection.activeGameObject = child;
|
||||
EditorGUIUtility.PingObject(child);
|
||||
}
|
||||
|
||||
private static BoxCollider2D CreateBox(GameObject go)
|
||||
{
|
||||
var c = Undo.AddComponent<BoxCollider2D>(go);
|
||||
c.size = new Vector2(1f, 0.5f);
|
||||
return c;
|
||||
}
|
||||
|
||||
private static CircleCollider2D CreateCircle(GameObject go)
|
||||
{
|
||||
var c = Undo.AddComponent<CircleCollider2D>(go);
|
||||
c.radius = 0.4f;
|
||||
return c;
|
||||
}
|
||||
|
||||
private static CapsuleCollider2D CreateCapsule(GameObject go)
|
||||
{
|
||||
var c = Undo.AddComponent<CapsuleCollider2D>(go);
|
||||
c.size = new Vector2(0.5f, 1f);
|
||||
return c;
|
||||
}
|
||||
|
||||
private static PolygonCollider2D CreatePolygon(GameObject go)
|
||||
{
|
||||
var c = Undo.AddComponent<PolygonCollider2D>(go);
|
||||
c.SetPath(0, new Vector2[]
|
||||
{
|
||||
new( 0f, 0.3f),
|
||||
new( 0.5f, 0f ),
|
||||
new( 0f, -0.3f),
|
||||
new(-0.5f, 0f ),
|
||||
});
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
11
Assets/_Game/Scripts/Editor/Combat/HitBoxEditor.cs.meta
Normal file
11
Assets/_Game/Scripts/Editor/Combat/HitBoxEditor.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 414d2528016107148929f4d65d5507e7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -6,9 +6,14 @@ using BaseGames.Combat;
|
||||
namespace BaseGames.Editor
|
||||
{
|
||||
/// <summary>
|
||||
/// HurtBox 运行时注入状态可视化面板。
|
||||
/// 通过 HurtBox 上的 Editor* 属性读取注入状态,以颜色区分是否注入成功。
|
||||
/// 绿色 = 注入完成;橙色 = 未注入(该能力静默不生效);灰色 = 非 PlayMode。
|
||||
/// HurtBox 自定义 Inspector。
|
||||
/// ① 提供"多形状受击区域"快捷按钮:一键创建带 HurtBox 的子形状节点(共享同一 HP 池)。
|
||||
/// ② 运行时注入状态可视化面板:绿色 = 注入完成;橙色 = 未注入;灰色 = 非 PlayMode。
|
||||
///
|
||||
/// 生成子节点结构:
|
||||
/// [HurtBoxParent] ← HurtBox + 任意 Collider2D(主受击区)
|
||||
/// ├── [HurtShape_Box] ← BoxCollider2D(isTrigger) + HurtBox(共享 HP)
|
||||
/// └── [HurtShape_Circle] ← CircleCollider2D(isTrigger) + HurtBox(共享 HP)
|
||||
/// </summary>
|
||||
[CustomEditor(typeof(HurtBox))]
|
||||
public class HurtBoxEditor : UnityEditor.Editor
|
||||
@@ -27,6 +32,24 @@ namespace BaseGames.Editor
|
||||
{
|
||||
DrawDefaultInspector();
|
||||
|
||||
// ── 多形状受击区域 ──────────────────────────────────────────────
|
||||
EditorGUILayout.Space(6);
|
||||
EditorGUILayout.LabelField("── 多形状受击区域 ──", EditorStyles.boldLabel);
|
||||
EditorGUILayout.HelpBox(
|
||||
"在子节点添加 HurtBox + Collider2D 以组合多形状受击区域,各子节点共享同一 HP 池。\n" +
|
||||
"子节点 Layer 自动继承本节点;HurtBoxOwnerGuard 防止同一次攻击重复扣血。",
|
||||
MessageType.Info);
|
||||
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
if (GUILayout.Button("+ Box"))
|
||||
AddShapeChild((HurtBox)target, ShapeKind.Box);
|
||||
if (GUILayout.Button("+ Circle"))
|
||||
AddShapeChild((HurtBox)target, ShapeKind.Circle);
|
||||
if (GUILayout.Button("+ Capsule"))
|
||||
AddShapeChild((HurtBox)target, ShapeKind.Capsule);
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// ── 运行时注入状态 ──────────────────────────────────────────────
|
||||
EditorGUILayout.Space(4);
|
||||
EditorGUILayout.LabelField("── 运行时注入状态 ──", EditorStyles.boldLabel);
|
||||
|
||||
@@ -59,6 +82,34 @@ namespace BaseGames.Editor
|
||||
// 持续刷新(避免只显示初始状态)
|
||||
if (Application.isPlaying) Repaint();
|
||||
}
|
||||
|
||||
// ── 子形状创建工具 ─────────────────────────────────────────────────────
|
||||
|
||||
private enum ShapeKind { Box, Circle, Capsule }
|
||||
|
||||
private static void AddShapeChild(HurtBox hurtBox, ShapeKind kind)
|
||||
{
|
||||
var child = new GameObject($"[HurtShape_{kind}]");
|
||||
Undo.RegisterCreatedObjectUndo(child, $"Add HurtBox {kind} Shape");
|
||||
child.transform.SetParent(hurtBox.transform, false);
|
||||
child.layer = hurtBox.gameObject.layer;
|
||||
|
||||
// 先加 Collider2D 以满足 HurtBox 的 [RequireComponent(typeof(Collider2D))],
|
||||
// 再 AddComponent<HurtBox>() 则不会重复创建 Collider2D。
|
||||
Collider2D col = kind switch
|
||||
{
|
||||
ShapeKind.Box => Undo.AddComponent<BoxCollider2D>(child),
|
||||
ShapeKind.Circle => Undo.AddComponent<CircleCollider2D>(child),
|
||||
ShapeKind.Capsule => Undo.AddComponent<CapsuleCollider2D>(child),
|
||||
_ => Undo.AddComponent<BoxCollider2D>(child),
|
||||
};
|
||||
col.isTrigger = true;
|
||||
|
||||
Undo.AddComponent<HurtBox>(child);
|
||||
|
||||
Selection.activeGameObject = child;
|
||||
EditorGUIUtility.PingObject(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user