Files
zeling_v2/Assets/_Game/Scripts/Editor/Addressables/AddressableRules.cs
Joywayer a1b4e629aa 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.
2026-05-23 19:10:29 +08:00

175 lines
12 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 System;
using System.Collections.Generic;
using BaseGames.Core.Assets;
namespace BaseGames.Editor
{
/// <summary>
/// Addressable 分组与标签的权威规则数据。
/// 规范来源:<c>Docs/Standards/AddressablesLabelSpec.md §3</c> 与
/// <c>Docs/Standards/AssetFolderSpec.md §8</c>。
///
/// <see cref="AddressableBatchTool"/> 和 <see cref="AddressableRuleSyncWindow"/> 均引用此处,
/// 保证两个工具的分组/标签判断完全一致,修改规则时只需改这一处。
/// </summary>
public static class AddressableRules
{
// ── 已知标签白名单 ────────────────────────────────────────────────────────
// AddressableRuleSyncWindow 用此白名单区分:
// • 规则要求但缺失 → 红色,必须补齐
// • 规则不要求但存在且不在白名单 → 黄色警告(自定义标签,建议写进规范)
// • 规则不要求但存在且在白名单 → 白色,合法的人工附加标签
// 每次向 AddressablesLabelSpec 新增 Label 时,须同步在此处添加。
public static readonly HashSet<string> KnownLabels = new(StringComparer.Ordinal)
{
AddressKeys.Labels.Preload,
AddressKeys.Labels.Poolable,
AddressKeys.Labels.Enemy,
AddressKeys.Labels.BGM,
AddressKeys.Labels.SFX,
AddressKeys.Labels.Charms,
AddressKeys.Labels.Config,
AddressKeys.Labels.Weapon,
};
// ── 前缀 → 分组名 ──────────────────────────────────────────────────────
// 规则:按 AssetFolderSpec §8.1 Group 划分策略。
// 顺序:更长/更具体的前缀必须排在更短/更泛化的前缀之前,否则短前缀会先匹配。
// 特殊Room_/Boss_ 地址的分组名在运行时动态计算,见 GetExpectedGroup()。
public static readonly (string Prefix, string Group)[] PrefixGroupMap =
{
// ── 场景 ─────────────────────────────────────────────────────────
("Scene_", "Scenes"),
// ── 玩家 & 武器 ──────────────────────────────────────────────────
("PLY_", "Player"),
("WPN_", "Player"), // 武器与玩家 Prefab 同组AssetFolderSpec §8.1
// ── 敌人 & 投射物 ────────────────────────────────────────────────
("ENM_", "Enemies"),
("PROJ_", "Projectiles"),
// ── 特效 & UI ────────────────────────────────────────────────────
("VFX_", "VFX_Common"), // 通用特效AssetFolderSpec §8.1
("UI_", "UI"),
// ── 收集物 ──────────────────────────────────────────────────────
("COL_", "Collectibles"),
// ── 配置数据(更具体的前缀排在泛化前缀之前)──────────────────────
("CHM_", "Config"), // 护身符 SOAddressablesLabelSpec §3.9
("SKL_", "Config"), // 技能配置 SOAssetFolderSpec §4
("SPL_", "Config"), // 法术配置 SO
("ABL_", "Config"), // 能力配置 SO
("MAP_", "Config"), // 地图数据 SOAssetFolderSpec §4
("STR_", "Config"), // 流式加载配置 SOStreamingBudgetConfigSO
("Config/", "Config"), // 路径前缀配置AssetFolderSpec §8.2
// ── 音频AUD_BGM_ / AUD_SFX_ 必须在通配 AUD_ 之前)─────────────
("AUD_BGM_", "Audio_Music"), // BGM 流式音频
("AUD_SFX_", "Audio_SFX"), // SFX 音效(独立分组便于包体按需加载)
("AUD_", "Audio_Music"), // 未细分音频归 BGM 组
// ── 世界 & 持久化 ────────────────────────────────────────────────
("WLD_", "World"), // 可交互世界物件 Prefab
("SYS_", "Persistent"), // Persistent 场景管理器 Prefab
};
// ── 精确地址 → 标签(优先级高于前缀规则)────────────────────────────────
private static readonly Dictionary<string, string[]> ExactLabelMap =
new(StringComparer.Ordinal)
{
// Scene_MainMenu 是唯一需要 Preload 的场景
{ AddressKeys.SceneMainMenu, new[] { AddressKeys.Labels.Preload } },
// Persistent 场景无需标签(随引擎启动,不通过 label 批量加载)
{ AddressKeys.ScenePersistent, Array.Empty<string>() },
// FloatingDamageText 是 Poolable + PreloadUI_ 前缀通常无 label此处例外
{ AddressKeys.PrefabUIFloatingDmgText, new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload } },
// FootstepCatalog 是首帧必须可用的配置
{ AddressKeys.DataFootstepCatalog, new[] { AddressKeys.Labels.Config, AddressKeys.Labels.Preload } },
// 流式加载预算配置,运行时初始化前必须可用
{ AddressKeys.DataStreamingBudgetConfig, new[] { AddressKeys.Labels.Config, AddressKeys.Labels.Preload } },
};
// ── 前缀 → 标签列表 ─────────────────────────────────────────────────────
// 顺序更具体的前缀AUD_BGM_在更泛化的前缀AUD_之前。
private static readonly (string Prefix, string[] Labels)[] PrefixLabelMap =
{
// ── 音频(更具体的 AUD_BGM_ / AUD_SFX_ 必须排在 AUD_ 之前)──────
("AUD_BGM_", new[] { AddressKeys.Labels.BGM }),
("AUD_SFX_", new[] { AddressKeys.Labels.SFX }),
("AUD_", new[] { AddressKeys.Labels.BGM }), // 未细分音频默认归 BGM
// ── 场景(除 MainMenu 外无 label由 ExactLabelMap 特殊处理)──────
("Scene_", Array.Empty<string>()),
// ── 玩家 & 武器 ──────────────────────────────────────────────────
("PLY_", new[] { AddressKeys.Labels.Preload }),
("WPN_", new[] { AddressKeys.Labels.Weapon, AddressKeys.Labels.Preload }),
// ── 敌人 & 投射物 ────────────────────────────────────────────────
("ENM_", new[] { AddressKeys.Labels.Enemy }),
("PROJ_", new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload }),
// ── 特效 & UI ────────────────────────────────────────────────────
("VFX_", new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload }),
("UI_", Array.Empty<string>()), // 除 FloatingDamageText 外 UI 无默认 label
// ── 收集物 ──────────────────────────────────────────────────────
("COL_", new[] { AddressKeys.Labels.Poolable, AddressKeys.Labels.Preload }),
// ── 配置数据 ─────────────────────────────────────────────────────
("CHM_", new[] { AddressKeys.Labels.Charms }),
("MAP_", new[] { AddressKeys.Labels.Config }), // 地图数据 SO 为动态加载配置
("STR_", new[] { AddressKeys.Labels.Config }), // 流式加载配置 SOStreamingBudgetConfigSO
("Config/", new[] { AddressKeys.Labels.Config }),
// ── 技能 / 法术 / 能力 / 世界物件 / 持久化:无批量加载需求,不加 Label ──
("SKL_", Array.Empty<string>()),
("SPL_", Array.Empty<string>()),
("ABL_", Array.Empty<string>()),
("WLD_", Array.Empty<string>()),
("SYS_", Array.Empty<string>()),
};
// ── 公开 API ───────────────────────────────────────────────────────────
/// <summary>
/// 根据 Addressable 地址字符串返回期望的分组名称。
/// <list type="bullet">
/// <item><c>Room_Forest_01</c> → <c>Room_Forest</c>(动态计算)</item>
/// <item><c>Boss_CaoZhi</c> → <c>Boss_CaoZhi</c>(动态计算)</item>
/// <item>无匹配前缀时返回 <c>null</c>(调用方可回退到 Default Group</item>
/// </list>
/// </summary>
public static string GetExpectedGroup(string address)
{
if (string.IsNullOrEmpty(address)) return null;
// Room_/Boss_ 的分组名在地址中动态编码
if (address.StartsWith("Room_", StringComparison.Ordinal))
{
var parts = address.Split('_');
return parts.Length >= 2 ? $"Room_{parts[1]}" : "Room_Unknown";
}
if (address.StartsWith("Boss_", StringComparison.Ordinal))
{
// Boss_CaoZhi → 整个地址即为分组名(与 AssetFolderSpec §8.1 一致)
return address;
}
foreach (var (prefix, group) in PrefixGroupMap)
{
if (address.StartsWith(prefix, StringComparison.Ordinal))
return group;
}
return null;
}
/// <summary>
/// 根据 Addressable 地址字符串返回期望的标签集合。
/// 精确地址匹配优先,其次前缀匹配,均无匹配时返回空数组。
/// </summary>
public static string[] GetExpectedLabels(string address)
{
if (string.IsNullOrEmpty(address)) return Array.Empty<string>();
if (ExactLabelMap.TryGetValue(address, out var exact))
return exact;
foreach (var (prefix, labels) in PrefixLabelMap)
{
if (address.StartsWith(prefix, StringComparison.Ordinal))
return labels;
}
return Array.Empty<string>();
}
}
}