地图系统

This commit is contained in:
2026-06-05 18:41:33 +08:00
parent 613f2a4d13
commit fe4fd60083
234 changed files with 33090 additions and 4899 deletions

View File

@@ -0,0 +1,99 @@
#if UNITY_EDITOR
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
namespace BaseGames.Editor.Validation
{
/// <summary>
/// 资源加载合规校验器:扫描项目脚本,禁止裸 <c>Resources.Load</c> 与散落的 <c>Addressables.*</c> 运行时加载,
/// 强制所有资源管理统一经 <c>BaseGames.Core.Assets.AssetLoader</c> 门面(设计师拖拽的 AssetReference 实例方法不算违规)。
/// <para>
/// 白名单(允许直接用 Addressables
/// <list type="bullet">
/// <item><c>AssetLoader.cs</c> —— 门面本体。</item>
/// <item><c>GameSaveManager.cs</c> —— 位于底层 BaseGames.Core.Save 程序集,无法引用上层门面(循环依赖),已注明的唯一架构豁免点。</item>
/// </list>
/// </para>
/// 菜单BaseGames ▸ Tools ▸ Validation ▸ Validate Resource Usage
/// </summary>
public static class ResourceUsageValidator
{
private const string ScanRoot = "Assets/_Game/Scripts";
// 允许直接调用 Addressables 的文件(门面本体 + 已注明的架构豁免)
private static readonly HashSet<string> Whitelist = new()
{
"AssetLoader.cs",
"GameSaveManager.cs",
};
// 禁止的运行时加载调用
private static readonly Regex ResourcesRe = new(@"\bResources\.(Load|LoadAsync|LoadAll)\b", RegexOptions.Compiled);
private static readonly Regex AddressablesRe = new(
@"\bAddressables\.(LoadAssetAsync|InstantiateAsync|LoadSceneAsync|UnloadSceneAsync|LoadResourceLocationsAsync|DownloadDependenciesAsync|Release|ReleaseInstance)\b",
RegexOptions.Compiled);
[MenuItem("BaseGames/Tools/Validation/Validate Resource Usage")]
public static void Validate()
{
if (!Directory.Exists(ScanRoot))
{
Debug.LogWarning($"[ResourceUsageValidator] 未找到扫描目录 {ScanRoot}。");
return;
}
var resourcesHits = new List<string>();
var addressablesHits = new List<string>();
foreach (var file in Directory.GetFiles(ScanRoot, "*.cs", SearchOption.AllDirectories))
{
string fileName = Path.GetFileName(file);
// 跳过校验器自身:它的注释/消息字符串里含有这些模式字面量(属数据,非真实调用)。
if (fileName == "ResourceUsageValidator.cs") continue;
bool whitelisted = Whitelist.Contains(fileName);
string assetPath = file.Replace('\\', '/');
var lines = File.ReadAllLines(file);
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
string trimmed = line.TrimStart();
// 跳过注释行(//、///、* 块注释、/* 开头),避免误报文档/说明
if (trimmed.StartsWith("//") || trimmed.StartsWith("*") || trimmed.StartsWith("/*"))
continue;
if (ResourcesRe.IsMatch(line)) // Resources.Load 一律禁止(含白名单文件)
resourcesHits.Add($"{assetPath}:{i + 1} {trimmed}");
if (!whitelisted && AddressablesRe.IsMatch(line))
addressablesHits.Add($"{assetPath}:{i + 1} {trimmed}");
}
}
int total = resourcesHits.Count + addressablesHits.Count;
if (total == 0)
{
Debug.Log("[ResourceUsageValidator] ✅ 合规:项目脚本无裸 Resources.Load无散落 Addressables 运行时加载(门面与豁免文件除外)。所有资源管理已统一经 AssetLoader。");
return;
}
var sb = new System.Text.StringBuilder();
sb.AppendLine($"[ResourceUsageValidator] ⚠ 发现 {total} 处资源加载违规(应改为经 AssetLoader");
if (resourcesHits.Count > 0)
{
sb.AppendLine($"\n— Resources.Load禁止改用 Addressables 并经 AssetLoader×{resourcesHits.Count}");
foreach (var h in resourcesHits) sb.AppendLine(" • " + h);
}
if (addressablesHits.Count > 0)
{
sb.AppendLine($"\n— 散落的 Addressables.*(改为经 AssetLoader 门面)×{addressablesHits.Count}");
foreach (var h in addressablesHits) sb.AppendLine(" • " + h);
}
Debug.LogWarning(sb.ToString());
}
}
}
#endif

View File

@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bbfc24e90d3cb9b4eab071c49f403ef4
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: