- QuestSO: Add ValidateBranchCycles() DFS detection for branches[].nextQuest loop - QuestSO: Mark three legacy prerequisite fields with v2.0 removal warning in Tooltip - IQuestManager: Add QuestLockReason enum + QuestLockInfo struct (strongly-typed lock info) - IQuestManager: Add GetQuestLockInfo() method to interface; GetQuestLockReason() now delegates to it - IQuestEventSource: Add OnQuestStateChanged(questId, oldState, newState) unified event - QuestManager: Implement GetQuestLockInfo(); fire OnQuestStateChanged on all state transitions - DialogueManager: Add one-frame yield in HandleChoices before ShowChoices (skip-debounce fix) - DialogueManager: Increment _playbackId in ForceEnd() to invalidate residual choice callbacks - DialogueSequenceSO: Add UNITY_EDITOR debug log in TryGetActiveVariant on variant match - WorldStateRegistry: Add OnBatchStateChanged event + BatchMark() batch-write API - DialogueModule: List badge shows warning indicator for unconditional-shadowing variants - DialogueModule: BuildVariantsCard shows logic mode (AND/OR) alongside flag conditions Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
77 lines
3.3 KiB
C#
77 lines
3.3 KiB
C#
using BaseGames.Core.Events;
|
||
using UnityEngine;
|
||
|
||
namespace BaseGames.World
|
||
{
|
||
/// <summary>
|
||
/// 区域到达触发器(架构 22_QuestChallengeModule §3 扩展)。
|
||
/// 挂在场景中的 2D 触发碰撞体上,玩家进入时广播 EVT_AreaReached 事件,
|
||
/// 驱动 ReachAreaObjective(markerTag 模式)的任务目标进度。
|
||
///
|
||
/// 使用方式:
|
||
/// 1. 在目标区域创建空 GameObject,添加 Collider2D(勾选 IsTrigger)。
|
||
/// 2. 挂上 TriggerZone,填写 markerTag(与 ReachAreaObjective.markerTag 保持一致)。
|
||
/// 3. 将 EVT_AreaReached 事件频道资产拖入 _onAreaReached 字段。
|
||
/// 4. 将 QuestManager 同一 _onAreaReached 频道字段也引用同一资产即可联通。
|
||
/// </summary>
|
||
[RequireComponent(typeof(Collider2D))]
|
||
public class TriggerZone : MonoBehaviour
|
||
{
|
||
[Header("区域标识")]
|
||
[Tooltip("区域唯一标记(如 \"ZONE_ForestEntry\")。需与 ReachAreaObjective.markerTag 保持完全一致。")]
|
||
[SerializeField] private string _markerTag;
|
||
|
||
[Tooltip("触发后是否只生效一次(推荐勾选,防止重复广播)。")]
|
||
[SerializeField] private bool _triggerOnce = true;
|
||
|
||
[Header("玩家层级")]
|
||
[Tooltip("玩家所在的物理层,用于过滤非玩家碰撞体进入。只有属于此层的碰撞体才触发广播。")]
|
||
[SerializeField] private LayerMask _playerLayer;
|
||
|
||
[Header("事件频道")]
|
||
[Tooltip("EVT_AreaReached 事件频道(StringEventChannelSO)。触发时以 markerTag 为 payload 广播。\n" +
|
||
"需与 QuestManager._onAreaReached 字段引用同一资产。")]
|
||
[SerializeField] private StringEventChannelSO _onAreaReached;
|
||
|
||
private bool _triggered;
|
||
|
||
/// <summary>区域唯一标记(只读)。供编辑器工具(QuestModule 批量验证)交叉比对使用。</summary>
|
||
public string MarkerTag => _markerTag;
|
||
|
||
private void OnTriggerEnter2D(Collider2D other)
|
||
{
|
||
if (_triggered) return;
|
||
if (string.IsNullOrEmpty(_markerTag)) return;
|
||
if (((1 << other.gameObject.layer) & _playerLayer.value) == 0) return;
|
||
|
||
_onAreaReached?.Raise(_markerTag);
|
||
|
||
if (_triggerOnce) _triggered = true;
|
||
}
|
||
|
||
/// <summary>重置触发状态(如读档/重新进入关卡时调用)。</summary>
|
||
public void ResetTrigger() => _triggered = false;
|
||
|
||
#if UNITY_EDITOR
|
||
private void OnDrawGizmos()
|
||
{
|
||
if (string.IsNullOrEmpty(_markerTag)) return;
|
||
Gizmos.color = new Color(0.2f, 0.8f, 0.4f, 0.25f);
|
||
var col = GetComponent<Collider2D>();
|
||
if (col != null) Gizmos.DrawWireSphere(col.bounds.center, 0.3f);
|
||
}
|
||
|
||
private void OnDrawGizmosSelected()
|
||
{
|
||
var col = GetComponent<Collider2D>();
|
||
if (col == null) return;
|
||
Gizmos.color = new Color(0.2f, 0.9f, 0.4f, 0.5f);
|
||
Gizmos.DrawWireCube(col.bounds.center, col.bounds.size);
|
||
UnityEditor.Handles.Label(
|
||
col.bounds.center + Vector3.up * (col.bounds.extents.y + 0.2f),
|
||
string.IsNullOrEmpty(_markerTag) ? "(未设置 markerTag)" : _markerTag);
|
||
}
|
||
#endif
|
||
}
|
||
}
|