diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs b/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs new file mode 100644 index 00000000..b6a492d9 --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs @@ -0,0 +1,96 @@ +using UnityEngine; +using BaseGames.AI; +using BaseGames.Core.Events; + +namespace BaseGames.Enemies +{ + /// + /// Boss 开战触发:玩家进入竞技场触发区时,广播开战事件并让 Boss 的决策层从静置态进战。 + /// 此前开战事件频道全项目零发送方,Boss 永远不会开战。 + /// + /// 放在 Boss 房间入口的触发区上(Collider2D + isTrigger)。只触发一次。 + /// 漏配一律在 Awake 显式报错——静默不触发会表现为"Boss 站着不动",极难定位。 + /// + [RequireComponent(typeof(Collider2D))] + public sealed class BossFightTrigger : MonoBehaviour + { + [Header("目标 Boss")] + [Tooltip("要唤醒的 Boss 的决策层组件")] + [SerializeField] private EnemyAiBrain _bossBrain; + + [Header("事件频道")] + [Tooltip("EVT_BossFightStarted — payload 为 bossId,驱动全局状态机 / 后处理 / HUD")] + [SerializeField] private StringEventChannelSO _onBossFightStarted; + [Tooltip("EVT_BossFightEnded — 布尔开关频道,true 表示开始,驱动 Boss 血条与 BGM 切换")] + [SerializeField] private BoolEventChannelSO _onBossFightToggled; + + [Header("标识")] + [Tooltip("Boss id,与 Boss 身上配置的 bossId 一致")] + [SerializeField] private string _bossId; + + [Header("触发条件")] + [Tooltip("玩家所在的层")] + [SerializeField] private LayerMask _playerLayers; + + private bool _fired; + + // 漏配处理沿用 EnemyAiBrain 的形态:逐条点名报错 + 停用组件。 + // 不做「缺谁就跳过谁」的部分执行——半开的战斗(有 BGM 没血条、Boss 不动) + // 比彻底不开更难查,问题必须停在配置这一层。 + private void Awake() + { + bool valid = true; + + var col = GetComponent(); + if (!col.isTrigger) + { + Debug.LogError($"[BossFightTrigger] {name} 的 Collider2D 未勾选 Is Trigger,无法触发开战。", this); + valid = false; + } + if (_bossBrain == null) + { + Debug.LogError($"[BossFightTrigger] {name} 未指定目标 Boss 的 EnemyAiBrain,开战不会生效。", this); + valid = false; + } + if (string.IsNullOrEmpty(_bossId)) + { + Debug.LogError($"[BossFightTrigger] {name} 未填 bossId,BGM / 血条无法定位该 Boss。", this); + valid = false; + } + if (_onBossFightStarted == null) + { + Debug.LogError($"[BossFightTrigger] {name} 未绑定开战事件频道,全局状态机 / 后处理不会响应开战。", this); + valid = false; + } + if (_onBossFightToggled == null) + { + Debug.LogError($"[BossFightTrigger] {name} 未绑定开战开关频道,Boss 血条与战斗 BGM 不会切换。", this); + valid = false; + } + // 掩码为空 = 任何层都过不了下面的判定,触发区形同虚设。 + if (_playerLayers.value == 0) + { + Debug.LogError($"[BossFightTrigger] {name} 的玩家层掩码为空,触发区永远不会被触发。", this); + valid = false; + } + + if (!valid) enabled = false; + } + + // 能走到这里说明 Awake 的配置校验全过了,故不再判空—— + // 判空只会把已经报过错的漏配再盖一层。 + private void OnTriggerEnter2D(Collider2D other) + { + if (_fired) return; + if ((_playerLayers.value & (1 << other.gameObject.layer)) == 0) return; + + _fired = true; + _onBossFightStarted.Raise(_bossId); + _onBossFightToggled.Raise(true); + _bossBrain.Send(AiSignal.Engaged); + } + + /// 供存档回载 / 重试时复位(Boss 未被击败则重新可触发)。 + public void ResetTrigger() => _fired = false; + } +} diff --git a/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs.meta b/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs.meta new file mode 100644 index 00000000..1438cefb --- /dev/null +++ b/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd8a9d00a149c124a9bd90ffba663a32 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: