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:
2026-06-02 16:10:44 +08:00
parent bcd8b0e90b
commit 06048c966a
47 changed files with 1912 additions and 1195 deletions

View File

@@ -2,7 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using BaseGames.Boss;
@@ -15,6 +15,8 @@ using BaseGames.Parry;
using BaseGames.Player;
using BaseGames.Player.States;
using BaseGames.Skills;
using BaseGames.Editor.Combat;
using BaseGames.Editor.Skills;
namespace BaseGames.Editor
{
@@ -59,6 +61,7 @@ namespace BaseGames.Editor
// 小怪类型选择 — 具体敌人类型
private int _enemyTypeIndex = 0;
private SceneObjectPlacerTool.EnemyBodyColliderType _enemyBodyCollider = SceneObjectPlacerTool.EnemyBodyColliderType.Box;
private static readonly (string id, string name)[] EnemyTypes =
{
("E001", "草蛭"),
@@ -195,6 +198,8 @@ namespace BaseGames.Editor
var jumpGroup = MakeActionGroup();
jumpGroup.Add(MakeJumpButton("Data Hub武器/技能/形态)", DataHubWindow.Open));
jumpGroup.Add(MakeJumpButton("武器 HitBox 向导", WeaponHitBoxWizard.Open));
jumpGroup.Add(MakeJumpButton("技能 HitBox 向导", SkillHitBoxWizard.Open));
jumpGroup.Add(MakeJumpButton("SO 全局校验", SOValidationRunner.ValidateMenu));
root.Add(jumpGroup);
@@ -248,8 +253,9 @@ namespace BaseGames.Editor
root.Add(MakeSectionHeader("▶ 专项编辑器"));
var jumpGroup = MakeActionGroup();
jumpGroup.Add(MakeJumpButton("Data Hub敌人数据", DataHubWindow.Open));
jumpGroup.Add(MakeJumpButton("SO 全局校验", SOValidationRunner.ValidateMenu));
jumpGroup.Add(MakeJumpButton("Data Hub敌人数据", DataHubWindow.Open));
jumpGroup.Add(MakeJumpButton("武器 HitBox 向导", WeaponHitBoxWizard.Open));
jumpGroup.Add(MakeJumpButton("SO 全局校验", SOValidationRunner.ValidateMenu));
root.Add(jumpGroup);
return root;
@@ -286,11 +292,16 @@ namespace BaseGames.Editor
container.Add(MakeSeparator());
container.Add(MakeSectionHeader("▶ 场景搭建"));
container.Add(MakeHelpBox("在当前活动场景中放置完整组件树并自动绑定已有 SO。"));
container.Add(MakeHelpBox("在当前活动场景中放置完整组件树(含 Visual 子节点对齐 Collider并自动绑定已有 SO。"));
var colliderField = new UnityEngine.UIElements.EnumField("主体碰撞器类型", _enemyBodyCollider);
colliderField.RegisterValueChangedCallback(evt => _enemyBodyCollider = (SceneObjectPlacerTool.EnemyBodyColliderType)evt.newValue);
container.Add(colliderField);
var sceneGroup = MakeActionGroup();
string sceneLabel = $"放置 {id} {name} 到场景";
sceneGroup.Add(MakeSceneButton(sceneLabel, () => PlaceSpecificEnemy(id)));
sceneGroup.Add(MakeSceneButton(sceneLabel, () => PlaceSpecificEnemy(id, _enemyBodyCollider)));
sceneGroup.Add(MakeSceneButton("迁移选中对象 Visual 节点", MigrateSelectedEnemyVisualNode));
container.Add(sceneGroup);
}
@@ -330,15 +341,17 @@ namespace BaseGames.Editor
var sceneGroup = MakeActionGroup();
sceneGroup.Add(MakeSceneButton("放置嘲风到场景并绑定 SO", SceneObjectPlacerTool.PlaceChaoFeng));
sceneGroup.Add(MakeSceneButton("迁移选中对象 Visual 节点", MigrateSelectedEnemyVisualNode));
root.Add(sceneGroup);
root.Add(MakeSeparator());
root.Add(MakeSectionHeader("▶ 专项编辑器"));
var jumpGroup = MakeActionGroup();
jumpGroup.Add(MakeJumpButton("Boss 技能序列查看器", BossSkillSequenceWindow.OpenWindow));
jumpGroup.Add(MakeJumpButton("Data HubBoss技能", DataHubWindow.Open));
jumpGroup.Add(MakeJumpButton("SO 全局校验", SOValidationRunner.ValidateMenu));
jumpGroup.Add(MakeJumpButton("Boss 技能序列查看器", BossSkillSequenceWindow.OpenWindow));
jumpGroup.Add(MakeJumpButton("武器 HitBox 向导", WeaponHitBoxWizard.Open));
jumpGroup.Add(MakeJumpButton("Data HubBoss技能", DataHubWindow.Open));
jumpGroup.Add(MakeJumpButton("SO 全局校验", SOValidationRunner.ValidateMenu));
root.Add(jumpGroup);
return root;
@@ -603,23 +616,45 @@ namespace BaseGames.Editor
$"全部 {id} SO 已创建(已存在的跳过)。\n请放置到场景后检查组件绑定。", "确定");
}
private static void PlaceSpecificEnemy(string id)
private static void PlaceSpecificEnemy(string id, SceneObjectPlacerTool.EnemyBodyColliderType bodyCollider)
{
switch (id)
{
case "E001": SceneObjectPlacerTool.PlaceE001_CaoZhi(); break;
case "E002": SceneObjectPlacerTool.PlaceE002_HuangZhi(); break;
case "E003": SceneObjectPlacerTool.PlaceE003_YouZhi_Enemy(); break;
case "E004": SceneObjectPlacerTool.PlaceE004_ZhiMu_Enemy(); break;
case "E005": SceneObjectPlacerTool.PlaceE005_FeiZhi_Enemy(); break;
case "E006": SceneObjectPlacerTool.PlaceE006_Huan(); break;
case "E001": SceneObjectPlacerTool.PlaceE001_CaoZhi(bodyCollider); break;
case "E002": SceneObjectPlacerTool.PlaceE002_HuangZhi(bodyCollider); break;
case "E003": SceneObjectPlacerTool.PlaceE003_YouZhi_Enemy(bodyCollider); break;
case "E004": SceneObjectPlacerTool.PlaceE004_ZhiMu_Enemy(bodyCollider); break;
case "E005": SceneObjectPlacerTool.PlaceE005_FeiZhi_Enemy(bodyCollider); break;
case "E006": SceneObjectPlacerTool.PlaceE006_Huan(bodyCollider); break;
default:
Debug.LogError($"[CharacterWizardWindow] 未注册的敌人 id '{id}',请在 SceneObjectPlacerTool 中实现对应 PlaceE...() 方法并注册。");
SceneObjectPlacerTool.PlaceEnemy();
SceneObjectPlacerTool.PlaceEnemy(bodyCollider);
break;
}
}
private static void MigrateSelectedEnemyVisualNode()
{
var targets = Selection.gameObjects;
if (targets == null || targets.Length == 0)
{
EditorUtility.DisplayDialog("迁移 Visual 节点",
"请先在 Hierarchy 中选中一个或多个 Enemy 对象。", "确定");
return;
}
int count = 0;
foreach (var go in targets)
{
var movement = go.GetComponent<EnemyMovement>();
if (movement != null) { movement.SetupVisualNode(); count++; }
}
if (count == 0)
EditorUtility.DisplayDialog("迁移 Visual 节点",
"选中的对象中没有找到 EnemyMovement 组件。", "确定");
else
Debug.Log($"[CharacterWizardWindow] 已对 {count} 个对象完成 Visual 节点迁移。");
}
// ── SO 资产工厂:嘲风 Boss ─────────────────────────────────────────────
private static void CreateChaoFengStatsSO()