第4章 Groups详解与配置策略

第4章 Groups详解与配置策略

Groups的概念与作用

Addressables Groups基础概念

Addressables Groups是Unity Addressables系统中用于组织和管理资源的核心概念。它们提供了一种逻辑分组机制,允许开发者将相关的资源集合在一起,并为每个组配置不同的构建和加载策略。

Groups的主要功能

  1. 资源组织:将相关资源按逻辑分组,便于管理
  2. 构建策略:为不同组配置不同的打包和压缩策略
  3. 加载路径:控制资源的构建输出路径和运行时加载路径
  4. 更新管理:支持内容更新的分组管理
  5. 依赖处理:自动处理组内资源的依赖关系

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", "各场景相关资源");

// 创建UI资源组
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;

// 获取或添加BundledAssetGroupSchema
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>();
}

// PlayerDataGroup不需要额外配置
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)
{
// 核心资源组使用Pack Together
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)
{
// 大型资源组使用Pack Separately
var settings = UnityEditor.AddressableAssets.AddressableAssetSettingsDefaultObject.Settings;
var targetGroup = settings.FindGroup(groupName);
if (targetGroup != null)
{
// 如果组中包含大文件,考虑使用Separate
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) // 10MB以上
{
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) // 1MB以下
{
entry.SetLabel("Small", true, true);
}
else if (size < 10 * 1024 * 1024) // 1-10MB
{
entry.SetLabel("Medium", true, true);
}
else // 10MB以上
{
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[]));

// 实际设置路径(使用Addressables API)
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提供了三种压缩策略:

  1. Uncompressed:无压缩,加载速度快但包体积大
  2. LZ4:快速压缩,平衡了压缩率和加载速度
  3. 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"))
{
// 纹理资源通常已经压缩,使用LZ4
return BundledAssetGroupSchema.BundleCompressionMode.LZ4;
}

if (group.Name.ToLower().Contains("audio"))
{
// 音频文件通常已压缩,使用LZ4
return BundledAssetGroupSchema.BundleCompressionMode.LZ4;
}

if (group.Name.ToLower().Contains("core") ||
group.Name.ToLower().Contains("essential"))
{
// 核心资源优先考虑加载速度,使用LZ4
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; // 假设1MB
}
else if (assetPath.EndsWith(".fbx"))
{
// 模型文件压缩效果好
estimatedSize += 512 * 1024; // 假设512KB
}
else
{
// 其他文件
estimatedSize += 64 * 1024; // 假设64KB
}
}
}
}

// 应用压缩比率估算
switch (compression)
{
case BundledAssetGroupSchema.BundleCompressionMode.LZ4:
estimatedSize = (long)(estimatedSize * 0.7f); // LZ4大约70%大小
break;
case BundledAssetGroupSchema.BundleCompressionMode.LZMA:
estimatedSize = (long)(estimatedSize * 0.5f); // LZMA大约50%大小
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)
{
// UI和活动资源经常更新
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、路径配置、压缩策略以及各种分组策略的最佳实践。下一章我们将深入探讨远程资源管理和热更新机制。