feat: Implement Room Streaming System
- Add RoomStreamingManager to manage room loading and unloading based on player proximity. - Create StreamingBudgetConfigSO for memory and performance budgeting of the streaming system. - Introduce TransitionDirector to handle seamless and atmospheric fade transitions between rooms. - Develop WorldGraph to represent room connectivity and facilitate neighbor queries and distance calculations. - Implement RoomNode and RoomEdge classes to structure room data and connections.
This commit is contained in:
89
Assets/_Game/Scripts/Enemies/Perception/EnemySensorHub.cs
Normal file
89
Assets/_Game/Scripts/Enemies/Perception/EnemySensorHub.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using Micosmo.SensorToolkit;
|
||||
|
||||
namespace BaseGames.Enemies.Perception
|
||||
{
|
||||
/// <summary>
|
||||
/// 敌人感知 Hub(架构 07_EnemyModule §9)。
|
||||
/// 集中暴露挂载在敌人 Prefab 上的各种 SensorToolkit Sensor,BD 任务通过
|
||||
/// 字符串槽位查询,避免在 BD 任务 Inspector 中拖具体 Sensor 引用。
|
||||
///
|
||||
/// 典型槽位命名约定:
|
||||
/// - "aggro" : RangeSensor2D,玩家入侵警戒圈
|
||||
/// - "attack_melee" : RangeSensor2D,近战触发距离
|
||||
/// - "attack_range" : RangeSensor2D,远程触发距离
|
||||
/// - "los" : LOSSensor2D,视线
|
||||
/// - "wall_ahead" : RaySensor2D,前方墙体检测
|
||||
/// - "ledge" : RaySensor2D,前方悬崖检测
|
||||
/// </summary>
|
||||
[DisallowMultipleComponent]
|
||||
public sealed class EnemySensorHub : MonoBehaviour, IPerceptionSystem
|
||||
{
|
||||
[System.Serializable]
|
||||
public struct SensorSlot
|
||||
{
|
||||
public string slotName;
|
||||
public Sensor sensor;
|
||||
}
|
||||
|
||||
[SerializeField] private SensorSlot[] _slots;
|
||||
|
||||
private Dictionary<string, Sensor> _map;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_map = new Dictionary<string, Sensor>(_slots?.Length ?? 0);
|
||||
if (_slots == null) return;
|
||||
for (int i = 0; i < _slots.Length; i++)
|
||||
{
|
||||
var s = _slots[i];
|
||||
if (s.sensor != null && !string.IsNullOrEmpty(s.slotName))
|
||||
_map[s.slotName] = s.sensor;
|
||||
}
|
||||
}
|
||||
|
||||
public Sensor Get(string slotName)
|
||||
{
|
||||
if (_map == null || string.IsNullOrEmpty(slotName)) return null;
|
||||
_map.TryGetValue(slotName, out var s);
|
||||
return s;
|
||||
}
|
||||
|
||||
public bool IsDetecting(string slotName, GameObject target)
|
||||
{
|
||||
var s = Get(slotName);
|
||||
return s != null && target != null && s.IsDetected(target);
|
||||
}
|
||||
|
||||
public bool HasAnyDetection(string slotName)
|
||||
{
|
||||
var s = Get(slotName);
|
||||
if (s == null) return false;
|
||||
foreach (var _ in s.Detections) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public GameObject GetFirstDetection(string slotName)
|
||||
{
|
||||
var s = Get(slotName);
|
||||
if (s == null) return null;
|
||||
foreach (var go in s.Detections) return go;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停或恢复所有插槽的 Sensor。
|
||||
/// 当敌人超出 QuotaManager 活跃范围时调用(关闭),归入活跃范围时恢复(开启)。
|
||||
/// </summary>
|
||||
public void SetSuspended(bool suspended)
|
||||
{
|
||||
if (_slots == null) return;
|
||||
for (int i = 0; i < _slots.Length; i++)
|
||||
{
|
||||
var sensor = _slots[i].sensor;
|
||||
if (sensor != null) sensor.enabled = !suspended;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 58cb3ac0e49c151429cad39d3e164a3d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Enemies.Perception
|
||||
{
|
||||
/// <summary>
|
||||
/// 威胁评估器:在原始 LOS 结果上叠加感知反应延迟,使敌人不会瞬间发现玩家。
|
||||
///
|
||||
/// 工作原理:
|
||||
/// <list type="bullet">
|
||||
/// <item>读取 <see cref="EnemyBase.HasLineOfSight"/>(原始 LOS,BatchLOSSystem 写入)。</item>
|
||||
/// <item>连续感知到玩家超过 <see cref="reactionDelay"/> 秒后,<see cref="IsThreatDetected"/> 才变为 true。</item>
|
||||
/// <item>一旦丢失 LOS,<see cref="IsThreatDetected"/> 立即重置为 false(保持对"躲起来"的快速响应)。</item>
|
||||
/// </list>
|
||||
///
|
||||
/// 挂载:添加到与 <see cref="EnemyBase"/> 相同的 GameObject。
|
||||
/// <see cref="EnemyBase.IsPlayerVisible()"/> 会自动路由至此组件(如果存在)。
|
||||
/// </summary>
|
||||
[RequireComponent(typeof(EnemyBase))]
|
||||
public class EnemyThreatAssessor : MonoBehaviour
|
||||
{
|
||||
[Tooltip("持续感知此时长(s)后判定为威胁;模拟敌人的反应时间")]
|
||||
[SerializeField] [Min(0f)] private float reactionDelay = 0.15f;
|
||||
|
||||
private EnemyBase _enemy;
|
||||
private float _losAccumulator;
|
||||
|
||||
/// <summary>是否已将玩家判定为当前威胁(含反应延迟)。</summary>
|
||||
public bool IsThreatDetected { get; private set; }
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
_enemy = GetComponent<EnemyBase>();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (_enemy == null) return;
|
||||
|
||||
bool hasLos = _enemy.HasLineOfSight;
|
||||
|
||||
if (hasLos)
|
||||
{
|
||||
_losAccumulator += Time.deltaTime;
|
||||
if (_losAccumulator >= reactionDelay)
|
||||
IsThreatDetected = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
_losAccumulator = 0f;
|
||||
IsThreatDetected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
Assets/_Game/Scripts/Enemies/Perception/IPerceptionSystem.cs
Normal file
23
Assets/_Game/Scripts/Enemies/Perception/IPerceptionSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace BaseGames.Enemies.Perception
|
||||
{
|
||||
/// <summary>
|
||||
/// 敌人感知系统接口。
|
||||
/// EnemyBase 通过此接口与感知实现解耦,支持运行时替换(SensorToolkit / 自定义实现)。
|
||||
/// </summary>
|
||||
public interface IPerceptionSystem
|
||||
{
|
||||
/// <summary>指定槽位是否检测到任意目标。</summary>
|
||||
bool HasAnyDetection(string slotName);
|
||||
|
||||
/// <summary>指定槽位是否正在检测 target 对象。</summary>
|
||||
bool IsDetecting(string slotName, GameObject target);
|
||||
|
||||
/// <summary>返回指定槽位第一个检测到的对象,无检测则返回 null。</summary>
|
||||
GameObject GetFirstDetection(string slotName);
|
||||
|
||||
/// <summary>暂停或恢复感知系统(LOD / 超出活跃范围时调用)。</summary>
|
||||
void SetSuspended(bool suspended);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e0f4d9ac2fab74409dfa127cc6a67d7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
43
Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs
Normal file
43
Assets/_Game/Scripts/Enemies/Perception/SensorSlotNames.cs
Normal file
@@ -0,0 +1,43 @@
|
||||
namespace BaseGames.Enemies.Perception
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="EnemySensorHub"/> 槽位名称常量。
|
||||
///
|
||||
/// 统一定义字符串键,避免在 BD Task Inspector 和代码中散布魔法字符串。
|
||||
/// Prefab 上 EnemySensorHub 组件的 slotName 字段必须与此处常量保持一致。
|
||||
/// </summary>
|
||||
public static class SensorSlotNames
|
||||
{
|
||||
/// <summary>
|
||||
/// 警戒范围(RangeSensor2D):玩家进入此圈触发 Alert 阶段。
|
||||
/// 通常半径大于攻击范围,小于视线检测范围。
|
||||
/// </summary>
|
||||
public const string Aggro = "aggro";
|
||||
|
||||
/// <summary>
|
||||
/// 视线检测(LOSSensor2D):敌我之间无遮挡时持续为 true。
|
||||
/// 由 BatchLOSSystem 批量计算,BD_IsPlayerVisible 读取结果。
|
||||
/// </summary>
|
||||
public const string LOS = "los";
|
||||
|
||||
/// <summary>
|
||||
/// 近战攻击范围(RangeSensor2D):玩家进入时触发近战攻击条件。
|
||||
/// </summary>
|
||||
public const string AttackMelee = "attack_melee";
|
||||
|
||||
/// <summary>
|
||||
/// 远程攻击范围(RangeSensor2D):玩家进入时触发远程攻击条件。
|
||||
/// </summary>
|
||||
public const string AttackRange = "attack_range";
|
||||
|
||||
/// <summary>
|
||||
/// 前方墙体(RaySensor2D):水平方向检测,用于巡逻转向。
|
||||
/// </summary>
|
||||
public const string WallAhead = "wall_ahead";
|
||||
|
||||
/// <summary>
|
||||
/// 前方悬崖(RaySensor2D):斜向下检测地面是否存在,用于巡逻转向。
|
||||
/// </summary>
|
||||
public const string Ledge = "ledge";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f449ccbbaeb13f468dd74ca202c4937
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user