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:
2026-05-23 19:10:29 +08:00
parent 81c326af53
commit a1b4e629aa
165 changed files with 7904 additions and 313 deletions

View File

@@ -0,0 +1,149 @@
#if UNITY_EDITOR || DEVELOPMENT_BUILD
using UnityEngine;
using BaseGames.Enemies.Abilities;
using BaseGames.Enemies.StatusEffects;
namespace BaseGames.Enemies
{
/// <summary>
/// 运行时 AI 状态调试叠加层(仅 Editor
/// 挂到敌人 Prefab 根节点,运行时在场景视图 / GameView 上显示:
/// - AiPhase / EnemyStateType
/// - 当前激活的能力名称及运行状态
/// - LOS 状态 / 到玩家距离
/// - Boss技能执行状态 + 各技能冷却倒计时
/// </summary>
[AddComponentMenu("BaseGames/Debug/Enemy Debug Overlay")]
public sealed class EnemyDebugOverlay : MonoBehaviour
{
[Header("显示选项")]
[Tooltip("在 Scene 视图中绘制 GUI 标签")]
[SerializeField] private bool _showInSceneView = true;
[Tooltip("在 Game 视图中绘制 GUI 标签(需要 OnGUI")]
[SerializeField] private bool _showInGameView = false;
[Tooltip("文字背景透明度")]
[SerializeField, Range(0f, 1f)] private float _bgAlpha = 0.65f;
private EnemyBase _enemy;
private BossBase _boss;
private EnemyStatusEffectManager _statusMgr;
private GUIStyle _labelStyle;
private GUIStyle _boxStyle;
private bool _stylesInit;
private void Awake()
{
_enemy = GetComponent<EnemyBase>();
_boss = GetComponent<BossBase>();
_statusMgr = GetComponent<EnemyStatusEffectManager>();
}
// ── Scene 视图标签UnityEditor.Handles.Label─────────────────────
private void OnDrawGizmos()
{
if (!_showInSceneView || !Application.isPlaying || _enemy == null) return;
var lines = BuildLines();
var label = string.Join("\n", lines);
var pos = transform.position + Vector3.up * 2.2f;
UnityEditor.Handles.Label(pos, label);
}
// ── Game 视图 GUIOnGUI──────────────────────────────────────────
private void OnGUI()
{
if (!_showInGameView || !Application.isPlaying || _enemy == null) return;
InitStyles();
var cam = UnityEngine.Camera.main;
if (cam == null) return;
Vector3 worldPos = transform.position + Vector3.up * 2.2f;
Vector3 screen = cam.WorldToScreenPoint(worldPos);
if (screen.z < 0f) return;
float screenY = Screen.height - screen.y;
var lines = BuildLines();
var text = string.Join("\n", lines);
var size = _labelStyle.CalcSize(new GUIContent(text));
var rect = new Rect(screen.x - size.x * 0.5f, screenY - size.y, size.x + 8f, size.y + 4f);
GUI.Box(rect, GUIContent.none, _boxStyle);
GUI.Label(rect, text, _labelStyle);
}
// ── 内部 ──────────────────────────────────────────────────────────
private System.Collections.Generic.List<string> BuildLines()
{
var list = new System.Collections.Generic.List<string>(8);
// 名称
list.Add($"<b>{gameObject.name}</b>");
// AiPhase + EnemyState
list.Add($"Phase: <color=#ffcc44>{_enemy.CurrentAiPhase}</color> State: <color=#88ddff>{_enemy.CurrentState}</color>");
// 距离 + LOS
float dist = _enemy.Stats != null ? Mathf.Sqrt(_enemy.Stats.SqrDistanceToPlayer) : -1f;
bool los = _enemy.IsPlayerVisible();
var losColor = los ? "#44ff88" : "#ff6644";
list.Add($"Dist: {dist:F1}m LOS: <color={losColor}>{los}</color>");
// 当前激活能力
if (_enemy.Abilities != null)
{
foreach (var ab in _enemy.Abilities.All)
{
if (ab.IsRunning)
{
string abilityId = ab.Config?.abilityId ?? ab.GetType().Name;
list.Add($"Ability: <color=#ffaa44>{abilityId}</color> [{ab.Phase}]");
}
}
}
// Boss 信息
if (_boss != null)
{
list.Add($"BossSkillExec: <color={( _boss.IsBossSkillExecuting ? "#ff4444" : "#aaaaaa")}>{_boss.IsBossSkillExecuting}</color>");
}
// 状态效果
if (_statusMgr != null)
{
var effects = _statusMgr.ActiveEffects;
for (int i = 0; i < effects.Count; i++)
list.Add($"FX: <color=#dd88ff>{effects[i].Type}</color>");
}
return list;
}
private void InitStyles()
{
if (_stylesInit) return;
_stylesInit = true;
_labelStyle = new GUIStyle(GUI.skin.label)
{
richText = true,
fontSize = 11,
alignment = TextAnchor.UpperLeft,
padding = new RectOffset(4, 4, 2, 2),
};
_labelStyle.normal.textColor = Color.white;
var bgTex = new Texture2D(1, 1);
bgTex.SetPixel(0, 0, new Color(0.05f, 0.05f, 0.1f, _bgAlpha));
bgTex.Apply();
_boxStyle = new GUIStyle(GUI.skin.box);
_boxStyle.normal.background = bgTex;
}
}
}
#endif