Files
zeling_v2/Assets/_Game/Scripts/Enemies/Boss/BossFightTrigger.cs
T
joywayer 990f90d383 feat(enemy): BossFightTrigger——补上缺失的开战触发
开战事件频道此前全项目零发送方,Boss 永远不开战。
触发时广播开战事件并向 Boss 决策层发 AiSignal.Engaged。
漏配目标 Brain / bossId / 两个频道 / 玩家层掩码 / isTrigger 一律逐条点名报错并停用组件,
不做部分执行——半开的战斗比彻底不开更难查。
2026-07-30 14:55:05 +08:00

97 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using UnityEngine;
using BaseGames.AI;
using BaseGames.Core.Events;
namespace BaseGames.Enemies
{
/// <summary>
/// Boss 开战触发:玩家进入竞技场触发区时,广播开战事件并让 Boss 的决策层从静置态进战。
/// 此前开战事件频道全项目零发送方,Boss 永远不会开战。
///
/// 放在 Boss 房间入口的触发区上(Collider2D + isTrigger)。只触发一次。
/// 漏配一律在 Awake 显式报错——静默不触发会表现为"Boss 站着不动",极难定位。
/// </summary>
[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<Collider2D>();
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} 未填 bossIdBGM / 血条无法定位该 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);
}
/// <summary>供存档回载 / 重试时复位(Boss 未被击败则重新可触发)。</summary>
public void ResetTrigger() => _fired = false;
}
}