第7章 资源预加载与缓存

第7章 资源预加载与缓存

Preload Dependencies

预加载依赖的概念

在Unity Addressables系统中,预加载依赖是指在加载主要资源之前,预先加载该资源所依赖的其他资源。这种机制可以显著提高资源加载性能,避免在运行时临时加载依赖项导致的卡顿。

依赖预加载基础

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class PreloadDependenciesManager : MonoBehaviour
{
[System.Serializable]
public class PreloadGroup
{
public string groupName;
public string[] assetAddresses;
public bool preloadOnStart;
public int priority;
}

[Header("预加载配置")]
public PreloadGroup[] preloadGroups;

[Header("回调事件")]
public System.Action<string, float> onPreloadProgress;
public System.Action<string> onPreloadComplete;
public System.Action<string, string> onPreloadError;

private Dictionary<string, List<AsyncOperationHandle>> activePreloads = new Dictionary<string, List<AsyncOperationHandle>>();
private Dictionary<string, bool> preloadStatus = new Dictionary<string, bool>();

void Start()
{
// 自动预加载标记为启动时预加载的组
foreach (var group in preloadGroups)
{
if (group.preloadOnStart)
{
PreloadGroupAsync(group.groupName);
}
}
}

/// <summary>
/// 异步预加载资源组
/// </summary>
public async void PreloadGroupAsync(string groupName)
{
var group = System.Array.Find(preloadGroups, g => g.groupName == groupName);
if (group == null)
{
Debug.LogError($"未找到预加载组: {groupName}");
onPreloadError?.Invoke(groupName, "预加载组未找到");
return;
}

if (preloadStatus.ContainsKey(groupName) && preloadStatus[groupName])
{
Debug.Log($"预加载组已加载: {groupName}");
onPreloadComplete?.Invoke(groupName);
return;
}

Debug.Log($"开始预加载组: {groupName}, 资源数量: {group.assetAddresses.Length}");

var handles = new List<AsyncOperationHandle>();
activePreloads[groupName] = handles;

try
{
// 预加载依赖项
var dependencyHandle = Addressables.DownloadDependenciesAsync(group.assetAddresses, false);
handles.Add(dependencyHandle);

// 监听进度
dependencyHandle.PercentCompleteChanged += (handle) =>
{
float progress = handle.PercentComplete;
onPreloadProgress?.Invoke(groupName, progress);
};

// 等待依赖下载完成
await dependencyHandle.Task;

if (dependencyHandle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"预加载组完成: {groupName}");
preloadStatus[groupName] = true;
onPreloadComplete?.Invoke(groupName);
}
else
{
string errorMsg = dependencyHandle.OperationException?.Message ?? "未知错误";
Debug.LogError($"预加载组失败: {groupName}, 错误: {errorMsg}");
onPreloadError?.Invoke(groupName, errorMsg);
}
}
catch (System.Exception e)
{
string errorMsg = $"预加载异常: {e.Message}";
Debug.LogError(errorMsg);
onPreloadError?.Invoke(groupName, errorMsg);
}
finally
{
// 释放操作句柄
foreach (var handle in handles)
{
Addressables.Release(handle);
}
activePreloads.Remove(groupName);
}
}

/// <summary>
/// 预加载单个资源的依赖
/// </summary>
public async System.Threading.Tasks.Task PreloadAssetDependencies(string assetAddress)
{
try
{
var handle = Addressables.DownloadDependenciesAsync(assetAddress, false);

handle.PercentCompleteChanged += (op) =>
{
float progress = op.PercentComplete;
Debug.Log($"预加载依赖 {assetAddress} 进度: {(progress * 100):F1}%");
};

await handle.Task;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"资源依赖预加载完成: {assetAddress}");
}
else
{
Debug.LogError($"资源依赖预加载失败: {assetAddress}");
}

Addressables.Release(handle);
}
catch (System.Exception e)
{
Debug.LogError($"预加载资源依赖异常: {e.Message}");
}
}

/// <summary>
/// 预加载多个资源的依赖
/// </summary>
public async System.Threading.Tasks.Task PreloadMultipleAssetDependencies(string[] assetAddresses)
{
var handles = new List<AsyncOperationHandle>();

foreach (string address in assetAddresses)
{
var handle = Addressables.DownloadDependenciesAsync(address, false);
handles.Add(handle);
}

// 等待所有依赖加载完成
foreach (var handle in handles)
{
await handle.Task;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"批量预加载依赖完成: {handle.LocationName}");
}
else
{
Debug.LogError($"批量预加载依赖失败: {handle.LocationName}");
}

Addressables.Release(handle);
}

Debug.Log($"批量预加载完成,共 {assetAddresses.Length} 个资源");
}

/// <summary>
/// 检查预加载状态
/// </summary>
public bool IsGroupPreloaded(string groupName)
{
return preloadStatus.ContainsKey(groupName) && preloadStatus[groupName];
}

/// <summary>
/// 按优先级排序预加载
/// </summary>
public async System.Threading.Tasks.Task PreloadByPriority(int minPriority = 0, int maxPriority = int.MaxValue)
{
var groupsToPreload = new List<PreloadGroup>();

foreach (var group in preloadGroups)
{
if (group.priority >= minPriority && group.priority <= maxPriority)
{
groupsToPreload.Add(group);
}
}

// 按优先级排序
groupsToPreload.Sort((a, b) => b.priority.CompareTo(a.priority));

foreach (var group in groupsToPreload)
{
await System.Threading.Tasks.Task.Delay(100); // 短暂延迟
PreloadGroupAsync(group.groupName);
}
}

void OnDestroy()
{
// 清理所有预加载操作
foreach (var handles in activePreloads.Values)
{
foreach (var handle in handles)
{
Addressables.Release(handle);
}
}
activePreloads.Clear();
}
}

智能预加载系统

基于使用频率的预加载

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class IntelligentPreloadSystem : MonoBehaviour
{
[System.Serializable]
public class AssetUsageRecord
{
public string assetAddress;
public int usageCount;
public System.DateTime lastUsed;
public float averageLoadTime;
public bool isFrequentlyUsed;
}

[Header("智能预加载配置")]
public float preloadThreshold = 0.7f; // 预加载阈值
public int minUsageCount = 3; // 最小使用次数
public float timeWindowHours = 24f; // 统计时间窗口
public int maxPreloadAssets = 10; // 最大预加载资产数量

private List<AssetUsageRecord> usageRecords = new List<AssetUsageRecord>();
private Dictionary<string, System.DateTime> loadStartTimes = new Dictionary<string, System.DateTime>();

void Start()
{
// 加载历史使用记录
LoadUsageHistory();

// 定期分析使用模式
InvokeRepeating("AnalyzeUsagePatterns", 5f, 60f); // 每分钟分析一次
}

/// <summary>
/// 记录资源使用
/// </summary>
public void RecordAssetUsage(string assetAddress)
{
var record = usageRecords.Find(r => r.assetAddress == assetAddress);

if (record == null)
{
record = new AssetUsageRecord
{
assetAddress = assetAddress,
usageCount = 0,
lastUsed = System.DateTime.Now
};
usageRecords.Add(record);
}

record.usageCount++;
record.lastUsed = System.DateTime.Now;

// 记录加载开始时间
loadStartTimes[assetAddress] = System.DateTime.Now;

Debug.Log($"记录资源使用: {assetAddress}, 使用次数: {record.usageCount}");
}

/// <summary>
/// 完成资源加载记录
/// </summary>
public void CompleteAssetLoad(string assetAddress)
{
if (loadStartTimes.ContainsKey(assetAddress))
{
var startTime = loadStartTimes[assetAddress];
var loadTime = (System.DateTime.Now - startTime).TotalSeconds;

var record = usageRecords.Find(r => r.assetAddress == assetAddress);
if (record != null)
{
// 更新平均加载时间
if (record.averageLoadTime == 0)
{
record.averageLoadTime = (float)loadTime;
}
else
{
record.averageLoadTime = (record.averageLoadTime + (float)loadTime) / 2;
}
}

loadStartTimes.Remove(assetAddress);
}
}

/// <summary>
/// 分析使用模式并预加载
/// </summary>
public void AnalyzeUsagePatterns()
{
var frequentlyUsedAssets = GetFrequentlyUsedAssets();

if (frequentlyUsedAssets.Count > 0)
{
Debug.Log($"发现 {frequentlyUsedAssets.Count} 个频繁使用的资源,开始预加载");

// 限制预加载数量
int preloadCount = Mathf.Min(frequentlyUsedAssets.Count, maxPreloadAssets);

for (int i = 0; i < preloadCount; i++)
{
string assetAddress = frequentlyUsedAssets[i];

// 检查是否已经预加载
if (!IsAssetPreloaded(assetAddress))
{
StartCoroutine(PreloadAssetWithDelay(assetAddress, i * 0.5f)); // 延迟加载避免同时加载
}
}
}
}

private List<string> GetFrequentlyUsedAssets()
{
var candidates = new List<AssetUsageRecord>();

// 过滤频繁使用的资源
foreach (var record in usageRecords)
{
// 检查时间窗口
if ((System.DateTime.Now - record.lastUsed).TotalHours <= timeWindowHours)
{
// 检查使用频率
if (record.usageCount >= minUsageCount)
{
record.isFrequentlyUsed = true;
candidates.Add(record);
}
}
}

// 按使用频率排序
candidates.Sort((a, b) => b.usageCount.CompareTo(a.usageCount));

// 转换为地址列表
var frequentlyUsed = new List<string>();
foreach (var record in candidates)
{
frequentlyUsed.Add(record.assetAddress);
}

return frequentlyUsed;
}

private bool IsAssetPreloaded(string assetAddress)
{
// 这里可以检查资源是否已经在缓存中
// 简化实现,返回false表示未预加载
return false;
}

private System.Collections.IEnumerator PreloadAssetWithDelay(string assetAddress, float delay)
{
yield return new WaitForSeconds(delay);

// 预加载资源依赖
var handle = Addressables.DownloadDependenciesAsync(assetAddress, false);

handle.PercentCompleteChanged += (op) =>
{
float progress = op.PercentComplete;
Debug.Log($"智能预加载 {assetAddress} 进度: {(progress * 100):F1}%");
};

yield return handle;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"智能预加载完成: {assetAddress}");
}
else
{
Debug.LogError($"智能预加载失败: {assetAddress}");
}

Addressables.Release(handle);
}

/// <summary>
/// 获取使用统计
/// </summary>
public string GetUsageStatistics()
{
int totalAssets = usageRecords.Count;
int frequentlyUsed = 0;

foreach (var record in usageRecords)
{
if (record.isFrequentlyUsed)
{
frequentlyUsed++;
}
}

return $"总资源数: {totalAssets}, 频繁使用: {frequentlyUsed}";
}

/// <summary>
/// 保存使用历史
/// </summary>
public void SaveUsageHistory()
{
// 在实际项目中,这里应该保存到PlayerPrefs或文件
Debug.Log("保存使用历史");
}

/// <summary>
/// 加载使用历史
/// </summary>
public void LoadUsageHistory()
{
// 在实际项目中,这里应该从PlayerPrefs或文件加载
Debug.Log("加载使用历史");
}

void OnDestroy()
{
SaveUsageHistory();
}
}

资源预热策略

预热系统设计

资源预热是指在游戏运行的关键时刻(如场景切换前)预先加载可能需要的资源,以减少后续加载时间。

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class ResourceWarmupSystem : MonoBehaviour
{
[System.Serializable]
public class WarmupProfile
{
public string profileName;
public string[] assetAddresses;
public float preloadTime; // 预加载时间(秒)
public bool preloadOnSceneLoad; // 场景加载时预加载
public string[] requiredScenes; // 需要的场景
}

[Header("预热配置")]
public WarmupProfile[] warmupProfiles;

[Header("预热设置")]
public float warmupTimeout = 30f; // 预热超时时间
public int maxConcurrentWarmups = 3; // 最大并发预热数

private Dictionary<string, WarmupProfile> profileMap = new Dictionary<string, WarmupProfile>();
private List<AsyncOperationHandle> activeWarmups = new List<AsyncOperationHandle>();
private Queue<string> warmupQueue = new Queue<string>();
private bool isProcessingQueue = false;

void Start()
{
// 初始化配置映射
foreach (var profile in warmupProfiles)
{
profileMap[profile.profileName] = profile;

// 如果配置为场景加载时预加载
if (profile.preloadOnSceneLoad)
{
// 这里可以监听场景加载事件
// SceneManager.sceneLoaded += OnSceneLoaded;
}
}
}

/// <summary>
/// 启动预热配置
/// </summary>
public void StartWarmup(string profileName)
{
if (!profileMap.ContainsKey(profileName))
{
Debug.LogError($"未找到预热配置: {profileName}");
return;
}

var profile = profileMap[profileName];

Debug.Log($"开始预热配置: {profileName}, 资源数量: {profile.assetAddresses.Length}");

// 检查当前并发数
if (activeWarmups.Count >= maxConcurrentWarmups)
{
Debug.Log($"添加到预热队列: {profileName}");
warmupQueue.Enqueue(profileName);

if (!isProcessingQueue)
{
StartCoroutine(ProcessWarmupQueue());
}
return;
}

StartCoroutine(ExecuteWarmup(profile));
}

private System.Collections.IEnumerator ExecuteWarmup(WarmupProfile profile)
{
var handles = new List<AsyncOperationHandle>();

foreach (string assetAddress in profile.assetAddresses)
{
// 检查是否达到最大并发数
if (activeWarmups.Count >= maxConcurrentWarmups)
{
yield return new WaitUntil(() => activeWarmups.Count < maxConcurrentWarmups);
}

var handle = Addressables.DownloadDependenciesAsync(assetAddress, false);
handles.Add(handle);
activeWarmups.Add(handle);

// 设置超时检测
System.DateTime startTime = System.DateTime.Now;

// 监听进度
handle.PercentCompleteChanged += (op) =>
{
float progress = op.PercentComplete;
float elapsed = (float)(System.DateTime.Now - startTime).TotalSeconds;

if (elapsed > warmupTimeout)
{
Debug.LogWarning($"预热超时: {assetAddress}");
Addressables.Release(handle);
activeWarmups.Remove(handle);
return;
}

Debug.Log($"预热 {assetAddress} 进度: {(progress * 100):F1}%");
};

// 等待当前资源预热完成
yield return handle;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"预热完成: {assetAddress}");
}
else
{
Debug.LogError($"预热失败: {assetAddress}");
}

activeWarmups.Remove(handle);
Addressables.Release(handle);
}

Debug.Log($"预热配置完成: {profile.profileName}");
}

private System.Collections.IEnumerator ProcessWarmupQueue()
{
isProcessingQueue = true;

while (warmupQueue.Count > 0)
{
if (activeWarmups.Count < maxConcurrentWarmups)
{
string profileName = warmupQueue.Dequeue();

if (profileMap.ContainsKey(profileName))
{
StartCoroutine(ExecuteWarmup(profileMap[profileName]));
}
}

yield return new WaitForSeconds(0.1f); // 短暂延迟
}

isProcessingQueue = false;
}

/// <summary>
/// 预热场景相关资源
/// </summary>
public void WarmupSceneResources(string sceneName)
{
foreach (var profile in warmupProfiles)
{
if (System.Array.IndexOf(profile.requiredScenes, sceneName) >= 0)
{
StartWarmup(profile.profileName);
}
}
}

/// <summary>
/// 预热关卡资源
/// </summary>
public void WarmupLevelResources(int levelNumber)
{
string profileName = $"Level_{levelNumber}_Warmup";
StartWarmup(profileName);
}

/// <summary>
/// 预热UI资源
/// </summary>
public void WarmupUIResources()
{
StartWarmup("UI_Warmup");
}

/// <summary>
/// 预热音频资源
/// </summary>
public void WarmupAudioResources()
{
StartWarmup("Audio_Warmup");
}

/// <summary>
/// 预热角色资源
/// </summary>
public void WarmupCharacterResources()
{
StartWarmup("Character_Warmup");
}

/// <summary>
/// 取消所有预热操作
/// </summary>
public void CancelAllWarmups()
{
foreach (var handle in activeWarmups)
{
Addressables.Release(handle);
}
activeWarmups.Clear();

warmupQueue.Clear();
isProcessingQueue = false;

Debug.Log("已取消所有预热操作");
}

/// <summary>
/// 获取预热状态
/// </summary>
public string GetWarmupStatus()
{
return $"活跃预热: {activeWarmups.Count}, 队列中: {warmupQueue.Count}";
}

void OnDestroy()
{
CancelAllWarmups();
}
}

智能预热策略

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
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class SmartWarmupStrategy : MonoBehaviour
{
[System.Serializable]
public class SmartWarmupRule
{
public string ruleName;
public string[] triggerAssets; // 触发资源
public string[] targetAssets; // 目标预热资源
public float warmupDelay; // 预热延迟
public float priority; // 优先级
public string[] conditions; // 预热条件
}

[Header("智能预热规则")]
public SmartWarmupRule[] warmupRules;

private Dictionary<string, List<SmartWarmupRule>> triggerRuleMap = new Dictionary<string, List<SmartWarmupRule>>();
private ResourceWarmupSystem warmupSystem;

void Start()
{
warmupSystem = FindObjectOfType<ResourceWarmupSystem>();
if (warmupSystem == null)
{
warmupSystem = gameObject.AddComponent<ResourceWarmupSystem>();
}

// 构建触发规则映射
BuildTriggerRuleMap();
}

private void BuildTriggerRuleMap()
{
foreach (var rule in warmupRules)
{
foreach (string triggerAsset in rule.triggerAssets)
{
if (!triggerRuleMap.ContainsKey(triggerAsset))
{
triggerRuleMap[triggerAsset] = new List<SmartWarmupRule>();
}
triggerRuleMap[triggerAsset].Add(rule);
}
}
}

/// <summary>
/// 资源被访问时触发智能预热
/// </summary>
public void OnAssetAccessed(string assetAddress)
{
if (triggerRuleMap.ContainsKey(assetAddress))
{
var rules = triggerRuleMap[assetAddress];

// 按优先级排序
rules.Sort((a, b) => b.priority.CompareTo(a.priority));

foreach (var rule in rules)
{
// 检查条件
if (CheckWarmupConditions(rule))
{
// 延迟执行预热
StartCoroutine(DelayedWarmup(rule, rule.warmupDelay));
}
}
}
}

private bool CheckWarmupConditions(SmartWarmupRule rule)
{
// 检查各种预热条件
foreach (string condition in rule.conditions)
{
switch (condition.ToLower())
{
case "enough_memory":
if (!HasEnoughMemory())
return false;
break;
case "good_network":
if (!HasGoodNetworkConnection())
return false;
break;
case "not_loading":
if (IsCurrentlyLoading())
return false;
break;
}
}

return true;
}

private bool HasEnoughMemory()
{
// 检查可用内存
long availableMemory = SystemInfo.systemMemorySize * 1024 * 1024; // 转换为字节
long usedMemory = UnityEngine.Profiling.Profiler.usedHeapSizeLong;
long freeMemory = availableMemory - usedMemory;

// 至少需要100MB可用内存
return freeMemory > 100 * 1024 * 1024;
}

private bool HasGoodNetworkConnection()
{
// 检查网络连接质量
return Application.internetReachability != NetworkReachability.NotReachable;
}

private bool IsCurrentlyLoading()
{
// 检查是否有大量资源正在加载
// 这里可以集成加载管理器的状态
return false; // 简化实现
}

private System.Collections.IEnumerator DelayedWarmup(SmartWarmupRule rule, float delay)
{
yield return new WaitForSeconds(delay);

Debug.Log($"执行智能预热规则: {rule.ruleName}");

// 预热目标资源
foreach (string targetAsset in rule.targetAssets)
{
var handle = Addressables.DownloadDependenciesAsync(targetAsset, false);

yield return handle;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"智能预热完成: {targetAsset}");
}
else
{
Debug.LogError($"智能预热失败: {targetAsset}");
}

Addressables.Release(handle);
}
}

/// <summary>
/// 动态添加预热规则
/// </summary>
public void AddWarmupRule(SmartWarmupRule rule)
{
System.Array.Resize(ref warmupRules, warmupRules.Length + 1);
warmupRules[warmupRules.Length - 1] = rule;

// 更新映射
foreach (string triggerAsset in rule.triggerAssets)
{
if (!triggerRuleMap.ContainsKey(triggerAsset))
{
triggerRuleMap[triggerAsset] = new List<SmartWarmupRule>();
}
triggerRuleMap[triggerAsset].Add(rule);
}

Debug.Log($"添加智能预热规则: {rule.ruleName}");
}

/// <summary>
/// 移除预热规则
/// </summary>
public void RemoveWarmupRule(string ruleName)
{
var ruleToRemove = System.Array.Find(warmupRules, r => r.ruleName == ruleName);
if (ruleToRemove != null)
{
// 从规则数组中移除
var newRules = new List<SmartWarmupRule>(warmupRules);
newRules.Remove(ruleToRemove);
warmupRules = newRules.ToArray();

// 从映射中移除
foreach (string triggerAsset in ruleToRemove.triggerAssets)
{
if (triggerRuleMap.ContainsKey(triggerAsset))
{
triggerRuleMap[triggerAsset].Remove(ruleToRemove);
if (triggerRuleMap[triggerAsset].Count == 0)
{
triggerRuleMap.Remove(triggerAsset);
}
}
}

Debug.Log($"移除智能预热规则: {ruleName}");
}
}
}

Caching System配置

缓存系统基础配置

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
210
211
212
213
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;

public class CacheSystemConfig : MonoBehaviour
{
[System.Serializable]
public class CacheSettings
{
public bool enableCaching = true;
public long maxCacheSize = 512 * 1024 * 1024; // 512MB
public int cacheExpiryHours = 24; // 缓存过期时间(小时)
public bool compressCache = true;
public string cacheDirectory = "AddressablesCache";
public bool enableMemoryCache = true;
public long memoryCacheSize = 64 * 1024 * 1024; // 64MB
}

[Header("缓存设置")]
public CacheSettings cacheSettings;

[Header("缓存统计")]
public bool showCacheStats = true;

private bool isCacheInitialized = false;

void Start()
{
InitializeCacheSystem();
}

/// <summary>
/// 初始化缓存系统
/// </summary>
public void InitializeCacheSystem()
{
if (isCacheInitialized) return;

try
{
// 配置缓存设置
ConfigureCacheSettings();

// 初始化缓存系统
Addressables.InternalIdTransformFunc = (location) =>
{
if (location.PrimaryKey.StartsWith("http"))
{
// 为远程资源添加缓存配置
return location.InternalId;
}
return location.InternalId;
};

isCacheInitialized = true;
Debug.Log("缓存系统初始化完成");
}
catch (Exception e)
{
Debug.LogError($"缓存系统初始化失败: {e.Message}");
}
}

private void ConfigureCacheSettings()
{
if (!cacheSettings.enableCaching) return;

// 设置缓存大小限制
SetCacheSizeLimit(cacheSettings.maxCacheSize);

// 配置缓存目录
ConfigureCacheDirectory(cacheSettings.cacheDirectory);

Debug.Log($"缓存配置 - 最大大小: {FormatFileSize(cacheSettings.maxCacheSize)}, " +
$"过期时间: {cacheSettings.cacheExpiryHours}小时");
}

private void SetCacheSizeLimit(long sizeLimit)
{
// Unity Addressables会自动管理缓存大小
// 这里可以设置一些额外的缓存管理逻辑
Debug.Log($"设置缓存大小限制: {FormatFileSize(sizeLimit)}");
}

private void ConfigureCacheDirectory(string directory)
{
// 缓存目录通常由Unity自动管理
// 但可以在这里进行额外的配置
string cachePath = System.IO.Path.Combine(Application.persistentDataPath, directory);
Debug.Log($"缓存目录: {cachePath}");

// 确保缓存目录存在
if (!System.IO.Directory.Exists(cachePath))
{
System.IO.Directory.CreateDirectory(cachePath);
}
}

/// <summary>
/// 获取缓存统计信息
/// </summary>
public string GetCacheStatistics()
{
if (!isCacheInitialized) return "缓存系统未初始化";

try
{
// 获取缓存大小
long cacheSize = GetCacheSize();
long totalSize = cacheSettings.maxCacheSize;
float usagePercent = (float)cacheSize / totalSize * 100;

// 获取缓存项数量
int cacheItemCount = GetCacheItemCount();

return $"缓存大小: {FormatFileSize(cacheSize)} / {FormatFileSize(totalSize)} ({usagePercent:F1}%), " +
$"缓存项: {cacheItemCount}";
}
catch (Exception e)
{
return $"获取缓存统计失败: {e.Message}";
}
}

private long GetCacheSize()
{
// 这里应该获取实际的缓存大小
// 简化实现,返回估算值
return 100 * 1024 * 1024; // 100MB 估算
}

private int GetCacheItemCount()
{
// 这里应该获取实际的缓存项数量
// 简化实现,返回估算值
return 50; // 50项 估算
}

/// <summary>
/// 清理缓存
/// </summary>
public void ClearCache()
{
try
{
bool success = Addressables.ClearCache(true);
if (success)
{
Debug.Log("缓存清理成功");
}
else
{
Debug.LogError("缓存清理失败");
}
}
catch (Exception e)
{
Debug.LogError($"缓存清理异常: {e.Message}");
}
}

/// <summary>
/// 清理过期缓存
/// </summary>
public void ClearExpiredCache()
{
// 检查并清理过期的缓存项
// 这里可以实现自定义的过期检查逻辑
Debug.Log($"清理过期缓存({cacheSettings.cacheExpiryHours}小时前)");

// 在实际实现中,这里应该检查缓存文件的时间戳
ClearCache(); // 简化实现
}

/// <summary>
/// 检查缓存空间
/// </summary>
public bool HasEnoughCacheSpace(long requiredSize)
{
long cacheSize = GetCacheSize();
long availableSpace = cacheSettings.maxCacheSize - cacheSize;
return availableSpace >= requiredSize;
}

/// <summary>
/// 格式化文件大小
/// </summary>
private string FormatFileSize(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
double len = bytes;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}
return $"{len:F2}{sizes[order]}";
}

void Update()
{
if (showCacheStats)
{
// 定期显示缓存统计(每10秒)
if (Time.time % 10 < Time.deltaTime)
{
Debug.Log($"缓存状态: {GetCacheStatistics()}");
}
}
}
}

高级缓存管理

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;

public class AdvancedCacheManager : MonoBehaviour
{
[System.Serializable]
public class CacheEntry
{
public string assetAddress;
public string cacheKey;
public System.DateTime lastAccessed;
public System.DateTime createdTime;
public long size;
public int accessCount;
public bool isPinned; // 是否固定不被清理
public string category; // 缓存类别
}

[Header("高级缓存设置")]
public long maxCacheSize = 1024 * 1024 * 1024; // 1GB
public float cacheCleanupThreshold = 0.8f; // 缓存清理阈值
public int maxCacheEntries = 1000; // 最大缓存条目数
public float cleanupInterval = 300f; // 清理间隔(秒)

private List<CacheEntry> cacheEntries = new List<CacheEntry>();
private Dictionary<string, CacheEntry> cacheEntryMap = new Dictionary<string, CacheEntry>();

void Start()
{
InvokeRepeating("PerformCacheMaintenance", 60f, cleanupInterval); // 1分钟后开始,定期维护
}

/// <summary>
/// 记录缓存访问
/// </summary>
public void RecordCacheAccess(string assetAddress, string category = "General", long size = 0)
{
if (cacheEntryMap.ContainsKey(assetAddress))
{
// 更新现有条目
var entry = cacheEntryMap[assetAddress];
entry.lastAccessed = DateTime.Now;
entry.accessCount++;
}
else
{
// 创建新条目
var entry = new CacheEntry
{
assetAddress = assetAddress,
cacheKey = GenerateCacheKey(assetAddress),
lastAccessed = DateTime.Now,
createdTime = DateTime.Now,
size = size,
accessCount = 1,
category = category
};

cacheEntries.Add(entry);
cacheEntryMap[assetAddress] = entry;
}

// 检查是否需要清理
CheckCacheSize();
}

private string GenerateCacheKey(string assetAddress)
{
// 生成缓存键
return assetAddress.GetHashCode().ToString();
}

/// <summary>
/// 检查缓存大小并清理
/// </summary>
private void CheckCacheSize()
{
long currentSize = GetCurrentCacheSize();
float usageRatio = (float)currentSize / maxCacheSize;

if (usageRatio > cacheCleanupThreshold)
{
Debug.Log($"缓存使用率过高: {(usageRatio * 100):F1}%, 执行清理");
CleanupCache();
}
}

/// <summary>
/// 获取当前缓存大小
/// </summary>
private long GetCurrentCacheSize()
{
long totalSize = 0;
foreach (var entry in cacheEntries)
{
totalSize += entry.size;
}
return totalSize;
}

/// <summary>
/// 执行缓存清理
/// </summary>
private void CleanupCache()
{
// 按访问时间和使用频率排序
cacheEntries.Sort((a, b) =>
{
// 优先保留固定条目
if (a.isPinned && !b.isPinned) return -1;
if (!a.isPinned && b.isPinned) return 1;

// 然后按最后访问时间排序(最近访问的优先保留)
int timeCompare = b.lastAccessed.CompareTo(a.lastAccessed);
if (timeCompare != 0) return timeCompare;

// 最后按访问频率排序(频繁访问的优先保留)
return b.accessCount.CompareTo(a.accessCount);
});

// 移除最旧的条目,直到缓存大小在限制内
long currentSize = GetCurrentCacheSize();
int removedCount = 0;

for (int i = cacheEntries.Count - 1; i >= 0; i--)
{
if (currentSize <= maxCacheSize * cacheCleanupThreshold)
break;

var entry = cacheEntries[i];

// 不清理固定条目
if (entry.isPinned)
continue;

// 从缓存中移除(在实际实现中,这里应该删除实际的缓存文件)
currentSize -= entry.size;
cacheEntries.RemoveAt(i);
cacheEntryMap.Remove(entry.assetAddress);
removedCount++;
}

Debug.Log($"缓存清理完成,移除 {removedCount} 个条目");
}

/// <summary>
/// 固定缓存条目(防止被清理)
/// </summary>
public void PinCacheEntry(string assetAddress)
{
if (cacheEntryMap.ContainsKey(assetAddress))
{
cacheEntryMap[assetAddress].isPinned = true;
Debug.Log($"固定缓存条目: {assetAddress}");
}
}

/// <summary>
/// 取消固定缓存条目
/// </summary>
public void UnpinCacheEntry(string assetAddress)
{
if (cacheEntryMap.ContainsKey(assetAddress))
{
cacheEntryMap[assetAddress].isPinned = false;
Debug.Log($"取消固定缓存条目: {assetAddress}");
}
}

/// <summary>
/// 按类别清理缓存
/// </summary>
public void CleanupCacheByCategory(string category)
{
int removedCount = 0;

for (int i = cacheEntries.Count - 1; i >= 0; i--)
{
if (cacheEntries[i].category == category && !cacheEntries[i].isPinned)
{
cacheEntries.RemoveAt(i);
removedCount++;
}
}

UpdateCacheMap();
Debug.Log($"按类别清理缓存: {category}, 移除 {removedCount} 个条目");
}

private void UpdateCacheMap()
{
cacheEntryMap.Clear();
foreach (var entry in cacheEntries)
{
cacheEntryMap[entry.assetAddress] = entry;
}
}

/// <summary>
/// 获取缓存统计
/// </summary>
public string GetCacheStatistics()
{
long currentSize = GetCurrentCacheSize();
float usagePercent = (float)currentSize / maxCacheSize * 100;

int pinnedCount = 0;
foreach (var entry in cacheEntries)
{
if (entry.isPinned) pinnedCount++;
}

return $"缓存大小: {FormatFileSize(currentSize)} / {FormatFileSize(maxCacheSize)} ({usagePercent:F1}%), " +
$"条目数: {cacheEntries.Count}, 固定条目: {pinnedCount}";
}

/// <summary>
/// 执行缓存维护
/// </summary>
private void PerformCacheMaintenance()
{
Debug.Log("执行缓存维护");

// 清理过期条目
CleanupExpiredEntries();

// 整理缓存
CompactCache();

// 记录维护日志
Debug.Log($"缓存维护完成 - {GetCacheStatistics()}");
}

private void CleanupExpiredEntries()
{
// 清理超过一定时间未访问的条目(例如7天)
DateTime expiryTime = DateTime.Now.AddDays(-7);
int removedCount = 0;

for (int i = cacheEntries.Count - 1; i >= 0; i--)
{
if (cacheEntries[i].lastAccessed < expiryTime && !cacheEntries[i].isPinned)
{
cacheEntries.RemoveAt(i);
removedCount++;
}
}

if (removedCount > 0)
{
UpdateCacheMap();
Debug.Log($"清理过期缓存条目: {removedCount} 个");
}
}

private void CompactCache()
{
// 整理缓存,移除无效条目
// 在实际实现中,这里应该检查缓存文件是否仍然存在
}

/// <summary>
/// 格式化文件大小
/// </summary>
private string FormatFileSize(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
double len = bytes;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}
return $"{len:F2}{sizes[order]}";
}

void OnDestroy()
{
// 清理定时任务
CancelInvoke();
}
}

缓存清理策略

智能缓存清理系统

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
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;

public class SmartCacheCleanupSystem : MonoBehaviour
{
[System.Serializable]
public class CleanupRule
{
public string ruleName;
public string category; // 缓存类别
public int maxAgeDays; // 最大保存天数
public int maxCount; // 最大数量
public float priority; // 清理优先级
public bool enabled; // 是否启用
}

[Header("清理规则")]
public CleanupRule[] cleanupRules;

[Header("清理设置")]
public float cleanupInterval = 3600f; // 1小时
public bool autoCleanup = true;
public long minFreeSpaceRequired = 100 * 1024 * 1024; // 100MB最小可用空间

private AdvancedCacheManager cacheManager;

void Start()
{
cacheManager = FindObjectOfType<AdvancedCacheManager>();
if (cacheManager == null)
{
cacheManager = gameObject.AddComponent<AdvancedCacheManager>();
}

if (autoCleanup)
{
InvokeRepeating("PerformSmartCleanup", 60f, cleanupInterval); // 1分钟后开始
}
}

/// <summary>
/// 执行智能清理
/// </summary>
public void PerformSmartCleanup()
{
Debug.Log("开始智能缓存清理");

// 检查磁盘空间
if (!HasEnoughFreeSpace())
{
Debug.Log("磁盘空间不足,执行紧急清理");
PerformEmergencyCleanup();
return;
}

// 按规则执行清理
foreach (var rule in cleanupRules)
{
if (rule.enabled)
{
ApplyCleanupRule(rule);
}
}

Debug.Log("智能缓存清理完成");
}

private bool HasEnoughFreeSpace()
{
// 检查可用磁盘空间
// 这里使用简化的方法,实际项目中可能需要更精确的检查
try
{
var drive = new System.IO.DriveInfo(System.IO.Path.GetPathRoot(Application.persistentDataPath));
return drive.AvailableFreeSpace > minFreeSpaceRequired;
}
catch
{
return true; // 如果无法检查,假设空间充足
}
}

private void PerformEmergencyCleanup()
{
Debug.Log("执行紧急缓存清理");

// 首先清理临时类别缓存
cacheManager.CleanupCacheByCategory("Temporary");

// 然后清理低优先级类别
var lowPriorityCategories = new[] { "Downloaded", "Cache" };
foreach (string category in lowPriorityCategories)
{
cacheManager.CleanupCacheByCategory(category);
}

// 最后清理所有非固定条目
// 这里需要直接操作缓存管理器的内部状态
// 在实际实现中,可能需要添加公共方法
}

private void ApplyCleanupRule(CleanupRule rule)
{
Debug.Log($"应用清理规则: {rule.ruleName}");

// 按年龄清理
if (rule.maxAgeDays > 0)
{
CleanupByAge(rule.category, rule.maxAgeDays);
}

// 按数量清理
if (rule.maxCount > 0)
{
CleanupByCount(rule.category, rule.maxCount);
}
}

private void CleanupByAge(string category, int maxAgeDays)
{
DateTime cutoffTime = DateTime.Now.AddDays(-maxAgeDays);
var entriesToRemove = new List<string>();

// 这里需要访问缓存管理器的内部数据
// 在实际实现中,缓存管理器应该提供查询方法
Debug.Log($"按年龄清理类别: {category}, 最大天数: {maxAgeDays}");
}

private void CleanupByCount(string category, int maxCount)
{
// 按类别和数量限制清理
Debug.Log($"按数量清理类别: {category}, 最大数量: {maxCount}");
}

/// <summary>
/// 添加清理规则
/// </summary>
public void AddCleanupRule(CleanupRule rule)
{
var newRules = new List<CleanupRule>(cleanupRules);
newRules.Add(rule);
cleanupRules = newRules.ToArray();

Debug.Log($"添加清理规则: {rule.ruleName}");
}

/// <summary>
/// 移除清理规则
/// </summary>
public void RemoveCleanupRule(string ruleName)
{
var newRules = new List<CleanupRule>();
foreach (var rule in cleanupRules)
{
if (rule.ruleName != ruleName)
{
newRules.Add(rule);
}
}
cleanupRules = newRules.ToArray();

Debug.Log($"移除清理规则: {ruleName}");
}

/// <summary>
/// 手动触发清理
/// </summary>
public void ManualCleanup()
{
PerformSmartCleanup();
}

/// <summary>
/// 获取清理统计
/// </summary>
public string GetCleanupStatistics()
{
return cacheManager.GetCacheStatistics();
}

void OnDestroy()
{
CancelInvoke();
}
}

内存与磁盘缓存平衡

缓存平衡管理器

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;

public class CacheBalanceManager : MonoBehaviour
{
[System.Serializable]
public class CacheBalanceConfig
{
public long totalCacheSize = 512 * 1024 * 1024; // 512MB总缓存
public float memoryCacheRatio = 0.2f; // 内存缓存占比20%
public float diskCacheRatio = 0.8f; // 磁盘缓存占比80%
public long minMemoryCacheSize = 32 * 1024 * 1024; // 最小内存缓存32MB
public long maxMemoryCacheSize = 128 * 1024 * 1024; // 最大内存缓存128MB
public bool adaptiveBalancing = true; // 是否启用自适应平衡
public float balanceCheckInterval = 60f; // 平衡检查间隔
}

[Header("缓存平衡配置")]
public CacheBalanceConfig balanceConfig;

[Header("性能监控")]
public bool monitorPerformance = true;

private long currentMemoryCacheSize = 0;
private long currentDiskCacheSize = 0;
private float lastBalanceCheckTime = 0;

void Start()
{
InitializeCacheBalance();

if (balanceConfig.adaptiveBalancing)
{
InvokeRepeating("CheckAndBalanceCache", 30f, balanceConfig.balanceCheckInterval);
}
}

private void InitializeCacheBalance()
{
// 初始化缓存大小
UpdateCacheSizes();

Debug.Log($"缓存平衡初始化 - 内存: {FormatFileSize(GetMemoryCacheSize())}, " +
$"磁盘: {FormatFileSize(GetDiskCacheSize())}");
}

private void UpdateCacheSizes()
{
// 计算内存和磁盘缓存大小
long totalSize = balanceConfig.totalCacheSize;
long memorySize = (long)(totalSize * balanceConfig.memoryCacheRatio);
long diskSize = (long)(totalSize * balanceConfig.diskCacheRatio);

// 应用最小/最大限制
memorySize = Math.Max(balanceConfig.minMemoryCacheSize,
Math.Min(memorySize, balanceConfig.maxMemoryCacheSize));
diskSize = totalSize - memorySize; // 重新计算磁盘大小

currentMemoryCacheSize = memorySize;
currentDiskCacheSize = diskSize;

Debug.Log($"更新缓存大小 - 内存: {FormatFileSize(memorySize)}, 磁盘: {FormatFileSize(diskSize)}");
}

/// <summary>
/// 检查并平衡缓存
/// </summary>
private void CheckAndBalanceCache()
{
if (!balanceConfig.adaptiveBalancing) return;

// 检查系统资源使用情况
float memoryUsage = GetMemoryUsage();
float diskUsage = GetDiskUsage();

Debug.Log($"资源使用 - 内存: {(memoryUsage * 100):F1}%, 磁盘: {(diskUsage * 100):F1}%");

// 根据资源使用情况调整缓存平衡
AdjustCacheBalance(memoryUsage, diskUsage);
}

private float GetMemoryUsage()
{
// 获取当前内存使用率
long usedMemory = Profiling.Profiler.usedHeapSizeLong;
long totalMemory = SystemInfo.systemMemorySize * 1024 * 1024;

return (float)usedMemory / totalMemory;
}

private float GetDiskUsage()
{
// 获取磁盘使用率
try
{
var drive = new System.IO.DriveInfo(System.IO.Path.GetPathRoot(Application.persistentDataPath));
long totalSize = drive.TotalSize;
long availableSize = drive.AvailableFreeSpace;

return (float)(totalSize - availableSize) / totalSize;
}
catch
{
return 0.5f; // 默认50%
}
}

private void AdjustCacheBalance(float memoryUsage, float diskUsage)
{
// 根据内存和磁盘使用情况调整缓存平衡
float memoryPressure = memoryUsage;
float diskPressure = diskUsage;

// 如果内存压力大,减少内存缓存,增加磁盘缓存
if (memoryPressure > 0.8f)
{
Debug.Log("内存压力大,减少内存缓存");
ReduceMemoryCache();
}
// 如果磁盘压力大,减少磁盘缓存,增加内存缓存
else if (diskPressure > 0.8f)
{
Debug.Log("磁盘压力大,减少磁盘缓存");
ReduceDiskCache();
}
// 如果都正常,使用配置的比例
else
{
UpdateCacheSizes(); // 恢复配置的比例
}
}

private void ReduceMemoryCache()
{
// 减少内存缓存大小
long newMemorySize = (long)(currentMemoryCacheSize * 0.8f); // 减少20%
newMemorySize = Math.Max(balanceConfig.minMemoryCacheSize, newMemorySize);

long sizeChange = currentMemoryCacheSize - newMemorySize;
currentMemoryCacheSize = newMemorySize;
currentDiskCacheSize += sizeChange;

Debug.Log($"内存缓存减少: {FormatFileSize(sizeChange)}");
}

private void ReduceDiskCache()
{
// 减少磁盘缓存大小
long newDiskSize = (long)(currentDiskCacheSize * 0.8f); // 减少20%

long sizeChange = currentDiskCacheSize - newDiskSize;
currentDiskCacheSize = newDiskSize;
currentMemoryCacheSize += sizeChange;

// 确保不超过内存缓存最大限制
if (currentMemoryCacheSize > balanceConfig.maxMemoryCacheSize)
{
long excess = currentMemoryCacheSize - balanceConfig.maxMemoryCacheSize;
currentMemoryCacheSize = balanceConfig.maxMemoryCacheSize;
currentDiskCacheSize += excess;
}

Debug.Log($"磁盘缓存减少: {FormatFileSize(sizeChange)}");
}

/// <summary>
/// 获取当前内存缓存大小
/// </summary>
public long GetMemoryCacheSize()
{
return currentMemoryCacheSize;
}

/// <summary>
/// 获取当前磁盘缓存大小
/// </summary>
public long GetDiskCacheSize()
{
return currentDiskCacheSize;
}

/// <summary>
/// 获取缓存平衡状态
/// </summary>
public string GetBalanceStatus()
{
float memoryRatio = (float)currentMemoryCacheSize / balanceConfig.totalCacheSize;
float diskRatio = (float)currentDiskCacheSize / balanceConfig.totalCacheSize;

return $"内存缓存: {FormatFileSize(currentMemoryCacheSize)} ({memoryRatio:P1}), " +
$"磁盘缓存: {FormatFileSize(currentDiskCacheSize)} ({diskRatio:P1})";
}

/// <summary>
/// 手动调整缓存比例
/// </summary>
public void SetCacheRatio(float memoryRatio)
{
if (memoryRatio < 0 || memoryRatio > 1)
{
Debug.LogError("内存比例必须在0-1之间");
return;
}

long newMemorySize = (long)(balanceConfig.totalCacheSize * memoryRatio);
newMemorySize = Math.Max(balanceConfig.minMemoryCacheSize,
Math.Min(newMemorySize, balanceConfig.maxMemoryCacheSize));

currentMemoryCacheSize = newMemorySize;
currentDiskCacheSize = balanceConfig.totalCacheSize - newMemorySize;

Debug.Log($"手动设置缓存比例 - 内存: {memoryRatio:P1}, " +
$"内存缓存: {FormatFileSize(currentMemoryCacheSize)}, " +
$"磁盘缓存: {FormatFileSize(currentDiskCacheSize)}");
}

/// <summary>
/// 格式化文件大小
/// </summary>
private string FormatFileSize(long bytes)
{
string[] sizes = { "B", "KB", "MB", "GB" };
double len = bytes;
int order = 0;
while (len >= 1024 && order < sizes.Length - 1)
{
order++;
len = len / 1024;
}
return $"{len:F2}{sizes[order]}";
}

void Update()
{
if (monitorPerformance && Time.time - lastBalanceCheckTime > 5f)
{
// 每5秒显示一次性能监控
Debug.Log($"缓存平衡状态: {GetBalanceStatus()}");
lastBalanceCheckTime = Time.time;
}
}

void OnDestroy()
{
CancelInvoke();
}
}

实战案例:启动时智能预加载系统

完整的启动预加载系统

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
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;

public class StartupSmartPreloadSystem : MonoBehaviour
{
[System.Serializable]
public class StartupPreloadItem
{
public string address;
public string category; // "Core", "UI", "Audio", "Character", etc.
public int priority; // 优先级,数字越小优先级越高
public bool required; // 是否必需
public float weight; // 预加载权重
}

[System.Serializable]
public class StartupPreloadConfig
{
public StartupPreloadItem[] preloadItems;
public float preloadTimeout = 30f; // 预加载超时时间
public int maxConcurrentLoads = 3; // 最大并发加载数
public bool showProgressUI = true; // 是否显示进度UI
public string nextScene = "MainGame"; // 预加载完成后加载的场景
}

[Header("启动预加载配置")]
public StartupPreloadConfig preloadConfig;

[Header("进度UI")]
public UnityEngine.UI.Slider progressSlider;
public UnityEngine.UI.Text progressText;
public UnityEngine.UI.Text statusText;

[Header("性能设置")]
public bool enablePerformanceMonitoring = true;
public float performanceCheckInterval = 1f;

private List<AsyncOperationHandle> activePreloads = new List<AsyncOperationHandle>();
private Dictionary<string, float> itemWeights = new Dictionary<string, float>();
private Dictionary<string, AsyncOperationHandle> itemHandles = new Dictionary<string, AsyncOperationHandle>();
private int totalItems = 0;
private int completedItems = 0;
private bool isPreloading = false;
private float preloadStartTime;

void Start()
{
StartPreloadSequence();
}

/// <summary>
/// 开始预加载序列
/// </summary>
public void StartPreloadSequence()
{
if (isPreloading) return;

isPreloading = true;
preloadStartTime = Time.realtimeSinceStartup;

Debug.Log($"开始启动预加载,共 {preloadConfig.preloadItems.Length} 个资源");

// 初始化权重字典
foreach (var item in preloadConfig.preloadItems)
{
itemWeights[item.address] = item.weight;
}

totalItems = preloadConfig.preloadItems.Length;
completedItems = 0;

// 按优先级排序预加载项
Array.Sort(preloadConfig.preloadItems, (a, b) => a.priority.CompareTo(b.priority));

// 开始预加载
StartCoroutine(ExecutePreloadSequence());

if (enablePerformanceMonitoring)
{
StartCoroutine(MonitorPerformance());
}
}

private IEnumerator ExecutePreloadSequence()
{
var coreItems = new List<StartupPreloadItem>();
var otherItems = new List<StartupPreloadItem>();

// 分离核心资源和其他资源
foreach (var item in preloadConfig.preloadItems)
{
if (item.category == "Core" || item.required)
{
coreItems.Add(item);
}
else
{
otherItems.Add(item);
}
}

// 首先加载核心资源
yield return StartCoroutine(LoadItems(coreItems.ToArray(), "Core"));

// 然后加载其他资源
yield return StartCoroutine(LoadItems(otherItems.ToArray(), "Other"));

// 预加载完成
OnPreloadComplete();
}

private IEnumerator LoadItems(StartupPreloadItem[] items, string category)
{
Debug.Log($"开始加载{category}资源,数量: {items.Length}");

var loadQueue = new Queue<StartupPreloadItem>();
foreach (var item in items)
{
loadQueue.Enqueue(item);
}

while (loadQueue.Count > 0)
{
// 启动新的加载操作,直到达到最大并发数
while (loadQueue.Count > 0 && activePreloads.Count < preloadConfig.maxConcurrentLoads)
{
var item = loadQueue.Dequeue();
StartCoroutine(LoadSingleItem(item));
}

// 等待至少一个加载完成
if (activePreloads.Count > 0)
{
yield return new WaitUntil(() => activePreloads.Count < preloadConfig.maxConcurrentLoads || loadQueue.Count == 0);
}
else
{
yield return null;
}
}

// 等待所有加载完成
yield return new WaitUntil(() => activePreloads.Count == 0);

Debug.Log($"{category}资源加载完成");
}

private IEnumerator LoadSingleItem(StartupPreloadItem item)
{
var handle = Addressables.DownloadDependenciesAsync(item.address, false);
activePreloads.Add(handle);
itemHandles[item.address] = handle;

System.DateTime startTime = DateTime.Now;

// 监听进度
handle.PercentCompleteChanged += (op) =>
{
float progress = op.PercentComplete;
float elapsed = (float)(DateTime.Now - startTime).TotalSeconds;

// 检查是否超时
if (elapsed > preloadConfig.preloadTimeout)
{
Debug.LogWarning($"预加载超时: {item.address}");
Addressables.Release(handle);
activePreloads.Remove(handle);
itemHandles.Remove(item.address);
OnItemLoadFailed(item.address, "加载超时");
return;
}

UpdateProgress(item.address, progress);
};

yield return handle;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"预加载成功: {item.address}");
OnItemLoadComplete(item.address);
}
else
{
Debug.LogError($"预加载失败: {item.address}, 错误: {handle.OperationException?.Message}");
OnItemLoadFailed(item.address, handle.OperationException?.Message ?? "未知错误");
}

activePreloads.Remove(handle);
itemHandles.Remove(item.address);
Addressables.Release(handle);
}

private void UpdateProgress(string itemAddress, float progress)
{
// 计算总体进度
float itemWeight = itemWeights.ContainsKey(itemAddress) ? itemWeights[itemAddress] : 1.0f;
float weightedProgress = itemWeight * progress;

float overallProgress = (completedItems + weightedProgress) / totalItems;

// 更新UI
if (progressSlider != null)
{
progressSlider.value = overallProgress;
}

if (progressText != null)
{
progressText.text = $"预加载进度: {(overallProgress * 100):F1}%";
}

if (statusText != null)
{
statusText.text = $"正在预加载: {itemAddress}";
}

Debug.Log($"预加载 {itemAddress} 进度: {(progress * 100):F1}%, 总体: {(overallProgress * 100):F1}%");
}

private void OnItemLoadComplete(string address)
{
completedItems++;
UpdateProgressUI();
}

private void OnItemLoadFailed(string address, string error)
{
completedItems++;
UpdateProgressUI();

// 如果是必需资源,记录错误
var item = Array.Find(preloadConfig.preloadItems, i => i.address == address);
if (item != null && item.required)
{
Debug.LogError($"必需资源预加载失败: {address}, 错误: {error}");
}
}

private void UpdateProgressUI()
{
float overallProgress = (float)completedItems / totalItems;

if (progressSlider != null)
{
progressSlider.value = overallProgress;
}

if (progressText != null)
{
progressText.text = $"预加载进度: {(overallProgress * 100):F1}% ({completedItems}/{totalItems})";
}
}

private void OnPreloadComplete()
{
float totalTime = Time.realtimeSinceStartup - preloadStartTime;
Debug.Log($"启动预加载完成,总耗时: {totalTime:F2}秒");

if (statusText != null)
{
statusText.text = $"预加载完成! 耗时: {totalTime:F2}秒";
}

// 隐藏进度UI
if (progressSlider != null) progressSlider.gameObject.SetActive(false);
if (progressText != null) progressText.gameObject.SetActive(false);
if (statusText != null) statusText.gameObject.SetActive(false);

isPreloading = false;

// 加载下一个场景
if (!string.IsNullOrEmpty(preloadConfig.nextScene))
{
StartCoroutine(LoadNextScene());
}
}

private IEnumerator LoadNextScene()
{
yield return new WaitForSeconds(1f); // 短暂延迟

if (!string.IsNullOrEmpty(preloadConfig.nextScene))
{
AsyncOperation sceneLoad = SceneManager.LoadSceneAsync(preloadConfig.nextScene);
yield return sceneLoad;

Debug.Log($"场景加载完成: {preloadConfig.nextScene}");
}
}

private IEnumerator MonitorPerformance()
{
while (isPreloading)
{
// 监控内存使用
long memoryUsed = Profiling.Profiler.usedHeapSizeLong;
float memoryUsage = (float)memoryUsed / (SystemInfo.systemMemorySize * 1024 * 1024);

// 如果内存使用过高,降低并发数
if (memoryUsage > 0.8f && preloadConfig.maxConcurrentLoads > 1)
{
Debug.LogWarning($"内存使用过高: {(memoryUsage * 100):F1}%, 降低并发数");
preloadConfig.maxConcurrentLoads = Math.Max(1, preloadConfig.maxConcurrentLoads - 1);
}
// 如果内存使用正常,可以适当增加并发数
else if (memoryUsage < 0.6f && preloadConfig.maxConcurrentLoads < 5)
{
preloadConfig.maxConcurrentLoads = Math.Min(5, preloadConfig.maxConcurrentLoads + 1);
}

yield return new WaitForSeconds(performanceCheckInterval);
}
}

/// <summary>
/// 取消预加载
/// </summary>
public void CancelPreload()
{
if (!isPreloading) return;

Debug.Log("取消预加载");

// 释放所有活跃的加载操作
foreach (var handle in activePreloads)
{
Addressables.Release(handle);
}
activePreloads.Clear();

foreach (var handle in itemHandles.Values)
{
Addressables.Release(handle);
}
itemHandles.Clear();

isPreloading = false;
}

/// <summary>
/// 获取预加载状态
/// </summary>
public string GetPreloadStatus()
{
return $"预加载中: {isPreloading}, 完成: {completedItems}/{totalItems}, 并发数: {preloadConfig.maxConcurrentLoads}";
}

void OnDestroy()
{
CancelPreload();
}
}

启动预加载系统的使用示例

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
using UnityEngine;
using UnityEngine.UI;

public class StartupPreloadExample : MonoBehaviour
{
public StartupSmartPreloadSystem preloadSystem;
public Button startPreloadButton;
public Button cancelButton;

void Start()
{
if (preloadSystem == null)
{
preloadSystem = FindObjectOfType<StartupSmartPreloadSystem>();
}

if (startPreloadButton != null)
{
startPreloadButton.onClick.AddListener(StartPreload);
}

if (cancelButton != null)
{
cancelButton.onClick.AddListener(CancelPreload);
}
}

public void StartPreload()
{
if (preloadSystem != null)
{
preloadSystem.StartPreloadSequence();
}
}

public void CancelPreload()
{
if (preloadSystem != null)
{
preloadSystem.CancelPreload();
}
}

void Update()
{
if (preloadSystem != null)
{
// 每秒输出一次状态
if (Time.time % 1 < Time.deltaTime)
{
Debug.Log(preloadSystem.GetPreloadStatus());
}
}
}
}

通过本章的学习,您已经全面掌握了Addressables资源预加载与缓存管理的各个方面,包括预加载依赖、预热策略、缓存系统配置、清理策略以及内存与磁盘缓存的平衡管理。下一章我们将探讨性能分析与诊断工具的使用。