第4章 Groups详解与配置策略
发表于更新于
第4章 Groups详解与配置策略
Groups的概念与作用
Addressables Groups基础概念
Addressables Groups是Unity Addressables系统中用于组织和管理资源的核心概念。它们提供了一种逻辑分组机制,允许开发者将相关的资源集合在一起,并为每个组配置不同的构建和加载策略。
Groups的主要功能
- 资源组织:将相关资源按逻辑分组,便于管理
- 构建策略:为不同组配置不同的打包和压缩策略
- 加载路径:控制资源的构建输出路径和运行时加载路径
- 更新管理:支持内容更新的分组管理
- 依赖处理:自动处理组内资源的依赖关系
Group的类型
Addressables系统提供了几种不同类型的Groups:
- BundledAssetGroup:最常见的组类型,资源被打包成AssetBundle
- PlayerDataGroup:资源直接包含在Player中,不打包
- ContentUpdateGroup:专门用于内容更新的组
Group的创建和管理
通过编辑器创建Group
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
| using UnityEngine; using UnityEditor; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings;
public class GroupManager : EditorWindow { [MenuItem("Tools/Addressables/Group Manager")] static void Init() { GroupManager window = (GroupManager)EditorWindow.GetWindow(typeof(GroupManager)); window.titleContent = new GUIContent("Group Manager"); window.Show(); } void OnGUI() { GUILayout.Label("Addressables Group管理工具", EditorStyles.boldLabel); if (GUILayout.Button("创建标准Group结构")) { CreateStandardGroups(); } if (GUILayout.Button("显示当前Groups")) { ShowCurrentGroups(); } if (GUILayout.Button("清理未使用的Groups")) { CleanupUnusedGroups(); } } void CreateStandardGroups() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; CreateGroupIfNotExists(settings, "Core", "核心资源,包含游戏启动必需的资源"); CreateGroupIfNotExists(settings, "Scenes", "各场景相关资源"); CreateGroupIfNotExists(settings, "UI", "用户界面相关资源"); CreateGroupIfNotExists(settings, "Audio", "音频资源"); CreateGroupIfNotExists(settings, "Effects", "视觉特效资源"); Debug.Log("标准Group结构创建完成"); } void CreateGroupIfNotExists(AddressableAssetSettings settings, string groupName, string description) { if (settings.FindGroup(groupName) == null) { var group = settings.CreateGroup(groupName, false, false, true, null); group.AddSchema<BundledAssetGroupSchema>(); group.AddSchema<ContentUpdateGroupSchema>(); var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); updateSchema.StaticContent = false; Debug.Log($"创建Group: {groupName}"); } else { Debug.Log($"Group已存在: {groupName}"); } } void ShowCurrentGroups() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; Debug.Log("=== 当前Groups ==="); foreach (var group in settings.groups) { Debug.Log($"Group: {group.Name}, 类型: {group.GetType().Name}, 资源数量: {group.entries.Count}"); } } void CleanupUnusedGroups() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; for (int i = settings.groups.Count - 1; i >= 0; i--) { var group = settings.groups[i]; if (group.entries.Count == 0 && !group.IsDefaultGroup()) { settings.RemoveGroup(group); Debug.Log($"删除空Group: {group.Name}"); } } } }
|
Schema系统详解(BundledAssetGroupSchema等)
Schema系统概述
Schema是Addressables Groups系统的核心配置组件,它们定义了组的具体行为和配置选项。每个Group可以包含多个Schema,每个Schema负责不同的配置方面。
BundledAssetGroupSchema详解
BundledAssetGroupSchema是最重要的Schema之一,负责配置AssetBundle相关的设置。
主要配置选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| using UnityEngine; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class BundledAssetGroupConfig : EditorWindow { [MenuItem("Tools/Addressables/Bundled Group Config")] static void Init() { BundledAssetGroupConfig window = (BundledAssetGroupConfig)EditorWindow.GetWindow(typeof(BundledAssetGroupConfig)); window.titleContent = new GUIContent("Bundled Group Config"); window.Show(); } void OnGUI() { GUILayout.Label("BundledAssetGroupSchema配置", EditorStyles.boldLabel); if (GUILayout.Button("配置标准Bundled Group")) { ConfigureBundledGroup(); } if (GUILayout.Button("批量配置Groups")) { BatchConfigureGroups(); } } void ConfigureBundledGroup() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; var group = settings.DefaultGroup; if (group == null) return; var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema == null) { schema = group.AddSchema<BundledAssetGroupSchema>(); } schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; schema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; schema.IncludeInBuild = true; schema.LoadPath = "Assets/AssetBundles/{Platform}"; schema.BuildPath = "ServerData/{Platform}"; Debug.Log("BundledAssetGroupSchema配置完成"); } void BatchConfigureGroups() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; foreach (var group in settings.groups) { if (group.HasSchema<BundledAssetGroupSchema>()) { var schema = group.GetSchema<BundledAssetGroupSchema>(); switch (group.Name.ToLower()) { case "core": schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; schema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZMA; break; case "scenes": schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately; schema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; break; default: schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel; schema.Compression = BundledAssetGroupSchema.BundleCompressionMode.Uncompressed; break; } } } Debug.Log("批量配置Groups完成"); } }
|
ContentUpdateGroupSchema详解
ContentUpdateGroupSchema用于配置内容更新相关的设置。
更新配置示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| using UnityEngine; using UnityEditor.AddressableAssets.Settings; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class ContentUpdateConfig { public static void ConfigureContentUpdate(AddressableAssetGroup group, bool isStaticContent = false) { var schema = group.GetSchema<ContentUpdateGroupSchema>(); if (schema == null) { schema = group.AddSchema<ContentUpdateGroupSchema>(); } schema.StaticContent = isStaticContent; schema.UseAssetBundleCaching = true; schema.UseAssetBundleCacheForLocal = !isStaticContent; schema.BundleNaming = ContentUpdateGroupSchema.BundleNamingStyle.OnlyHash; Debug.Log($"配置内容更新: {group.Name}, 静态内容: {isStaticContent}"); } public static void ConfigureAllGroupsForUpdates(AddressableAssetSettings settings) { foreach (var group in settings.groups) { if (group.HasSchema<ContentUpdateGroupSchema>()) { bool isStatic = group.Name.ToLower().Contains("core") || group.Name.ToLower().Contains("static"); ConfigureContentUpdate(group, isStatic); } } } }
|
其他Schema类型
PlayerDataGroupSchema
用于PlayerDataGroup类型的组:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class PlayerDataGroupConfig { public static void ConfigurePlayerDataGroup(AddressableAssetGroup group) { var schema = group.GetSchema<PlayerDataGroupSchema>(); if (schema == null) { schema = group.AddSchema<PlayerDataGroupSchema>(); } Debug.Log($"配置PlayerDataGroup: {group.Name}"); } }
|
Pack Mode详解(Pack Together/Separately/Together By Label)
Pack Mode概念
Pack Mode决定了资源如何被打包到AssetBundle中,这是影响加载性能和包大小的重要配置。
Pack Together模式
所有资源被打包到一个AssetBundle中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class PackTogetherExample { public static void ConfigurePackTogether(AddressableAssetGroup group) { var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema != null) { schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; Debug.Log($"配置{group.Name}为Pack Together模式"); Debug.Log("优点: 依赖处理简单,加载效率高"); Debug.Log("缺点: 单个包体积大,更新粒度粗"); } } public static bool ShouldUsePackTogether(string groupName) { if (groupName.ToLower().Contains("core") || groupName.ToLower().Contains("essential")) { return true; } var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings; var targetGroup = settings.FindGroup(groupName); if (targetGroup != null && targetGroup.entries.Count < 10) { return true; } return false; } }
|
Pack Separately模式
每个资源被打包到单独的AssetBundle中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class PackSeparatelyExample { public static void ConfigurePackSeparately(AddressableAssetGroup group) { var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema != null) { schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately; Debug.Log($"配置{group.Name}为Pack Separately模式"); Debug.Log("优点: 更新粒度细,按需加载"); Debug.Log("缺点: 依赖管理复杂,可能产生重复资源"); } } public static bool ShouldUsePackSeparately(string groupName) { var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings; var targetGroup = settings.FindGroup(groupName); if (targetGroup != null) { foreach (var entry in targetGroup.entries) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); if (!string.IsNullOrEmpty(assetPath)) { System.IO.FileInfo fileInfo = new System.IO.FileInfo( System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), assetPath)); if (fileInfo.Exists && fileInfo.Length > 10 * 1024 * 1024) { return true; } } } } return false; } }
|
Pack Together By Label模式
具有相同标签的资源被打包到同一个AssetBundle中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using System.Collections.Generic;
public class PackTogetherByLabelExample { public static void ConfigurePackTogetherByLabel(AddressableAssetGroup group) { var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema != null) { schema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel; Debug.Log($"配置{group.Name}为Pack Together By Label模式"); Debug.Log("优点: 平衡了前两种模式的优点"); Debug.Log("缺点: 需要合理设计标签系统"); } } public static void AutoAssignLabelsBySize(AddressableAssetGroup group) { var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; foreach (var entry in group.entries) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); if (!string.IsNullOrEmpty(assetPath)) { System.IO.FileInfo fileInfo = new System.IO.FileInfo( System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), assetPath)); if (fileInfo.Exists) { long size = fileInfo.Length; if (size < 1 * 1024 * 1024) { entry.SetLabel("Small", true, true); } else if (size < 10 * 1024 * 1024) { entry.SetLabel("Medium", true, true); } else { entry.SetLabel("Large", true, true); } } } } Debug.Log($"为{group.Name}中的资源自动分配大小标签"); } }
|
Build & Load Paths配置
路径配置基础
Build Path和Load Path是控制AssetBundle构建输出位置和运行时加载位置的重要配置。
路径变量详解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class PathConfigurationExample { public static readonly Dictionary<string, string> PathTemplates = new Dictionary<string, string> { { "LocalBuild", "Assets/AssetBundles/{Platform}" }, { "LocalLoad", "file://{UnityEngine.Application.streamingAssetsPath}/{Platform}" }, { "RemoteBuild", "ServerData/{Platform}" }, { "RemoteLoad", "https://cdn.example.com/bundles/{Platform}" }, { "LocalStreaming", "Assets/StreamingAssets/{Platform}" }, { "PersistentData", "{UnityEngine.Application.persistentDataPath}/Bundles/{Platform}" } }; public static void ConfigurePaths(AddressableAssetGroup group, string buildPathKey, string loadPathKey) { var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema != null) { schema.BuildPath = new UnityEngine.ResourceManagement.ResourceLocations.ResourceLocationBase( buildPathKey, buildPathKey, typeof(UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider).FullName, typeof(byte[])); schema.LoadPath = new UnityEngine.ResourceManagement.ResourceLocations.ResourceLocationBase( loadPathKey, loadPathKey, typeof(UnityEngine.ResourceManagement.ResourceProviders.BundledAssetProvider).FullName, typeof(byte[])); schema.BuildPath.SetVariableByName("Platform", "{Platform}"); schema.LoadPath.SetVariableByName("Platform", "{Platform}"); } } public static void ConfigurePlatformPaths(AddressableAssetGroup group, string platform) { var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema != null) { schema.BuildPath = BuildPathAddressable(schema.BuildPath, platform); if (group.Name.ToLower().Contains("remote")) { schema.LoadPath = $"https://cdn.yourdomain.com/bundles/{platform}"; } else { schema.LoadPath = $"file://{Application.streamingAssetsPath}/{platform}"; } } } private static string BuildPathAddressable(string currentPath, string platform) { return currentPath.Replace("{Platform}", platform); } }
|
动态路径管理系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
| using System.Collections.Generic; using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
[System.Serializable] public class DynamicPathManager { [System.Serializable] public class PathRule { public string groupNamePattern; public string buildPath; public string loadPath; public bool isRemote; } public List<PathRule> pathRules = new List<PathRule>(); public DynamicPathManager() { pathRules.Add(new PathRule { groupNamePattern = "*Core*", buildPath = "ServerData/{Platform}/Core", loadPath = "https://cdn.example.com/bundles/{Platform}/Core", isRemote = true }); pathRules.Add(new PathRule { groupNamePattern = "*Scene*", buildPath = "ServerData/{Platform}/Scenes", loadPath = "https://cdn.example.com/bundles/{Platform}/Scenes", isRemote = true }); pathRules.Add(new PathRule { groupNamePattern = "*", buildPath = "ServerData/{Platform}", loadPath = "file://{UnityEngine.Application.streamingAssetsPath}/{Platform}", isRemote = false }); } public void ApplyPathsToGroup(AddressableAssetGroup group) { string groupName = group.Name; foreach (var rule in pathRules) { if (GroupNameMatchesPattern(groupName, rule.groupNamePattern)) { var schema = group.GetSchema<BundledAssetGroupSchema>(); if (schema != null) { schema.BuildPath = rule.buildPath; schema.LoadPath = rule.loadPath; } var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); if (updateSchema != null) { updateSchema.UseAssetBundleCaching = !rule.isRemote; } Debug.Log($"为组{groupName}应用路径规则: Build={rule.buildPath}, Load={rule.loadPath}"); return; } } } private bool GroupNameMatchesPattern(string groupName, string pattern) { if (string.IsNullOrEmpty(pattern)) return false; if (pattern == "*") return true; pattern = pattern.Replace("*", ".*"); return System.Text.RegularExpressions.Regex.IsMatch(groupName, pattern); } public void ApplyToAllGroups() { var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; foreach (var group in settings.groups) { ApplyPathsToGroup(group); } Debug.Log("已为所有Groups应用路径配置"); } }
|
Compression压缩策略选择
压缩策略详解
Addressables提供了三种压缩策略:
- Uncompressed:无压缩,加载速度快但包体积大
- LZ4:快速压缩,平衡了压缩率和加载速度
- LZMA:高压缩率,但加载速度较慢
压缩策略选择工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119
| using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas; using System.Collections.Generic;
public class CompressionStrategySelector : EditorWindow { [MenuItem("Tools/Addressables/Compression Strategy")] static void Init() { CompressionStrategySelector window = (CompressionStrategySelector)EditorWindow.GetWindow(typeof(CompressionStrategySelector)); window.titleContent = new GUIContent("Compression Strategy"); window.Show(); } enum CompressionType { Uncompressed = 0, LZ4 = 1, LZMA = 2 } CompressionType defaultCompression = CompressionType.LZ4; void OnGUI() { GUILayout.Label("压缩策略选择", EditorStyles.boldLabel); defaultCompression = (CompressionType)EditorGUILayout.EnumPopup("默认压缩策略:", defaultCompression); if (GUILayout.Button("智能选择压缩策略")) { AutoSelectCompression(); } if (GUILayout.Button("应用到所有Groups")) { ApplyToAllGroups(); } if (GUILayout.Button("显示压缩建议")) { ShowCompressionRecommendations(); } } void AutoSelectCompression() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; foreach (var group in settings.groups) { if (group.HasSchema<BundledAssetGroupSchema>()) { var schema = group.GetSchema<BundledAssetGroupSchema>(); schema.Compression = DetermineBestCompression(group); } } Debug.Log("自动选择压缩策略完成"); } BundledAssetGroupSchema.BundleCompressionMode DetermineBestCompression(AddressableAssetGroup group) { if (group.Name.ToLower().Contains("texture") || group.Name.ToLower().Contains("image")) { return BundledAssetGroupSchema.BundleCompressionMode.LZ4; } if (group.Name.ToLower().Contains("audio")) { return BundledAssetGroupSchema.BundleCompressionMode.LZ4; } if (group.Name.ToLower().Contains("core") || group.Name.ToLower().Contains("essential")) { return BundledAssetGroupSchema.BundleCompressionMode.LZ4; } return (BundledAssetGroupSchema.BundleCompressionMode)(int)defaultCompression; } void ApplyToAllGroups() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; foreach (var group in settings.groups) { if (group.HasSchema<BundledAssetGroupSchema>()) { var schema = group.GetSchema<BundledAssetGroupSchema>(); schema.Compression = (BundledAssetGroupSchema.BundleCompressionMode)(int)defaultCompression; } } Debug.Log($"应用压缩策略: {defaultCompression}"); } void ShowCompressionRecommendations() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; Debug.Log("=== 压缩策略建议 ==="); foreach (var group in settings.groups) { var recommended = DetermineBestCompression(group); Debug.Log($"{group.Name}: 推荐 {recommended}"); } } }
|
压缩效果分析工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
| using System.Collections.Generic; using UnityEngine; using UnityEditor.AddressableAssets.Settings.GroupSchemas;
public class CompressionAnalyzer { public class CompressionResult { public string groupName; public long uncompressedSize; public long lz4Size; public long lzmaSize; public float lz4Ratio; public float lzmaRatio; } public static List<CompressionResult> AnalyzeGroups() { var results = new List<CompressionResult>(); var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return results; foreach (var group in settings.groups) { if (group.HasSchema<BundledAssetGroupSchema>()) { var result = new CompressionResult { groupName = group.Name, uncompressedSize = EstimateGroupSize(group, BundledAssetGroupSchema.BundleCompressionMode.Uncompressed), lz4Size = EstimateGroupSize(group, BundledAssetGroupSchema.BundleCompressionMode.LZ4), lzmaSize = EstimateGroupSize(group, BundledAssetGroupSchema.BundleCompressionMode.LZMA) }; result.lz4Ratio = result.uncompressedSize > 0 ? (float)result.lz4Size / result.uncompressedSize : 0; result.lzmaRatio = result.uncompressedSize > 0 ? (float)result.lzmaSize / result.uncompressedSize : 0; results.Add(result); } } return results; } private static long EstimateGroupSize(AddressableAssetGroup group, BundledAssetGroupSchema.BundleCompressionMode compression) { long estimatedSize = 0; foreach (var entry in group.entries) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); if (!string.IsNullOrEmpty(assetPath)) { var importer = UnityEditor.AssetImporter.GetAtPath(assetPath); if (importer != null) { if (assetPath.EndsWith(".png") || assetPath.EndsWith(".jpg")) { estimatedSize += 1024 * 1024; } else if (assetPath.EndsWith(".fbx")) { estimatedSize += 512 * 1024; } else { estimatedSize += 64 * 1024; } } } } switch (compression) { case BundledAssetGroupSchema.BundleCompressionMode.LZ4: estimatedSize = (long)(estimatedSize * 0.7f); break; case BundledAssetGroupSchema.BundleCompressionMode.LZMA: estimatedSize = (long)(estimatedSize * 0.5f); break; } return estimatedSize; } public static void PrintAnalysisReport() { var results = AnalyzeGroups(); Debug.Log("=== 压缩分析报告 ==="); foreach (var result in results) { Debug.Log($"{result.groupName}:"); Debug.Log($" 未压缩: {result.uncompressedSize / 1024 / 1024:F2}MB"); Debug.Log($" LZ4: {result.lz4Size / 1024 / 1024:F2}MB ({result.lz4Ratio:P2})"); Debug.Log($" LZMA: {result.lzmaSize / 1024 / 1024:F2}MB ({result.lzmaRatio:P2})"); } } }
|
分组策略最佳实践(按功能/场景/更新频率分组)
功能分组策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134
| using System.Collections.Generic; using UnityEngine; using UnityEditor.AddressableAssets.Settings;
public class FunctionalGroupingStrategy { public static void ApplyFunctionalGrouping(AddressableAssetSettings settings) { MoveAssetsByFunction(settings); ConfigureFunctionalGroups(settings); } private static void MoveAssetsByFunction(AddressableAssetSettings settings) { var functionMappings = new Dictionary<string, string> { { "/Characters/", "Characters" }, { "/UI/", "UI" }, { "/Audio/", "Audio" }, { "/Effects/", "Effects" }, { "/Environment/", "Environment" }, { "/Weapons/", "Weapons" }, { "/Items/", "Items" } }; foreach (var entry in new List<AddressableAssetEntry>(settings.GetAllAssets())) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); foreach (var mapping in functionMappings) { if (assetPath.Contains(mapping.Key)) { var targetGroup = settings.FindGroup(mapping.Value); if (targetGroup == null) { targetGroup = settings.CreateGroup(mapping.Value, false, false, true, null); targetGroup.AddSchema<BundledAssetGroupSchema>(); targetGroup.AddSchema<ContentUpdateGroupSchema>(); } settings.MoveEntry(entry, targetGroup, false, true); break; } } } } private static void ConfigureFunctionalGroups(AddressableAssetSettings settings) { foreach (var group in settings.groups) { switch (group.Name.ToLower()) { case "characters": ConfigureCharacterGroup(group); break; case "ui": ConfigureUIGroup(group); break; case "audio": ConfigureAudioGroup(group); break; case "effects": ConfigureEffectsGroup(group); break; default: ConfigureDefaultGroup(group); break; } } } private static void ConfigureCharacterGroup(AddressableAssetGroup group) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); if (updateSchema != null) { updateSchema.StaticContent = false; } } private static void ConfigureUIGroup(AddressableAssetGroup group) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } } private static void ConfigureAudioGroup(AddressableAssetGroup group) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZMA; } } private static void ConfigureEffectsGroup(AddressableAssetGroup group) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } } private static void ConfigureDefaultGroup(AddressableAssetGroup group) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } } }
|
场景分组策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89
| using System.Collections.Generic; using UnityEngine; using UnityEditor.AddressableAssets.Settings;
public class SceneBasedGroupingStrategy { public static void ApplySceneGrouping(AddressableAssetSettings settings) { MoveAssetsByScene(settings); ConfigureSceneGroups(settings); } private static void MoveAssetsByScene(AddressableAssetSettings settings) { var sceneAssets = new List<AddressableAssetEntry>(); foreach (var entry in settings.GetAllAssets()) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); if (assetPath.EndsWith(".unity")) { sceneAssets.Add(entry); } } foreach (var sceneEntry in sceneAssets) { string scenePath = UnityEditor.AssetDatabase.GUIDToAssetPath(sceneEntry.guid); string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath); string groupName = $"Scene_{sceneName}"; var group = settings.FindGroup(groupName); if (group == null) { group = settings.CreateGroup(groupName, false, false, true, null); group.AddSchema<BundledAssetGroupSchema>(); group.AddSchema<ContentUpdateGroupSchema>(); } FindAndMoveSceneRelatedAssets(settings, scenePath, group); } } private static void FindAndMoveSceneRelatedAssets(AddressableAssetSettings settings, string scenePath, AddressableAssetGroup targetGroup) { string sceneDir = System.IO.Path.GetDirectoryName(scenePath); string sceneName = System.IO.Path.GetFileNameWithoutExtension(scenePath); string relatedDir = sceneDir + "/" + sceneName; foreach (var entry in new List<AddressableAssetEntry>(settings.GetAllAssets())) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); if (assetPath.StartsWith(relatedDir)) { settings.MoveEntry(entry, targetGroup, false, true); } } } private static void ConfigureSceneGroups(AddressableAssetSettings settings) { foreach (var group in settings.groups) { if (group.Name.StartsWith("Scene_")) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); if (updateSchema != null) { updateSchema.StaticContent = false; } } } } }
|
更新频率分组策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| using System.Collections.Generic; using UnityEngine; using UnityEditor.AddressableAssets.Settings;
public class UpdateFrequencyGroupingStrategy { public static void ApplyUpdateFrequencyGrouping(AddressableAssetSettings settings) { MoveAssetsByUpdateFrequency(settings); ConfigureUpdateFrequencyGroups(settings); } private static void MoveAssetsByUpdateFrequency(AddressableAssetSettings settings) { var frequencyGroups = new Dictionary<string, System.Func<string, bool>> { { "Static", (path) => IsStaticAsset(path) }, { "FrequentlyUpdated", (path) => IsFrequentlyUpdatedAsset(path) }, { "ModeratelyUpdated", (path) => IsModeratelyUpdatedAsset(path) } }; foreach (var entry in new List<AddressableAssetEntry>(settings.GetAllAssets())) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); foreach (var groupDef in frequencyGroups) { if (groupDef.Value(assetPath)) { var targetGroup = settings.FindGroup(groupDef.Key); if (targetGroup == null) { targetGroup = settings.CreateGroup(groupDef.Key, false, false, true, null); targetGroup.AddSchema<BundledAssetGroupSchema>(); targetGroup.AddSchema<ContentUpdateGroupSchema>(); } settings.MoveEntry(entry, targetGroup, false, true); break; } } } } private static bool IsStaticAsset(string assetPath) { return assetPath.Contains("/Core/") || assetPath.Contains("/Engine/") || assetPath.Contains("/Base/"); } private static bool IsFrequentlyUpdatedAsset(string assetPath) { return assetPath.Contains("/UI/") || assetPath.Contains("/Events/") || assetPath.Contains("/Promotions/"); } private static bool IsModeratelyUpdatedAsset(string assetPath) { return !IsStaticAsset(assetPath) && !IsFrequentlyUpdatedAsset(assetPath); } private static void ConfigureUpdateFrequencyGroups(AddressableAssetSettings settings) { foreach (var group in settings.groups) { var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); if (updateSchema == null) continue; switch (group.Name.ToLower()) { case "static": updateSchema.StaticContent = true; updateSchema.UseAssetBundleCaching = true; break; case "frequentlyupdated": updateSchema.StaticContent = false; updateSchema.UseAssetBundleCaching = true; break; case "moderatelyupdated": updateSchema.StaticContent = false; updateSchema.UseAssetBundleCaching = true; break; } } } }
|
实战案例:为不同类型资源设计分组方案
综合分组策略实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
| using System.Collections.Generic; using UnityEngine; using UnityEditor.AddressableAssets.Settings;
public class ComprehensiveGroupingStrategy : EditorWindow { [MenuItem("Tools/Addressables/Comprehensive Grouping")] static void Init() { ComprehensiveGroupingStrategy window = (ComprehensiveGroupingStrategy)EditorWindow.GetWindow(typeof(ComprehensiveGroupingStrategy)); window.titleContent = new GUIContent("Comprehensive Grouping"); window.Show(); } enum GroupingStrategy { Functional, SceneBased, UpdateFrequency, Hybrid } GroupingStrategy strategy = GroupingStrategy.Hybrid; bool autoConfigure = true; void OnGUI() { GUILayout.Label("综合分组策略", EditorStyles.boldLabel); strategy = (GroupingStrategy)EditorGUILayout.EnumPopup("分组策略:", strategy); autoConfigure = EditorGUILayout.Toggle("自动配置", autoConfigure); if (GUILayout.Button("执行分组")) { ExecuteGrouping(); } if (GUILayout.Button("显示分组结果")) { ShowGroupingResults(); } } void ExecuteGrouping() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; switch (strategy) { case GroupingStrategy.Functional: FunctionalGroupingStrategy.ApplyFunctionalGrouping(settings); break; case GroupingStrategy.SceneBased: SceneBasedGroupingStrategy.ApplySceneGrouping(settings); break; case GroupingStrategy.UpdateFrequency: UpdateFrequencyGroupingStrategy.ApplyUpdateFrequencyGrouping(settings); break; case GroupingStrategy.Hybrid: ApplyHybridStrategy(settings); break; } if (autoConfigure) { ConfigureAllGroups(settings); } Debug.Log("分组策略执行完成"); } void ApplyHybridStrategy(AddressableAssetSettings settings) { FunctionalGroupingStrategy.ApplyFunctionalGrouping(settings); foreach (var group in settings.groups) { if (group.Name.ToLower().Contains("level") || group.Name.ToLower().Contains("scene")) { SplitGroupByUpdateFrequency(settings, group); } } } void SplitGroupByUpdateFrequency(AddressableAssetSettings settings, AddressableAssetGroup group) { var entriesToMove = new List<AddressableAssetEntry>(); var staticEntries = new List<AddressableAssetEntry>(); var dynamicEntries = new List<AddressableAssetEntry>(); foreach (var entry in group.entries) { string assetPath = UnityEditor.AssetDatabase.GUIDToAssetPath(entry.guid); if (IsStaticAsset(assetPath)) { staticEntries.Add(entry); } else { dynamicEntries.Add(entry); } } if (staticEntries.Count > 0) { var staticGroup = settings.CreateGroup($"{group.Name}_Static", false, false, true, null); staticGroup.AddSchema<BundledAssetGroupSchema>(); staticGroup.AddSchema<ContentUpdateGroupSchema>(); foreach (var entry in staticEntries) { settings.MoveEntry(entry, staticGroup, false, true); } } if (dynamicEntries.Count > 0) { var dynamicGroup = settings.CreateGroup($"{group.Name}_Dynamic", false, false, true, null); dynamicGroup.AddSchema<BundledAssetGroupSchema>(); dynamicGroup.AddSchema<ContentUpdateGroupSchema>(); foreach (var entry in dynamicEntries) { settings.MoveEntry(entry, dynamicGroup, false, true); } } } bool IsStaticAsset(string assetPath) { return assetPath.Contains("/Core/") || assetPath.Contains("/Base/") || assetPath.Contains("/Engine/"); } void ConfigureAllGroups(AddressableAssetSettings settings) { foreach (var group in settings.groups) { ConfigureGroupByType(settings, group); } } void ConfigureGroupByType(AddressableAssetSettings settings, AddressableAssetGroup group) { var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema == null) return; var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); string groupName = group.Name.ToLower(); if (groupName.Contains("core") || groupName.Contains("static")) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZMA; if (updateSchema != null) updateSchema.StaticContent = true; } else if (groupName.Contains("audio")) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackSeparately; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZMA; } else if (groupName.Contains("ui") || groupName.Contains("texture")) { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogetherByLabel; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } else { bundleSchema.BundleMode = BundledAssetGroupSchema.BundlePackingMode.PackTogether; bundleSchema.Compression = BundledAssetGroupSchema.BundleCompressionMode.LZ4; } } void ShowGroupingResults() { var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) return; Debug.Log("=== 分组结果 ==="); foreach (var group in settings.groups) { Debug.Log($"{group.Name}: {group.entries.Count} 个资源"); var bundleSchema = group.GetSchema<BundledAssetGroupSchema>(); if (bundleSchema != null) { Debug.Log($" 打包模式: {bundleSchema.BundleMode}"); Debug.Log($" 压缩方式: {bundleSchema.Compression}"); } var updateSchema = group.GetSchema<ContentUpdateGroupSchema>(); if (updateSchema != null) { Debug.Log($" 静态内容: {updateSchema.StaticContent}"); } } } }
|
通过本章的学习,您已经全面掌握了Addressables Groups系统的配置和管理方法,包括Schema系统、Pack Mode、路径配置、压缩策略以及各种分组策略的最佳实践。下一章我们将深入探讨远程资源管理和热更新机制。