using UnityEditor; using UnityEngine; using BaseGames.Core.Save; using BaseGames.World.Map; namespace BaseGames.Editor.Setup { /// /// 一键创建项目所需的非事件频道 ScriptableObject 资产占位件。 /// 菜单:BaseGames → Setup → Create Project Assets /// /// 包含: /// · Assets/_Game/Data/Save/SaveSecurityConfig.asset — HMAC 密钥容器(Addressable "SaveSecurityConfig",运行时经 AssetLoader 加载) /// · Assets/_Game/Data/World/Map/RegionRegistry.asset — 区域注册表(SaveSlotUI 用于查找背景图) /// · Assets/_Game/Data/World/Map/MapDatabase.asset — 全局地图数据库 /// public static class ProjectAssetSetup { [MenuItem("BaseGames/Setup/Create Project Assets")] public static void CreateAll() { CreateSaveSecurityConfig(); CreateMapDatabase(); // 先于 RegionRegistry,使其能自动绑定 _mapDatabase CreateRegionRegistry(); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); Debug.Log("[ProjectAssetSetup] 项目资产初始化完成。"); } // ── SaveSecurityConfig ──────────────────────────────────────────────── private static void CreateSaveSecurityConfig() { // 统一走 Addressables,不放 Resources。资产置于 Data/Save,并标 Addressable 地址 "SaveSecurityConfig"。 const string folder = "Assets/_Game/Data/Save"; const string assetPath = folder + "/SaveSecurityConfig.asset"; EnsureFolder("Assets/_Game/Data", "Save"); if (AssetDatabase.LoadAssetAtPath(assetPath) == null) { var cfg = ScriptableObject.CreateInstance(); // HmacKey 留空:开发期游戏正常运行(使用兜底密钥);构建前由 CI/CD 注入。 AssetDatabase.CreateAsset(cfg, assetPath); Debug.Log($"[ProjectAssetSetup] 已创建 {assetPath}(HmacKey 留空,构建前需注入)。"); } else Debug.Log("[ProjectAssetSetup] SaveSecurityConfig 已存在。"); MarkAddressable(assetPath, "SaveSecurityConfig"); } /// 将资产标记为 Addressable(置于默认组),并设置地址。 private static void MarkAddressable(string assetPath, string address) { var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings; if (settings == null) { Debug.LogWarning($"[ProjectAssetSetup] 无 Addressable 设置,无法标记 '{address}'。"); return; } string guid = AssetDatabase.AssetPathToGUID(assetPath); var entry = settings.CreateOrMoveEntry(guid, settings.DefaultGroup); entry.SetAddress(address); } // ── RegionRegistry ──────────────────────────────────────────────────── private static void CreateRegionRegistry() { const string folder = "Assets/_Game/Data/World/Map"; const string assetPath = folder + "/RegionRegistry.asset"; EnsureFolder("Assets/_Game/Data", "World"); EnsureFolder("Assets/_Game/Data/World", "Map"); var registry = AssetDatabase.LoadAssetAtPath(assetPath); bool created = false; if (registry == null) { registry = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(registry, assetPath); created = true; } // 自动绑定 _mapDatabase(CreateMapDatabase 已先行创建);幂等,已存在的也补绑 var mapDb = AssetDatabase.LoadAssetAtPath(folder + "/MapDatabase.asset"); if (mapDb != null) { var so = new SerializedObject(registry); var prop = so.FindProperty("_mapDatabase"); if (prop != null && prop.objectReferenceValue != mapDb) { prop.objectReferenceValue = mapDb; so.ApplyModifiedProperties(); EditorUtility.SetDirty(registry); } } Debug.Log($"[ProjectAssetSetup] RegionRegistry {(created ? "已创建" : "已存在")},_mapDatabase {(mapDb != null ? "已绑定" : "未找到 MapDatabase")}。请在 Inspector 中将 _regions 数组填入所有 RegionDefinitionSO。"); } // ── MapDatabase ─────────────────────────────────────────────────────── private static void CreateMapDatabase() { const string folder = "Assets/_Game/Data/World/Map"; const string assetPath = folder + "/MapDatabase.asset"; if (AssetDatabase.LoadAssetAtPath(assetPath) != null) { Debug.Log("[ProjectAssetSetup] MapDatabase 已存在,跳过。"); return; } EnsureFolder("Assets/_Game/Data", "World"); EnsureFolder("Assets/_Game/Data/World", "Map"); var db = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(db, assetPath); Debug.Log($"[ProjectAssetSetup] 已创建 {assetPath}。"); } // ── 工具 ────────────────────────────────────────────────────────────── private static void EnsureFolder(string parent, string newFolder) { string fullPath = parent + "/" + newFolder; if (!AssetDatabase.IsValidFolder(fullPath)) AssetDatabase.CreateFolder(parent, newFolder); } } }