第10章 加载速度优化

第10章 加载速度优化

并发加载控制

并发加载基础

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

public class ConcurrentLoadController : MonoBehaviour
{
[System.Serializable]
public class LoadRequest
{
public string address;
public System.Type type;
public System.Action<UnityEngine.Object> onComplete;
public System.Action<string> onError;
public float priority; // 优先级,数值越小优先级越高
public DateTime requestTime;
}

[Header("并发设置")]
public int maxConcurrentLoads = 4;
public float requestTimeout = 30f;
public bool enablePriorityBasedLoading = true;

[Header("性能监控")]
public bool enablePerformanceMonitoring = true;
public float performanceReportInterval = 5f;

private Queue<LoadRequest> requestQueue = new Queue<LoadRequest>();
private List<AsyncOperationHandle> activeLoads = new List<AsyncOperationHandle>();
private Dictionary<string, LoadRequest> pendingRequests = new Dictionary<string, LoadRequest>();
private int totalRequests = 0;
private int completedRequests = 0;
private int failedRequests = 0;

void Start()
{
if (enablePerformanceMonitoring && performanceReportInterval > 0)
{
InvokeRepeating("ReportPerformanceStats", 1f, performanceReportInterval);
}
}

/// <summary>
/// 请求加载资源
/// </summary>
public void RequestLoad(string address, System.Type type,
System.Action<UnityEngine.Object> onComplete = null,
System.Action<string> onError = null,
float priority = 0)
{
var request = new LoadRequest
{
address = address,
type = type,
onComplete = onComplete,
onError = onError,
priority = priority,
requestTime = DateTime.Now
};

// 检查是否已经在加载中
if (pendingRequests.ContainsKey(address))
{
Debug.LogWarning($"资源已在加载队列中: {address}");
return;
}

// 检查当前活跃加载数
if (activeLoads.Count < maxConcurrentLoads)
{
StartLoad(request);
}
else
{
// 添加到队列等待
EnqueueRequest(request);
}

totalRequests++;
pendingRequests[address] = request;

Debug.Log($"请求加载: {address}, 当前队列: {requestQueue.Count}, 活跃: {activeLoads.Count}");
}

/// <summary>
/// 请求加载GameObject
/// </summary>
public void RequestLoadGameObject(string address,
System.Action<GameObject> onComplete = null,
System.Action<string> onError = null,
float priority = 0)
{
RequestLoad(address, typeof(GameObject),
obj => onComplete?.Invoke(obj as GameObject), onError, priority);
}

/// <summary>
/// 请求加载Texture
/// </summary>
public void RequestLoadTexture(string address,
System.Action<Texture> onComplete = null,
System.Action<string> onError = null,
float priority = 0)
{
RequestLoad(address, typeof(Texture),
obj => onComplete?.Invoke(obj as Texture), onError, priority);
}

private void EnqueueRequest(LoadRequest request)
{
if (enablePriorityBasedLoading)
{
// 按优先级插入队列
var queueList = new List<LoadRequest>(requestQueue);
int insertIndex = 0;

for (int i = 0; i < queueList.Count; i++)
{
if (request.priority < queueList[i].priority)
{
insertIndex = i;
break;
}
insertIndex = i + 1;
}

queueList.Insert(insertIndex, request);
requestQueue = new Queue<LoadRequest>(queueList);
}
else
{
requestQueue.Enqueue(request);
}
}

private void StartLoad(LoadRequest request)
{
AsyncOperationHandle handle;

if (request.type == typeof(GameObject))
{
handle = Addressables.LoadAssetAsync<GameObject>(request.address);
}
else if (request.type == typeof(Texture))
{
handle = Addressables.LoadAssetAsync<Texture>(request.address);
}
else if (request.type == typeof(Sprite))
{
handle = Addressables.LoadAssetAsync<Sprite>(request.address);
}
else
{
handle = Addressables.LoadAssetAsync<UnityEngine.Object>(request.address);
}

activeLoads.Add(handle);

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

handle.Completed += (op) =>
{
float loadTime = (float)(DateTime.Now - startTime).TotalSeconds;

if (op.Status == AsyncOperationStatus.Succeeded)
{
completedRequests++;
request.onComplete?.Invoke(op.Result);
Debug.Log($"加载完成: {request.address}, 耗时: {loadTime:F3}s");
}
else
{
failedRequests++;
request.onError?.Invoke(op.OperationException?.Message ?? "未知错误");
Debug.LogError($"加载失败: {request.address}, 错误: {op.OperationException?.Message}");
}

// 从活跃列表中移除
activeLoads.Remove(handle);
pendingRequests.Remove(request.address);

// 处理队列中的下一个请求
ProcessNextRequest();

// 释放句柄
Addressables.Release(handle);
};

// 监控超时
StartCoroutine(MonitorLoadTimeout(request.address, startTime));
}

private System.Collections.IEnumerator MonitorLoadTimeout(string address, DateTime startTime)
{
yield return new UnityEngine.WaitForSeconds(requestTimeout);

if (pendingRequests.ContainsKey(address))
{
var request = pendingRequests[address];
request.onError?.Invoke($"加载超时: {requestTimeout}秒");
Debug.LogWarning($"加载超时: {address}");

// 从队列中移除超时请求
pendingRequests.Remove(address);
ProcessNextRequest();
}
}

private void ProcessNextRequest()
{
if (requestQueue.Count > 0 && activeLoads.Count < maxConcurrentLoads)
{
var nextRequest = requestQueue.Dequeue();
StartLoad(nextRequest);
}
}

/// <summary>
/// 取消加载请求
/// </summary>
public bool CancelLoadRequest(string address)
{
if (pendingRequests.ContainsKey(address))
{
var request = pendingRequests[address];
pendingRequests.Remove(address);

// 如果请求在队列中,从队列移除
var queueList = new List<LoadRequest>(requestQueue);
queueList.RemoveAll(r => r.address == address);
requestQueue = new Queue<LoadRequest>(queueList);

Debug.Log($"取消加载请求: {address}");
return true;
}

return false;
}

/// <summary>
/// 取消所有加载请求
/// </summary>
public void CancelAllRequests()
{
// 清空队列
requestQueue.Clear();

// 清空待处理请求
pendingRequests.Clear();

Debug.Log($"取消所有加载请求,共 {totalRequests} 个");
}

/// <summary>
/// 动态调整并发数
/// </summary>
public void AdjustConcurrency(int newConcurrency)
{
maxConcurrentLoads = Mathf.Max(1, newConcurrency);
Debug.Log($"调整并发数至: {maxConcurrentLoads}");

// 尝试处理队列中的请求
ProcessNextRequest();
}

/// <summary>
/// 获取加载统计
/// </summary>
public string GetLoadStats()
{
float successRate = totalRequests > 0 ? (float)completedRequests / totalRequests * 100 : 0;

return $"总请求: {totalRequests}, 完成: {completedRequests}, " +
$"失败: {failedRequests}, 成功率: {successRate:F1}%, " +
$"队列: {requestQueue.Count}, 活跃: {activeLoads.Count}";
}

private void ReportPerformanceStats()
{
if (enablePerformanceMonitoring)
{
Debug.Log($"[性能报告] 并发加载统计: {GetLoadStats()}");
}
}

void OnDestroy()
{
CancelAllRequests();
if (enablePerformanceMonitoring)
{
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
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class SmartConcurrencyManager : MonoBehaviour
{
[System.Serializable]
public class ConcurrencyRule
{
public string assetType; // "Texture", "Audio", "Model", "Prefab", etc.
public int maxConcurrentLoads;
public int optimalBandwidthKBps; // 最优带宽KB/s
public float priorityWeight; // 优先级权重
}

[Header("智能并发规则")]
public ConcurrencyRule[] concurrencyRules;

[Header("网络检测")]
public bool enableNetworkAdaptation = true;
public float networkCheckInterval = 10f;

private Dictionary<string, int> typeConcurrentLimits = new Dictionary<string, int>();
private Dictionary<string, int> typeActiveLoads = new Dictionary<string, int>();
private int currentNetworkKBps = 0;
private bool isNetworkFast = true;

void Start()
{
InitializeConcurrencyRules();

if (enableNetworkAdaptation)
{
InvokeRepeating("CheckNetworkCondition", 2f, networkCheckInterval);
}
}

private void InitializeConcurrencyRules()
{
foreach (var rule in concurrencyRules)
{
typeConcurrentLimits[rule.assetType] = rule.maxConcurrentLoads;
typeActiveLoads[rule.assetType] = 0;
}

// 设置默认规则
if (!typeConcurrentLimits.ContainsKey("Default"))
{
typeConcurrentLimits["Default"] = 4;
typeActiveLoads["Default"] = 0;
}
}

/// <summary>
/// 检查是否可以开始加载指定类型的资源
/// </summary>
public bool CanStartLoad(string assetType)
{
string typeKey = GetAssetTypeKey(assetType);

if (typeActiveLoads.ContainsKey(typeKey))
{
return typeActiveLoads[typeKey] < typeConcurrentLimits[typeKey];
}

return typeActiveLoads["Default"] < typeConcurrentLimits["Default"];
}

/// <summary>
/// 开始加载(更新计数)
/// </summary>
public void StartLoad(string assetType)
{
string typeKey = GetAssetTypeKey(assetType);

if (typeActiveLoads.ContainsKey(typeKey))
{
typeActiveLoads[typeKey]++;
}
else
{
typeActiveLoads["Default"]++;
}
}

/// <summary>
/// 完成加载(更新计数)
/// </summary>
public void CompleteLoad(string assetType)
{
string typeKey = GetAssetTypeKey(assetType);

if (typeActiveLoads.ContainsKey(typeKey) && typeActiveLoads[typeKey] > 0)
{
typeActiveLoads[typeKey]--;
}
else if (typeActiveLoads.ContainsKey("Default") && typeActiveLoads["Default"] > 0)
{
typeActiveLoads["Default"]--;
}
}

private string GetAssetTypeKey(string assetType)
{
return typeConcurrentLimits.ContainsKey(assetType) ? assetType : "Default";
}

/// <summary>
/// 检查网络条件并调整并发策略
/// </summary>
private void CheckNetworkCondition()
{
StartCoroutine(DetectNetworkSpeed());
}

private System.Collections.IEnumerator DetectNetworkSpeed()
{
string testUrl = "https://httpbin.org/bytes/1048576"; // 1MB测试文件
var request = UnityWebRequest.Get(testUrl);

System.DateTime startTime = DateTime.Now;
yield return request.SendWebRequest();

if (request.result == UnityWebRequest.Result.Success)
{
float duration = (float)(DateTime.Now - startTime).TotalSeconds;
if (duration > 0)
{
currentNetworkKBps = (int)(1024 / duration); // 1MB / duration 秒 = KB/s
isNetworkFast = currentNetworkKBps > 1024; // > 1MB/s认为是快速网络

Debug.Log($"网络速度: {currentNetworkKBps} KB/s, 快速网络: {isNetworkFast}");

// 根据网络速度调整并发数
AdjustConcurrencyForNetworkSpeed();
}
}
else
{
Debug.LogWarning($"网络速度检测失败: {request.error}");
}
}

private void AdjustConcurrencyForNetworkSpeed()
{
// 根据网络速度调整并发限制
float multiplier = isNetworkFast ? 1.5f : 0.7f;

foreach (var rule in concurrencyRules)
{
int newLimit = Mathf.RoundToInt(rule.maxConcurrentLoads * multiplier);
newLimit = Mathf.Clamp(newLimit, 1, 10); // 限制在1-10之间
typeConcurrentLimits[rule.assetType] = newLimit;
}

Debug.Log($"根据网络速度调整并发限制,倍数: {multiplier:F2}");
}

/// <summary>
/// 获取类型加载统计
/// </summary>
public string GetTypeLoadStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== 类型加载统计 ===");

foreach (var pair in typeActiveLoads)
{
int limit = typeConcurrentLimits.ContainsKey(pair.Key) ?
typeConcurrentLimits[pair.Key] : typeConcurrentLimits["Default"];
stats.AppendLine($"{pair.Key}: {pair.Value}/{limit}");
}

stats.AppendLine($"当前网络速度: {currentNetworkKBps} KB/s");
stats.AppendLine($"快速网络: {isNetworkFast}");

return stats.ToString();
}

void OnDestroy()
{
if (enableNetworkAdaptation)
{
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
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;

public class PriorityLoadSystem : MonoBehaviour
{
[System.Serializable]
public class PriorityGroup
{
public string groupName;
public int priorityLevel; // 数值越小优先级越高
public float loadDelay; // 加载延迟(秒)
public bool loadOnStart;
public string[] assetAddresses;
}

[System.Serializable]
public class LoadTask
{
public string address;
public int priority;
public float delay;
public System.Type type;
public System.Action<UnityEngine.Object> onComplete;
public System.Action<string> onError;
public DateTime scheduledTime;
}

[Header("优先级配置")]
public PriorityGroup[] priorityGroups;

[Header("系统设置")]
public int maxConcurrentLoads = 3;
public bool enablePriorityScheduling = true;
public float priorityCheckInterval = 0.1f;

private List<LoadTask> scheduledTasks = new List<LoadTask>();
private List<LoadTask> readyTasks = new List<LoadTask>();
private List<string> activeLoads = new List<string>();
private Dictionary<string, PriorityGroup> groupMap = new Dictionary<string, PriorityGroup>();
private ConcurrentLoadController loadController;
private SmartConcurrencyManager concurrencyManager;

void Start()
{
loadController = GetComponent<ConcurrentLoadController>();
if (loadController == null)
{
loadController = gameObject.AddComponent<ConcurrentLoadController>();
}

concurrencyManager = GetComponent<SmartConcurrencyManager>();
if (concurrencyManager == null)
{
concurrencyManager = gameObject.AddComponent<SmartConcurrencyManager>();
}

// 初始化组映射
foreach (var group in priorityGroups)
{
groupMap[group.groupName] = group;

if (group.loadOnStart)
{
ScheduleGroupLoad(group.groupName);
}
}

// 开始优先级调度
if (enablePriorityScheduling)
{
InvokeRepeating("ProcessPriorityQueue", 0f, priorityCheckInterval);
}
}

/// <summary>
/// 调度组加载
/// </summary>
public void ScheduleGroupLoad(string groupName)
{
if (!groupMap.ContainsKey(groupName))
{
Debug.LogError($"未找到优先级组: {groupName}");
return;
}

var group = groupMap[groupName];

foreach (string address in group.assetAddresses)
{
ScheduleLoad(address, group.priorityLevel, group.loadDelay);
}

Debug.Log($"调度组加载: {groupName}, 优先级: {group.priorityLevel}, 资源数: {group.assetAddresses.Length}");
}

/// <summary>
/// 调度单个资源加载
/// </summary>
public void ScheduleLoad(string address, int priority = 0, float delay = 0f, System.Type type = null)
{
var task = new LoadTask
{
address = address,
priority = priority,
delay = delay,
type = type ?? typeof(UnityEngine.Object),
scheduledTime = DateTime.Now.AddSeconds(delay)
};

scheduledTasks.Add(task);

Debug.Log($"调度加载: {address}, 优先级: {priority}, 延迟: {delay}s");
}

/// <summary>
/// 立即加载高优先级资源
/// </summary>
public void LoadImmediate(string address, System.Action<UnityEngine.Object> onComplete = null,
System.Action<string> onError = null)
{
loadController.RequestLoad(address, typeof(UnityEngine.Object), onComplete, onError, -1000); // 最高优先级
Debug.Log($"立即加载: {address}");
}

/// <summary>
/// 按优先级加载资源
/// </summary>
public void LoadWithPriority(string address, int priority,
System.Action<UnityEngine.Object> onComplete = null,
System.Action<string> onError = null)
{
var task = new LoadTask
{
address = address,
priority = priority,
type = typeof(UnityEngine.Object),
onComplete = onComplete,
onError = onError,
scheduledTime = DateTime.Now
};

// 直接添加到就绪队列(按优先级排序)
AddToReadyQueue(task);
}

private void ProcessPriorityQueue()
{
// 检查计划任务是否到期
var now = DateTime.Now;
var readyToProcess = new List<LoadTask>();

for (int i = scheduledTasks.Count - 1; i >= 0; i--)
{
if (scheduledTasks[i].scheduledTime <= now)
{
readyToProcess.Add(scheduledTasks[i]);
scheduledTasks.RemoveAt(i);
}
}

// 添加到就绪队列
foreach (var task in readyToProcess)
{
AddToReadyQueue(task);
}

// 处理就绪队列
ProcessReadyTasks();
}

private void AddToReadyQueue(LoadTask task)
{
// 按优先级插入
int insertIndex = 0;
for (int i = 0; i < readyTasks.Count; i++)
{
if (task.priority < readyTasks[i].priority)
{
insertIndex = i;
break;
}
insertIndex = i + 1;
}

readyTasks.Insert(insertIndex, task);
}

private void ProcessReadyTasks()
{
// 处理就绪队列中的任务
while (readyTasks.Count > 0 && activeLoads.Count < maxConcurrentLoads)
{
var task = readyTasks[0];
readyTasks.RemoveAt(0);

// 检查并发限制
string assetType = GetAssetTypeFromAddress(task.address);
if (concurrencyManager.CanStartLoad(assetType))
{
concurrencyManager.StartLoad(assetType);
StartLoadTask(task);
}
else
{
// 如果不能开始加载,重新插入队列
AddToReadyQueue(task);
break; // 等待其他加载完成
}
}
}

private void StartLoadTask(LoadTask task)
{
activeLoads.Add(task.address);

loadController.RequestLoad(task.address, task.type,
obj => OnLoadComplete(task, obj),
error => OnLoadError(task, error),
task.priority);
}

private void OnLoadComplete(LoadTask task, UnityEngine.Object obj)
{
activeLoads.Remove(task.address);
concurrencyManager.CompleteLoad(GetAssetTypeFromAddress(task.address));

task.onComplete?.Invoke(obj);
Debug.Log($"优先级加载完成: {task.address}");
}

private void OnLoadError(LoadTask task, string error)
{
activeLoads.Remove(task.address);
concurrencyManager.CompleteLoad(GetAssetTypeFromAddress(task.address));

task.onError?.Invoke(error);
Debug.LogError($"优先级加载失败: {task.address}, 错误: {error}");
}

private string GetAssetTypeFromAddress(string address)
{
// 根据地址后缀判断资源类型
if (address.EndsWith(".png") || address.EndsWith(".jpg"))
return "Texture";
else if (address.EndsWith(".mp3") || address.EndsWith(".wav"))
return "Audio";
else if (address.EndsWith(".fbx") || address.EndsWith(".obj"))
return "Model";
else if (address.EndsWith(".prefab"))
return "Prefab";
else
return "Default";
}

/// <summary>
/// 获取优先级队列状态
/// </summary>
public string GetPriorityQueueStatus()
{
return $"计划任务: {scheduledTasks.Count}, 就绪任务: {readyTasks.Count}, " +
$"活跃加载: {activeLoads.Count}, 最大并发: {maxConcurrentLoads}";
}

/// <summary>
/// 取消优先级加载
/// </summary>
public bool CancelPriorityLoad(string address)
{
// 从计划队列中移除
scheduledTasks.RemoveAll(t => t.address == address);

// 从就绪队列中移除
readyTasks.RemoveAll(t => t.address == address);

// 如果正在加载,尝试取消
bool cancelled = loadController.CancelLoadRequest(address);

Debug.Log($"取消优先级加载: {address}, 已取消: {cancelled}");
return cancelled || !scheduledTasks.Exists(t => t.address == address) ||
!readyTasks.Exists(t => t.address == address);
}

void OnDestroy()
{
if (enablePriorityScheduling)
{
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
using System;
using System.Collections.Generic;
using UnityEngine;

public class DynamicPriorityAdjuster : MonoBehaviour
{
[System.Serializable]
public class PriorityAdjustmentRule
{
public string conditionType; // "ResourceType", "UserAction", "Scene", "Time"
public string conditionValue;
public int priorityAdjustment; // 调整值,可正可负
public float duration; // 调整持续时间(秒)
}

[Header("动态调整规则")]
public PriorityAdjustmentRule[] adjustmentRules;

[Header("用户行为监控")]
public bool monitorUserActions = true;

private Dictionary<string, ActiveAdjustment> activeAdjustments = new Dictionary<string, ActiveAdjustment>();
private Dictionary<string, int> basePriorities = new Dictionary<string, int>();
private Dictionary<string, float> resourceUsage = new Dictionary<string, float>();

[System.Serializable]
public class ActiveAdjustment
{
public int adjustment;
public DateTime endTime;
public PriorityAdjustmentRule rule;
}

void Start()
{
if (monitorUserActions)
{
StartCoroutine(MonitorUserActions());
}
}

/// <summary>
/// 获取资源的实际优先级(考虑动态调整)
/// </summary>
public int GetAdjustedPriority(string resourceAddress, int basePriority = 0)
{
int adjustedPriority = basePriority;

// 应用活跃的调整
foreach (var adjustment in activeAdjustments.Values)
{
adjustedPriority += adjustment.adjustment;
}

// 应用资源使用频率调整
if (resourceUsage.ContainsKey(resourceAddress))
{
// 使用频繁的资源提高优先级
float usageFactor = Mathf.Clamp(resourceUsage[resourceAddress], 0, 10);
adjustedPriority -= (int)(usageFactor * 10); // 使用越多,优先级越高(数值越小)
}

return adjustedPriority;
}

/// <summary>
/// 应用优先级调整规则
/// </summary>
public void ApplyPriorityAdjustment(string resourceType, string conditionValue)
{
foreach (var rule in adjustmentRules)
{
if (rule.conditionType == "ResourceType" && rule.conditionValue == resourceType)
{
ApplyAdjustmentRule(rule);
}
else if (rule.conditionType == "UserAction" && rule.conditionValue == conditionValue)
{
ApplyAdjustmentRule(rule);
}
}
}

private void ApplyAdjustmentRule(PriorityAdjustmentRule rule)
{
string ruleKey = $"{rule.conditionType}_{rule.conditionValue}";

if (activeAdjustments.ContainsKey(ruleKey))
{
// 更新现有调整
var existing = activeAdjustments[ruleKey];
existing.adjustment += rule.priorityAdjustment;
existing.endTime = DateTime.Now.AddSeconds(rule.duration);
existing.rule = rule;
}
else
{
// 添加新调整
activeAdjustments[ruleKey] = new ActiveAdjustment
{
adjustment = rule.priorityAdjustment,
endTime = DateTime.Now.AddSeconds(rule.duration),
rule = rule
};
}

Debug.Log($"应用优先级调整: {ruleKey}, 调整值: {rule.priorityAdjustment}, 持续: {rule.duration}s");
}

/// <summary>
/// 记录资源使用
/// </summary>
public void RecordResourceUsage(string resourceAddress)
{
if (!resourceUsage.ContainsKey(resourceAddress))
{
resourceUsage[resourceAddress] = 0;
}

resourceUsage[resourceAddress] += 1.0f;

// 限制使用计数,防止无限增长
resourceUsage[resourceAddress] = Mathf.Min(resourceUsage[resourceAddress], 100);

Debug.Log($"记录资源使用: {resourceAddress}, 使用次数: {resourceUsage[resourceAddress]}");
}

/// <summary>
/// 监控用户行为
/// </summary>
private System.Collections.IEnumerator MonitorUserActions()
{
while (true)
{
// 检测用户交互
if (Input.GetMouseButtonDown(0))
{
// 鼠标点击 - 可能需要UI资源
ApplyPriorityAdjustment("UserAction", "UIInteraction");
}

if (Input.GetKeyDown(KeyCode.Space))
{
// 空格键 - 可能需要游戏资源
ApplyPriorityAdjustment("UserAction", "GameAction");
}

// 清理过期的调整
CleanupExpiredAdjustments();

yield return new WaitForSeconds(0.1f);
}
}

private void CleanupExpiredAdjustments()
{
var expiredKeys = new List<string>();

foreach (var pair in activeAdjustments)
{
if (DateTime.Now >= pair.Value.endTime)
{
expiredKeys.Add(pair.Key);
}
}

foreach (string key in expiredKeys)
{
activeAdjustments.Remove(key);
Debug.Log($"清理过期优先级调整: {key}");
}
}

/// <summary>
/// 获取调整统计
/// </summary>
public string GetAdjustmentStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== 优先级调整统计 ===");

stats.AppendLine($"活跃调整数: {activeAdjustments.Count}");
stats.AppendLine($"记录资源数: {resourceUsage.Count}");

foreach (var pair in activeAdjustments)
{
float remainingTime = (float)(pair.Value.endTime - DateTime.Now).TotalSeconds;
stats.AppendLine($"{pair.Key}: 调整 {pair.Value.adjustment}, 剩余 {remainingTime:F1}s");
}

return stats.ToString();
}

void Update()
{
// 每帧清理过期调整
CleanupExpiredAdjustments();
}
}

首包资源与分包策略

首包资源管理

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

public class InitialPackageManager : MonoBehaviour
{
[System.Serializable]
public class InitialPackageResource
{
public string address;
public string category; // "Core", "UI", "Audio", "Character", etc.
public bool requiredForStartup; // 是否为启动必需
public int priority; // 启动时加载优先级
public long estimatedSize; // 预估大小
public bool preloadDependencies; // 是否预加载依赖
}

[System.Serializable]
public class PackageConfiguration
{
public string packageName;
public InitialPackageResource[] resources;
public long maxSize; // 最大包大小
public float loadTimeout; // 加载超时
public bool compressPackage; // 是否压缩
}

[Header("首包配置")]
public PackageConfiguration initialPackage;

[Header("分包配置")]
public PackageConfiguration[] additionalPackages;

[Header("加载设置")]
public float startupLoadDelay = 0.1f; // 启动加载延迟
public int maxStartupConcurrentLoads = 2; // 启动时最大并发
public bool enablePreloading = true; // 启用预加载

private Dictionary<string, InitialPackageResource> resourceMap = new Dictionary<string, InitialPackageResource>();
private List<string> loadedResources = new List<string>();
private Dictionary<string, AsyncOperationHandle> activeHandles = new Dictionary<string, AsyncOperationHandle>();
private bool isStartupLoading = false;

[Header("回调事件")]
public System.Action<float> onStartupProgress; // 启动进度 (0-1)
public System.Action onStartupComplete; // 启动完成
public System.Action<string> onStartupError; // 启动错误

void Start()
{
InitializeResourceMap();

if (initialPackage.resources.Length > 0)
{
StartCoroutine(LoadInitialPackage());
}
}

private void InitializeResourceMap()
{
// 初始化首包资源映射
foreach (var resource in initialPackage.resources)
{
resourceMap[resource.address] = resource;
}

// 初始化分包资源映射
foreach (var package in additionalPackages)
{
foreach (var resource in package.resources)
{
if (!resourceMap.ContainsKey(resource.address))
{
resourceMap[resource.address] = resource;
}
}
}
}

/// <summary>
/// 加载首包资源
/// </summary>
private System.Collections.IEnumerator LoadInitialPackage()
{
isStartupLoading = true;
Debug.Log($"开始加载首包,资源数量: {initialPackage.resources.Length}");

// 按优先级排序资源
var sortedResources = new List<InitialPackageResource>(initialPackage.resources);
sortedResources.Sort((a, b) => a.priority.CompareTo(b.priority));

int loadedCount = 0;
int totalCount = sortedResources.Count;

// 分批加载,控制并发
var batch = new List<InitialPackageResource>();
int currentBatchSize = 0;

foreach (var resource in sortedResources)
{
if (currentBatchSize >= maxStartupConcurrentLoads)
{
// 加载当前批次
yield return LoadResourceBatch(batch);

batch.Clear();
currentBatchSize = 0;
}

batch.Add(resource);
currentBatchSize++;

// 如果是必需资源,立即加载
if (resource.requiredForStartup)
{
yield return LoadResourceBatch(batch);
batch.Clear();
currentBatchSize = 0;
}
}

// 加载剩余资源
if (batch.Count > 0)
{
yield return LoadResourceBatch(batch);
}

isStartupLoading = false;
onStartupComplete?.Invoke();

Debug.Log("首包加载完成");
}

private System.Collections.IEnumerator LoadResourceBatch(List<InitialPackageResource> batch)
{
var loadTasks = new List<System.Collections.IEnumerator>();

foreach (var resource in batch)
{
loadTasks.Add(LoadSingleResource(resource));
}

// 并行执行加载任务
foreach (var task in loadTasks)
{
yield return StartCoroutine(task);
}
}

private System.Collections.IEnumerator LoadSingleResource(InitialPackageResource resource)
{
if (loadedResources.Contains(resource.address))
{
yield break; // 已加载
}

try
{
Debug.Log($"加载资源: {resource.address}, 类别: {resource.category}");

var handle = Addressables.LoadAssetAsync<UnityEngine.Object>(resource.address);
activeHandles[resource.address] = handle;

// 监听进度
float progress = 0;
handle.PercentCompleteChanged += (op) =>
{
progress = op.PercentComplete;
UpdateStartupProgress();
};

yield return handle;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
loadedResources.Add(resource.address);

// 如果需要预加载依赖
if (resource.preloadDependencies && enablePreloading)
{
StartCoroutine(PreloadDependencies(resource.address));
}

Debug.Log($"资源加载成功: {resource.address}");
}
else
{
string errorMsg = handle.OperationException?.Message ?? "未知错误";
Debug.LogError($"资源加载失败: {resource.address}, 错误: {errorMsg}");
onStartupError?.Invoke($"资源加载失败: {resource.address}, {errorMsg}");
}
}
catch (Exception e)
{
Debug.LogError($"加载资源异常: {resource.address}, 错误: {e.Message}");
onStartupError?.Invoke($"资源加载异常: {resource.address}, {e.Message}");
}
finally
{
if (activeHandles.ContainsKey(resource.address))
{
Addressables.Release(activeHandles[resource.address]);
activeHandles.Remove(resource.address);
}

UpdateStartupProgress();
}
}

private System.Collections.IEnumerator PreloadDependencies(string resourceAddress)
{
var handle = Addressables.DownloadDependenciesAsync(resourceAddress, false);
yield return handle;

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

Addressables.Release(handle);
}

private void UpdateStartupProgress()
{
if (initialPackage.resources.Length > 0)
{
float progress = (float)loadedResources.Count / initialPackage.resources.Length;
onStartupProgress?.Invoke(progress);
}
}

/// <summary>
/// 加载分包
/// </summary>
public System.Collections.IEnumerator LoadAdditionalPackage(string packageName)
{
var package = System.Array.Find(additionalPackages, p => p.packageName == packageName);
if (package == null)
{
Debug.LogError($"未找到分包: {packageName}");
yield break;
}

Debug.Log($"开始加载分包: {packageName}, 资源数量: {package.resources.Length}");

foreach (var resource in package.resources)
{
yield return LoadSingleResource(ConvertToInitialPackageResource(resource));
yield return new WaitForSeconds(startupLoadDelay); // 添加延迟避免过于密集的加载
}

Debug.Log($"分包加载完成: {packageName}");
}

private InitialPackageResource ConvertToInitialPackageResource(InitialPackageResource resource)
{
// 简单转换,实际可能需要更多处理
return resource;
}

/// <summary>
/// 检查资源是否已加载
/// </summary>
public bool IsResourceLoaded(string address)
{
return loadedResources.Contains(address);
}

/// <summary>
/// 获取首包统计
/// </summary>
public string GetInitialPackageStats()
{
long totalSize = 0;
foreach (var resource in initialPackage.resources)
{
totalSize += resource.estimatedSize;
}

float progress = initialPackage.resources.Length > 0 ?
(float)loadedResources.Count / initialPackage.resources.Length : 0;

return $"首包资源: {loadedResources.Count}/{initialPackage.resources.Length}, " +
$"进度: {(progress * 100):F1}%, 大小: {FormatFileSize(totalSize)}";
}

/// <summary>
/// 获取分包统计
/// </summary>
public string GetAdditionalPackagesStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== 分包统计 ===");

foreach (var package in additionalPackages)
{
int loadedInPackage = 0;
long packageSize = 0;

foreach (var resource in package.resources)
{
if (loadedResources.Contains(resource.address))
{
loadedInPackage++;
}
packageSize += resource.estimatedSize;
}

float packageProgress = package.resources.Length > 0 ?
(float)loadedInPackage / package.resources.Length : 0;

stats.AppendLine($"{package.packageName}: {loadedInPackage}/{package.resources.Length}, " +
$"进度: {(packageProgress * 100):F1}%, 大小: {FormatFileSize(packageSize)}");
}

return stats.ToString();
}

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]}";
}

/// <summary>
/// 取消启动加载
/// </summary>
public void CancelStartupLoading()
{
isStartupLoading = false;

// 释放所有活跃句柄
foreach (var handle in activeHandles.Values)
{
Addressables.Release(handle);
}
activeHandles.Clear();

Debug.Log("启动加载已取消");
}

void OnDestroy()
{
CancelStartupLoading();
}
}

分包策略管理器

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

public class PackageStrategyManager : MonoBehaviour
{
[System.Serializable]
public class PackageStrategy
{
public string strategyName;
public string[] resourceCategories; // 资源类别
public long maxSize; // 最大包大小
public int maxResources; // 最大资源数量
public string[] dependencies; // 依赖的其他包
public bool downloadable; // 是否可下载
public bool cacheable; // 是否可缓存
public int priority; // 加载优先级
}

[Header("分包策略")]
public PackageStrategy[] strategies;

[Header("策略设置")]
public bool autoAssignResources = true;
public bool validatePackageIntegrity = true;

private Dictionary<string, PackageStrategy> strategyMap = new Dictionary<string, PackageStrategy>();
private Dictionary<string, string> resourceToPackageMap = new Dictionary<string, string>();
private Dictionary<string, List<string>> packageResources = new Dictionary<string, List<string>>();

void Start()
{
InitializeStrategies();

if (autoAssignResources)
{
AutoAssignResourcesToPackages();
}
}

private void InitializeStrategies()
{
foreach (var strategy in strategies)
{
strategyMap[strategy.strategyName] = strategy;
packageResources[strategy.strategyName] = new List<string>();
}
}

/// <summary>
/// 自动分配资源到包
/// </summary>
public void AutoAssignResourcesToPackages()
{
// 这里需要获取所有Addressable资源
// 简化实现,使用示例数据
var allResources = GetExampleResources();

foreach (var resource in allResources)
{
string packageName = DeterminePackageForResource(resource);
if (!string.IsNullOrEmpty(packageName))
{
AssignResourceToPackage(resource, packageName);
}
}

Debug.Log("自动资源分配完成");
}

private List<string> GetExampleResources()
{
// 示例资源列表
return new List<string>
{
"Assets/Prefabs/UI/MainMenu.prefab",
"Assets/Prefabs/Characters/Player.prefab",
"Assets/Textures/UI/Background.png",
"Assets/Audio/Music/Battle.mp3",
"Assets/Models/Environment/Tree.fbx"
};
}

private string DeterminePackageForResource(string resourceAddress)
{
// 根据资源路径确定包
if (resourceAddress.Contains("/UI/"))
return "UIPackage";
else if (resourceAddress.Contains("/Characters/"))
return "CharacterPackage";
else if (resourceAddress.Contains("/Audio/"))
return "AudioPackage";
else if (resourceAddress.Contains("/Models/Environment/"))
return "EnvironmentPackage";
else
return "DefaultPackage";
}

private void AssignResourceToPackage(string resourceAddress, string packageName)
{
if (packageResources.ContainsKey(packageName))
{
// 检查包大小和数量限制
var strategy = strategyMap[packageName];
if (packageResources[packageName].Count >= strategy.maxResources)
{
Debug.LogWarning($"包 {packageName} 已达到最大资源数量限制");
return;
}

packageResources[packageName].Add(resourceAddress);
resourceToPackageMap[resourceAddress] = packageName;

Debug.Log($"资源分配到包: {resourceAddress} -> {packageName}");
}
}

/// <summary>
/// 验证包完整性
/// </summary>
public bool ValidatePackageIntegrity(string packageName)
{
if (!packageResources.ContainsKey(packageName))
{
Debug.LogError($"包不存在: {packageName}");
return false;
}

var strategy = strategyMap[packageName];
var resources = packageResources[packageName];

// 检查资源数量
if (resources.Count > strategy.maxResources)
{
Debug.LogWarning($"包 {packageName} 资源数量超限: {resources.Count}/{strategy.maxResources}");
return false;
}

// 检查包大小(需要估算)
long estimatedSize = EstimatePackageSize(resources);
if (estimatedSize > strategy.maxSize)
{
Debug.LogWarning($"包 {packageName} 大小超限: {FormatFileSize(estimatedSize)}/{FormatFileSize(strategy.maxSize)}");
return false;
}

Debug.Log($"包完整性验证通过: {packageName}");
return true;
}

private long EstimatePackageSize(List<string> resources)
{
// 估算包大小(简化实现)
long estimatedSize = 0;
foreach (string resource in resources)
{
// 根据资源类型估算大小
if (resource.EndsWith(".png") || resource.EndsWith(".jpg"))
estimatedSize += 1024 * 1024; // 1MB
else if (resource.EndsWith(".mp3"))
estimatedSize += 2048 * 1024; // 2MB
else if (resource.EndsWith(".fbx"))
estimatedSize += 5120 * 1024; // 5MB
else
estimatedSize += 128 * 1024; // 128KB
}
return estimatedSize;
}

/// <summary>
/// 获取资源所属包
/// </summary>
public string GetPackageForResource(string resourceAddress)
{
if (resourceToPackageMap.ContainsKey(resourceAddress))
{
return resourceToPackageMap[resourceAddress];
}
return null;
}

/// <summary>
/// 获取包中所有资源
/// </summary>
public string[] GetResourcesInPackage(string packageName)
{
if (packageResources.ContainsKey(packageName))
{
return packageResources[packageName].ToArray();
}
return new string[0];
}

/// <summary>
/// 获取包策略
/// </summary>
public PackageStrategy GetPackageStrategy(string packageName)
{
if (strategyMap.ContainsKey(packageName))
{
return strategyMap[packageName];
}
return null;
}

/// <summary>
/// 生成分包报告
/// </summary>
public string GeneratePackageReport()
{
var report = new System.Text.StringBuilder();
report.AppendLine("=== 分包策略报告 ===");

foreach (var strategy in strategies)
{
report.AppendLine();
report.AppendLine($"策略: {strategy.strategyName}");
report.AppendLine($" 类别: {string.Join(", ", strategy.resourceCategories)}");
report.AppendLine($" 最大大小: {FormatFileSize(strategy.maxSize)}");
report.AppendLine($" 最大资源数: {strategy.maxResources}");
report.AppendLine($" 可下载: {strategy.downloadable}");
report.AppendLine($" 可缓存: {strategy.cacheable}");
report.AppendLine($" 优先级: {strategy.priority}");

if (strategy.dependencies.Length > 0)
{
report.AppendLine($" 依赖: {string.Join(", ", strategy.dependencies)}");
}

// 包统计
if (packageResources.ContainsKey(strategy.strategyName))
{
var resources = packageResources[strategy.strategyName];
long packageSize = EstimatePackageSize(resources);

report.AppendLine($" 实际资源数: {resources.Count}");
report.AppendLine($" 实际大小: {FormatFileSize(packageSize)}");
report.AppendLine($" 资源列表: {string.Join(", ", resources.GetRange(0, Math.Min(5, resources.Count)))}...");
}
}

return report.ToString();
}

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]}";
}

/// <summary>
/// 优化包分配
/// </summary>
public void OptimizePackageAssignment()
{
Debug.Log("开始优化包分配...");

// 重新评估资源分配,确保包大小和数量在限制内
foreach (var pair in packageResources)
{
var strategy = strategyMap[pair.Key];
var resources = pair.Value;

// 如果包超出了大小或数量限制,需要重新分配
if (resources.Count > strategy.maxResources ||
EstimatePackageSize(resources) > strategy.maxSize)
{
RebalancePackage(pair.Key, resources, strategy);
}
}

Debug.Log("包分配优化完成");
}

private void RebalancePackage(string packageName, List<string> resources, PackageStrategy strategy)
{
// 将超出限制的资源移动到其他合适的包
var remainingResources = new List<string>();
var movedResources = new List<string>();

long currentSize = 0;
int currentCount = 0;

foreach (string resource in resources)
{
long resourceSize = EstimateResourceSize(resource);

if (currentCount >= strategy.maxResources ||
currentSize + resourceSize > strategy.maxSize)
{
// 需要移动资源
string targetPackage = FindAlternativePackage(resource, packageName);
if (!string.IsNullOrEmpty(targetPackage))
{
AssignResourceToPackage(resource, targetPackage);
movedResources.Add(resource);
}
else
{
remainingResources.Add(resource);
}
}
else
{
remainingResources.Add(resource);
currentSize += resourceSize;
currentCount++;
}
}

// 更新包资源列表
packageResources[packageName] = remainingResources;

Debug.Log($"重新平衡包 {packageName}: 移动 {movedResources.Count} 个资源");
}

private long EstimateResourceSize(string resourceAddress)
{
// 估算单个资源大小
if (resourceAddress.EndsWith(".png") || resourceAddress.EndsWith(".jpg"))
return 1024 * 1024; // 1MB
else if (resourceAddress.EndsWith(".mp3"))
return 2048 * 1024; // 2MB
else if (resourceAddress.EndsWith(".fbx"))
return 5120 * 1024; // 5MB
else
return 128 * 1024; // 128KB
}

private string FindAlternativePackage(string resourceAddress, string originalPackage)
{
// 寻找其他可以容纳资源的包
foreach (var strategy in strategies)
{
if (strategy.strategyName != originalPackage)
{
var resources = packageResources[strategy.strategyName];
long newSize = EstimatePackageSize(resources) + EstimateResourceSize(resourceAddress);

if (resources.Count < strategy.maxResources && newSize <= strategy.maxSize)
{
return strategy.strategyName;
}
}
}

return null; // 没有找到合适的包
}
}

启动时间优化

启动时间分析器

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

public class StartupTimeAnalyzer : MonoBehaviour
{
[System.Serializable]
public class StartupPhase
{
public string phaseName;
public System.DateTime startTime;
public System.DateTime endTime;
public float duration;
public bool completed;
public string status; // "Success", "Failed", "Skipped"
public string details;
}

[Header("启动阶段配置")]
public string[] startupPhases = {
"Initialize Systems",
"Load Core Resources",
"Setup UI",
"Initialize Game Manager",
"Load Player Data",
"Finalize Startup"
};

[Header("性能阈值")]
public float maxStartupTime = 10f; // 最大启动时间(秒)
public float warningStartupTime = 5f; // 警告启动时间
public float maxPhaseTime = 2f; // 最大单个阶段时间

private List<StartupPhase> startupTimeline = new List<StartupPhase>();
private Dictionary<string, StartupPhase> phaseMap = new Dictionary<string, StartupPhase>();
private System.DateTime startupStartTime;
private bool isAnalyzing = false;

void Start()
{
BeginStartupAnalysis();
}

private void BeginStartupAnalysis()
{
isAnalyzing = true;
startupStartTime = DateTime.Now;

// 初始化所有启动阶段
foreach (string phaseName in startupPhases)
{
var phase = new StartupPhase
{
phaseName = phaseName,
startTime = DateTime.Now,
completed = false,
status = "Pending"
};

startupTimeline.Add(phase);
phaseMap[phaseName] = phase;
}

Debug.Log("启动时间分析开始");
}

/// <summary>
/// 开始一个启动阶段
/// </summary>
public void BeginPhase(string phaseName)
{
if (phaseMap.ContainsKey(phaseName))
{
var phase = phaseMap[phaseName];
phase.startTime = DateTime.Now;
phase.status = "Running";

Debug.Log($"开始启动阶段: {phaseName}");
}
}

/// <summary>
/// 完成一个启动阶段
/// </summary>
public void CompletePhase(string phaseName, string details = "", string status = "Success")
{
if (phaseMap.ContainsKey(phaseName))
{
var phase = phaseMap[phaseName];
phase.endTime = DateTime.Now;
phase.duration = (float)(phase.endTime - phase.startTime).TotalSeconds;
phase.completed = true;
phase.status = status;
phase.details = details;

Debug.Log($"完成启动阶段: {phaseName}, 耗时: {phase.duration:F3}s, 状态: {status}");

// 检查阶段时间是否超限
if (phase.duration > maxPhaseTime)
{
Debug.LogWarning($"启动阶段超时: {phaseName}, 耗时: {phase.duration:F3}s, 限制: {maxPhaseTime}s");
}
}
}

/// <summary>
/// 跳过一个启动阶段
/// </summary>
public void SkipPhase(string phaseName, string reason = "")
{
if (phaseMap.ContainsKey(phaseName))
{
var phase = phaseMap[phaseName];
phase.endTime = DateTime.Now;
phase.duration = 0;
phase.completed = false;
phase.status = "Skipped";
phase.details = reason;

Debug.Log($"跳过启动阶段: {phaseName}, 原因: {reason}");
}
}

/// <summary>
/// 完成整个启动过程
/// </summary>
public void CompleteStartup()
{
if (!isAnalyzing) return;

var totalTime = (float)(DateTime.Now - startupStartTime).TotalSeconds;

// 完成所有未完成的阶段
foreach (var phase in startupTimeline)
{
if (!phase.completed)
{
phase.endTime = DateTime.Now;
phase.duration = (float)(phase.endTime - phase.startTime).TotalSeconds;
if (phase.status == "Pending" || phase.status == "Running")
{
phase.status = "Incomplete";
}
}
}

isAnalyzing = false;

Debug.Log($"启动完成,总耗时: {totalTime:F3}s");

// 输出分析报告
string report = GenerateStartupReport();
Debug.Log(report);

// 检查整体时间是否超限
if (totalTime > maxStartupTime)
{
Debug.LogError($"启动时间超限: {totalTime:F3}s, 限制: {maxStartupTime}s");
}
else if (totalTime > warningStartupTime)
{
Debug.LogWarning($"启动时间警告: {totalTime:F3}s, 警告线: {warningStartupTime}s");
}
}

/// <summary>
/// 生成启动报告
/// </summary>
public string GenerateStartupReport()
{
var report = new System.Text.StringBuilder();
report.AppendLine("=== 启动时间分析报告 ===");

var totalTime = (float)(DateTime.Now - startupStartTime).TotalSeconds;
report.AppendLine($"总启动时间: {totalTime:F3}s");
report.AppendLine($"阶段数量: {startupTimeline.Count}");
report.AppendLine();

int completedCount = 0;
float totalPhaseTime = 0;

foreach (var phase in startupTimeline)
{
report.AppendLine($"{phase.phaseName}:");
report.AppendLine($" 状态: {phase.status}");
report.AppendLine($" 耗时: {phase.duration:F3}s");
report.AppendLine($" 开始: {phase.startTime:HH:mm:ss.fff}");
report.AppendLine($" 结束: {phase.endTime:HH:mm:ss.fff}");

if (!string.IsNullOrEmpty(phase.details))
{
report.AppendLine($" 详情: {phase.details}");
}

report.AppendLine();

if (phase.completed) completedCount++;
totalPhaseTime += phase.duration;
}

report.AppendLine("=== 总结 ===");
report.AppendLine($"完成阶段: {completedCount}/{startupTimeline.Count}");
report.AppendLine($"阶段总耗时: {totalPhaseTime:F3}s");
report.AppendLine($"其他时间: {(totalTime - totalPhaseTime):F3}s");

// 性能建议
var suggestions = GetPerformanceSuggestions();
if (suggestions.Count > 0)
{
report.AppendLine();
report.AppendLine("=== 性能优化建议 ===");
foreach (string suggestion in suggestions)
{
report.AppendLine($"• {suggestion}");
}
}

return report.ToString();
}

private List<string> GetPerformanceSuggestions()
{
var suggestions = new List<string>();

// 检查长时间运行的阶段
foreach (var phase in startupTimeline)
{
if (phase.duration > maxPhaseTime * 0.8f) // 超过限制80%的阶段
{
suggestions.Add($"阶段 '{phase.phaseName}' 耗时过长 ({phase.duration:F3}s),考虑优化或异步处理");
}
}

// 检查总体时间
var totalTime = (float)(DateTime.Now - startupStartTime).TotalSeconds;
if (totalTime > warningStartupTime * 0.8f)
{
suggestions.Add($"总体启动时间较长 ({totalTime:F3}s),考虑资源预加载或分阶段加载");
}

return suggestions;
}

/// <summary>
/// 获取当前启动状态
/// </summary>
public string GetStartupStatus()
{
int completed = 0;
float currentDuration = 0;

foreach (var phase in startupTimeline)
{
if (phase.completed) completed++;
currentDuration += phase.duration;
}

float totalTime = isAnalyzing ?
(float)(DateTime.Now - startupStartTime).TotalSeconds : currentDuration;

return $"启动进度: {completed}/{startupTimeline.Count}, " +
$"当前耗时: {totalTime:F3}s, " +
$"阶段完成: {currentDuration:F3}s";
}

/// <summary>
/// 重置分析器
/// </summary>
public void ResetAnalyzer()
{
startupTimeline.Clear();
phaseMap.Clear();
isAnalyzing = false;

Debug.Log("启动时间分析器已重置");
}

void OnDestroy()
{
if (isAnalyzing)
{
CompleteStartup();
}
}
}

启动优化管理器

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

public class StartupOptimizer : MonoBehaviour
{
[System.Serializable]
public class StartupOptimizationConfig
{
public bool enableAsyncLoading = true;
public int maxConcurrentStartupLoads = 3;
public float startupTimeout = 30f;
public bool enableResourcePreloading = true;
public bool enableBackgroundProcessing = true;
public float backgroundProcessInterval = 0.01f;
public bool enableEarlyOut = true; // 允许提前退出
}

[System.Serializable]
public class StartupResource
{
public string address;
public string category; // "Critical", "Important", "Optional"
public bool loadImmediately; // 是否立即加载
public float priority; // 加载优先级
public bool preloadDependencies; // 是否预加载依赖
public System.Action<UnityEngine.Object> onLoaded; // 加载完成回调
}

[Header("启动优化配置")]
public StartupOptimizationConfig optimizationConfig;

[Header("启动资源配置")]
public StartupResource[] startupResources;

[Header("性能监控")]
public bool enablePerformanceMonitoring = true;
public float performanceReportInterval = 2f;

private List<AsyncOperationHandle> activeStartupLoads = new List<AsyncOperationHandle>();
private Dictionary<string, StartupResource> resourceMap = new Dictionary<string, StartupResource>();
private Queue<StartupResource> loadQueue = new Queue<StartupResource>();
private bool isStartupComplete = false;
private System.DateTime startupStartTime;
private StartupTimeAnalyzer timeAnalyzer;

[Header("回调事件")]
public System.Action<float> onStartupProgress; // 启动进度
public System.Action onStartupComplete; // 启动完成
public System.Action<string> onStartupError; // 启动错误

void Start()
{
timeAnalyzer = GetComponent<StartupTimeAnalyzer>();
if (timeAnalyzer == null)
{
timeAnalyzer = gameObject.AddComponent<StartupTimeAnalyzer>();
}

InitializeStartupResources();

if (startupResources.Length > 0)
{
StartCoroutine(ExecuteOptimizedStartup());
}
}

private void InitializeStartupResources()
{
// 按优先级排序启动资源
Array.Sort(startupResources, (a, b) => a.priority.CompareTo(b.priority));

// 构建资源映射
foreach (var resource in startupResources)
{
resourceMap[resource.address] = resource;
}

// 将非立即加载的资源添加到队列
foreach (var resource in startupResources)
{
if (!resource.loadImmediately)
{
loadQueue.Enqueue(resource);
}
}
}

private IEnumerator ExecuteOptimizedStartup()
{
startupStartTime = DateTime.Now;
timeAnalyzer.BeginPhase("Optimized Startup");

Debug.Log($"开始优化启动,资源数量: {startupResources.Length}");

// 首先加载立即加载的资源
var immediateResources = new List<StartupResource>();
foreach (var resource in startupResources)
{
if (resource.loadImmediately)
{
immediateResources.Add(resource);
}
}

if (immediateResources.Count > 0)
{
timeAnalyzer.BeginPhase("Load Immediate Resources");
yield return StartCoroutine(LoadImmediateResources(immediateResources));
timeAnalyzer.CompletePhase("Load Immediate Resources");
}

// 然后加载其他资源
if (loadQueue.Count > 0)
{
timeAnalyzer.BeginPhase("Load Remaining Resources");
yield return StartCoroutine(LoadQueuedResources());
timeAnalyzer.CompletePhase("Load Remaining Resources");
}

// 执行后台处理
if (optimizationConfig.enableBackgroundProcessing)
{
timeAnalyzer.BeginPhase("Background Processing");
yield return StartCoroutine(ExecuteBackgroundProcessing());
timeAnalyzer.CompletePhase("Background Processing");
}

isStartupComplete = true;
timeAnalyzer.CompletePhase("Optimized Startup", "All startup resources loaded", "Success");
timeAnalyzer.CompleteStartup();

onStartupComplete?.Invoke();

Debug.Log("优化启动完成");
}

private IEnumerator LoadImmediateResources(List<StartupResource> resources)
{
var loadTasks = new List<Coroutine>();

foreach (var resource in resources)
{
if (activeStartupLoads.Count >= optimizationConfig.maxConcurrentStartupLoads)
{
// 等待一些加载完成
yield return new WaitUntil(() => activeStartupLoads.Count < optimizationConfig.maxConcurrentStartupLoads);
}

var task = StartCoroutine(LoadSingleStartupResource(resource));
loadTasks.Add(task);

// 添加小延迟以避免过于密集的加载
if (optimizationConfig.backgroundProcessInterval > 0)
{
yield return new WaitForSeconds(optimizationConfig.backgroundProcessInterval);
}
}

// 等待所有立即加载任务完成
foreach (var task in loadTasks)
{
yield return task;
}
}

private IEnumerator LoadQueuedResources()
{
while (loadQueue.Count > 0)
{
// 检查并发限制
if (activeStartupLoads.Count >= optimizationConfig.maxConcurrentStartupLoads)
{
yield return new WaitUntil(() => activeStartupLoads.Count < optimizationConfig.maxConcurrentStartupLoads);
}

var resource = loadQueue.Dequeue();
yield return StartCoroutine(LoadSingleStartupResource(resource));

// 更新进度
UpdateStartupProgress();

// 添加处理间隔
if (optimizationConfig.backgroundProcessInterval > 0)
{
yield return new WaitForSeconds(optimizationConfig.backgroundProcessInterval);
}
}
}

private IEnumerator LoadSingleStartupResource(StartupResource resource)
{
try
{
Debug.Log($"加载启动资源: {resource.address}, 类别: {resource.category}");

var handle = Addressables.LoadAssetAsync<UnityEngine.Object>(resource.address);
activeStartupLoads.Add(handle);

// 设置超时
System.DateTime loadStartTime = DateTime.Now;

// 监听进度
if (optimizationConfig.enablePerformanceMonitoring)
{
handle.PercentCompleteChanged += (op) =>
{
float progress = op.PercentComplete;
Debug.Log($"资源 {resource.address} 加载进度: {(progress * 100):F1}%");
};
}

yield return handle;

float loadDuration = (float)(DateTime.Now - loadStartTime).TotalSeconds;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"启动资源加载成功: {resource.address}, 耗时: {loadDuration:F3}s");

// 执行完成回调
resource.onLoaded?.Invoke(handle.Result);

// 预加载依赖
if (resource.preloadDependencies && optimizationConfig.enableResourcePreloading)
{
StartCoroutine(PreloadResourceDependencies(resource.address));
}
}
else
{
string errorMsg = handle.OperationException?.Message ?? "未知错误";
Debug.LogError($"启动资源加载失败: {resource.address}, 错误: {errorMsg}");
onStartupError?.Invoke($"资源加载失败: {resource.address}, {errorMsg}");
}
}
catch (System.Exception e)
{
Debug.LogError($"加载启动资源异常: {resource.address}, 错误: {e.Message}");
onStartupError?.Invoke($"资源加载异常: {resource.address}, {e.Message}");
}
finally
{
if (activeStartupLoads.Contains(handle))
{
activeStartupLoads.Remove(handle);
Addressables.Release(handle);
}

UpdateStartupProgress();
}
}

private IEnumerator PreloadResourceDependencies(string resourceAddress)
{
var handle = Addressables.DownloadDependenciesAsync(resourceAddress, false);
yield return handle;

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

Addressables.Release(handle);
}

private IEnumerator ExecuteBackgroundProcessing()
{
// 执行一些后台处理任务
yield return StartCoroutine(ProcessMemoryCleanup());
yield return StartCoroutine(OptimizeResourceCaching());
}

private IEnumerator ProcessMemoryCleanup()
{
// 强制垃圾回收
System.GC.Collect();
yield return new WaitForEndOfFrame();

// 等待一段时间让GC完成
yield return new WaitForSeconds(0.1f);

Debug.Log("后台内存清理完成");
}

private IEnumerator OptimizeResourceCaching()
{
// 优化资源缓存
yield return new WaitForSeconds(0.05f);

Debug.Log("资源缓存优化完成");
}

private void UpdateStartupProgress()
{
if (startupResources.Length > 0)
{
int loadedCount = startupResources.Length - loadQueue.Count;
float progress = (float)loadedCount / startupResources.Length;
onStartupProgress?.Invoke(progress);
}
}

/// <summary>
/// 获取启动统计
/// </summary>
public string GetStartupStats()
{
int totalResources = startupResources.Length;
int loadedResources = totalResources - loadQueue.Count;
float progress = totalResources > 0 ? (float)loadedResources / totalResources : 0;
float elapsed = (float)(DateTime.Now - startupStartTime).TotalSeconds;

return $"启动资源: {loadedResources}/{totalResources}, 进度: {(progress * 100):F1}%, " +
$"耗时: {elapsed:F3}s, 活跃加载: {activeStartupLoads.Count}";
}

/// <summary>
/// 取消启动过程
/// </summary>
public void CancelStartup()
{
// 释放所有活跃加载
foreach (var handle in activeStartupLoads)
{
Addressables.Release(handle);
}
activeStartupLoads.Clear();

// 清空加载队列
loadQueue.Clear();

isStartupComplete = true;

Debug.Log("启动过程已取消");
}

void OnDestroy()
{
if (!isStartupComplete)
{
CancelStartup();
}
}
}

WebGL平台特殊优化

WebGL特定优化

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
using System;
using System.Collections.Generic;
using UnityEngine;
#if UNITY_WEBGL
using System.Runtime.InteropServices;
#endif

public class WebGLSpecificOptimizer : MonoBehaviour
{
[System.Serializable]
public class WebGLOptimizationSettings
{
public bool enableAssetBundling = true;
public int maxConcurrentDownloads = 4;
public long maxDownloadSize = 50 * 1024 * 1024; // 50MB
public bool enableStreamingAssets = true;
public bool compressAssets = true;
public int compressionLevel = 6; // 1-9
public bool enableMemoryOptimization = true;
public long maxMemoryUsage = 256 * 1024 * 1024; // 256MB
public bool enableThreading = false; // WebGL不支持多线程
public bool enableWasmOptimization = true;
}

[Header("WebGL优化设置")]
public WebGLOptimizationSettings webglSettings;

[Header("内存监控")]
public bool enableMemoryMonitoring = true;
public float memoryCheckInterval = 2f;

private bool isWebGLPlatform = false;
private long peakMemoryUsage = 0;
private List<long> memoryUsageHistory = new List<long>();

void Start()
{
isWebGLPlatform = Application.platform == RuntimePlatform.WebGLPlayer;

if (isWebGLPlatform)
{
ApplyWebGLOptimizations();

if (enableMemoryMonitoring)
{
InvokeRepeating("MonitorMemoryUsage", 1f, memoryCheckInterval);
}
}
else
{
Debug.LogWarning("当前不是WebGL平台,WebGL优化不适用");
}
}

private void ApplyWebGLOptimizations()
{
Debug.Log("应用WebGL特定优化...");

// 设置并发下载限制
SetWebGLConcurrentDownloadLimit(webglSettings.maxConcurrentDownloads);

// 配置内存使用限制
ConfigureMemoryLimits();

// 优化资源加载策略
OptimizeResourceLoading();

// 配置压缩设置
ConfigureCompressionSettings();

Debug.Log("WebGL优化应用完成");
}

private void SetWebGLConcurrentDownloadLimit(int maxDownloads)
{
// 在WebGL中,浏览器通常限制并发连接数
// 这里设置一个合理的限制
Debug.Log($"设置WebGL并发下载限制: {maxDownloads}");

// 对Addressables系统进行配置
// 这可能需要通过Addressables的Profile或运行时配置
}

private void ConfigureMemoryLimits()
{
if (webglSettings.enableMemoryOptimization)
{
Debug.Log($"配置WebGL内存限制: {FormatFileSize(webglSettings.maxMemoryUsage)}");

// 在WebGL中监控内存使用
peakMemoryUsage = webglSettings.maxMemoryUsage * 8 / 10; // 使用80%作为峰值限制
}
}

private void OptimizeResourceLoading()
{
Debug.Log("优化WebGL资源加载策略...");

// 对于大资源,使用流式加载
// 优化纹理压缩格式
// 调整加载优先级
}

private void ConfigureCompressionSettings()
{
if (webglSettings.compressAssets)
{
Debug.Log($"配置WebGL资源压缩,级别: {webglSettings.compressionLevel}");

// 配置构建时的压缩设置
// 这通常在构建设置中配置
}
}

#if UNITY_WEBGL
[DllImport("__Internal")]
private static extern long GetWebGLMemoryUsage();

[DllImport("__Internal")]
private static extern void SetWebGLMemoryLimit(long limit);
#endif

private void MonitorMemoryUsage()
{
if (!isWebGLPlatform) return;

long currentMemory = GetCurrentMemoryUsage();
memoryUsageHistory.Add(currentMemory);

// 保持历史记录在合理范围内
if (memoryUsageHistory.Count > 50)
{
memoryUsageHistory.RemoveAt(0);
}

// 检查内存使用是否超过限制
if (currentMemory > webglSettings.maxMemoryUsage)
{
Debug.LogWarning($"WebGL内存使用超限: {FormatFileSize(currentMemory)}, 限制: {FormatFileSize(webglSettings.maxMemoryUsage)}");

// 执行内存清理
PerformMemoryCleanup();
}

// 更新峰值记录
if (currentMemory > peakMemoryUsage)
{
peakMemoryUsage = currentMemory;
}

Debug.Log($"WebGL内存监控 - 当前: {FormatFileSize(currentMemory)}, 峰值: {FormatFileSize(peakMemoryUsage)}");
}

private long GetCurrentMemoryUsage()
{
#if UNITY_WEBGL
// 尝试获取WebGL的实际内存使用
try
{
return GetWebGLMemoryUsage();
}
catch
{
// 如果无法获取实际内存使用,使用Unity的内存统计
return Profiling.Profiler.usedHeapSizeLong;
}
#else
// 非WebGL平台使用Unity内存统计
return Profiling.Profiler.usedHeapSizeLong;
#endif
}

private void PerformMemoryCleanup()
{
Debug.Log("执行WebGL内存清理...");

// 强制垃圾回收
System.GC.Collect();

// 清理不需要的资源
Resources.UnloadUnusedAssets();

// 等待下一帧完成清理
StartCoroutine(WaitForCleanup());
}

private System.Collections.IEnumerator WaitForCleanup()
{
yield return new UnityEngine.WaitForEndOfFrame();
System.GC.Collect();
}

/// <summary>
/// 优化WebGL资源包大小
/// </summary>
public void OptimizeAssetBundleForWebGL(string assetBundlePath)
{
if (!isWebGLPlatform) return;

Debug.Log($"优化WebGL AssetBundle: {assetBundlePath}");

// 对AssetBundle进行WebGL特定优化
// 如压缩、格式优化等
}

/// <summary>
/// 获取WebGL优化统计
/// </summary>
public string GetWebGLOptimizationStats()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== WebGL优化统计 ===");

stats.AppendLine($"平台: {(isWebGLPlatform ? "WebGL" : "非WebGL")}");
stats.AppendLine($"并发下载限制: {webglSettings.maxConcurrentDownloads}");
stats.AppendLine($"最大下载大小: {FormatFileSize(webglSettings.maxDownloadSize)}");
stats.AppendLine($"最大内存使用: {FormatFileSize(webglSettings.maxMemoryUsage)}");
stats.AppendLine($"压缩级别: {webglSettings.compressionLevel}");

if (enableMemoryMonitoring && memoryUsageHistory.Count > 0)
{
long avgMemory = 0;
foreach (long usage in memoryUsageHistory)
{
avgMemory += usage;
}
avgMemory /= memoryUsageHistory.Count;

stats.AppendLine($"平均内存使用: {FormatFileSize(avgMemory)}");
stats.AppendLine($"峰值内存使用: {FormatFileSize(peakMemoryUsage)}");
stats.AppendLine($"内存历史记录: {memoryUsageHistory.Count}");
}

return stats.ToString();
}

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]}";
}

/// <summary>
/// 检查WebGL兼容性
/// </summary>
public bool CheckWebGLCompatibility()
{
if (!isWebGLPlatform) return false;

// 检查各种WebGL限制
bool isCompatible = true;
var issues = new List<string>();

// 检查内存限制
if (webglSettings.maxMemoryUsage > 512 * 1024 * 1024) // 512MB
{
issues.Add("内存限制过高,WebGL浏览器通常限制在512MB以下");
isCompatible = false;
}

// 检查并发下载限制
if (webglSettings.maxConcurrentDownloads > 6)
{
issues.Add("并发下载数过高,浏览器通常限制在6个以下");
isCompatible = false;
}

if (issues.Count > 0)
{
Debug.LogWarning($"WebGL兼容性问题:\n{string.Join("\n", issues)}");
}

return isCompatible;
}

void OnDestroy()
{
if (enableMemoryMonitoring)
{
CancelInvoke();
}
}
}

WebGL资源加载优化器

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

public class WebGLResourceLoaderOptimizer : MonoBehaviour
{
[System.Serializable]
public class WebGLResourceProfile
{
public string profileName;
public int maxConcurrentLoads = 2;
public float loadDelay = 0.1f;
public long maxResourceSize = 10 * 1024 * 1024; // 10MB
public bool enableCaching = true;
public int cacheSizeLimit = 50; // 50MB
public bool compressLargeResources = true;
}

[Header("WebGL资源加载配置")]
public WebGLResourceProfile[] resourceProfiles;

[Header("动态调整")]
public bool enableDynamicAdjustment = true;
public float adjustmentCheckInterval = 5f;

private Dictionary<string, WebGLResourceProfile> profileMap = new Dictionary<string, WebGLResourceProfile>();
private Dictionary<string, AsyncOperationHandle> activeLoads = new Dictionary<string, AsyncOperationHandle>();
private Queue<string> loadQueue = new Queue<string>();
private string currentProfile = "Default";

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

// 设置默认配置文件
if (resourceProfiles.Length > 0)
{
currentProfile = resourceProfiles[0].profileName;
}

if (enableDynamicAdjustment)
{
InvokeRepeating("AdjustForPerformance", 2f, adjustmentCheckInterval);
}
}

/// <summary>
/// 加载WebGL优化的资源
/// </summary>
public async System.Threading.Tasks.Task<UnityEngine.Object> LoadWebGLResource(string address,
string resourceType = "Generic")
{
var profile = GetCurrentProfile();

// 检查是否超过并发限制
while (activeLoads.Count >= profile.maxConcurrentLoads)
{
await System.Threading.Tasks.Task.Delay(100);
}

try
{
// 检查资源大小(如果可能)
if (await IsLargeResource(address))
{
if (profile.compressLargeResources)
{
Debug.Log($"压缩大资源加载: {address}");
}
}

var handle = Addressables.LoadAssetAsync<UnityEngine.Object>(address);
activeLoads[address] = handle;

var resource = await handle.Task;

if (handle.Status == AsyncOperationStatus.Succeeded)
{
Debug.Log($"WebGL资源加载成功: {address}");
}
else
{
Debug.LogError($"WebGL资源加载失败: {address}");
}

activeLoads.Remove(address);
Addressables.Release(handle);

return resource;
}
catch (Exception e)
{
Debug.LogError($"WebGL资源加载异常: {e.Message}");
activeLoads.Remove(address);
return null;
}
}

private async System.Threading.Tasks.Task<bool> IsLargeResource(string address)
{
// 简化的资源大小检查
// 在实际实现中,可能需要更精确的方法
return address.Length > 100; // 简单示例
}

private WebGLResourceProfile GetCurrentProfile()
{
if (profileMap.ContainsKey(currentProfile))
{
return profileMap[currentProfile];
}

// 返回默认配置
return new WebGLResourceProfile
{
maxConcurrentLoads = 2,
loadDelay = 0.1f,
maxResourceSize = 10 * 1024 * 1024
};
}

private void AdjustForPerformance()
{
if (!enableDynamicAdjustment) return;

var profile = GetCurrentProfile();

// 根据当前性能动态调整配置
float memoryUsage = (float)Profiling.Profiler.usedHeapSizeLong / (1024 * 1024); // MB

if (memoryUsage > 200) // 200MB以上
{
// 降低并发数以减少内存压力
profile.maxConcurrentLoads = Mathf.Max(1, profile.maxConcurrentLoads - 1);
Debug.LogWarning($"内存使用高 ({memoryUsage:F1}MB),降低并发加载数至 {profile.maxConcurrentLoads}");
}
else if (memoryUsage < 100 && profile.maxConcurrentLoads < 4)
{
// 增加并发数
profile.maxConcurrentLoads = Mathf.Min(4, profile.maxConcurrentLoads + 1);
Debug.Log($"内存使用正常,增加并发加载数至 {profile.maxConcurrentLoads}");
}
}

/// <summary>
/// 切换资源加载配置文件
/// </summary>
public bool SwitchProfile(string profileName)
{
if (profileMap.ContainsKey(profileName))
{
currentProfile = profileName;
Debug.Log($"切换到WebGL资源配置: {profileName}");
return true;
}

Debug.LogError($"未找到WebGL资源配置: {profileName}");
return false;
}

/// <summary>
/// 获取当前配置统计
/// </summary>
public string GetCurrentProfileStats()
{
var profile = GetCurrentProfile();

return $"当前配置: {currentProfile}, 并发加载: {profile.maxConcurrentLoads}, " +
$"最大资源大小: {FormatFileSize(profile.maxResourceSize)}, " +
$"活跃加载: {activeLoads.Count}";
}

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()
{
if (enableDynamicAdjustment)
{
CancelInvoke();
}

// 释放所有活跃加载
foreach (var handle in activeLoads.Values)
{
Addressables.Release(handle);
}
activeLoads.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
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
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;

public class ComprehensiveStartupOptimizer : MonoBehaviour
{
[System.Serializable]
public class StartupPhaseConfig
{
public string phaseName;
public string[] requiredResources;
public int maxLoadTime; // 最大加载时间(秒)
public bool optional; // 是否可选
public System.Action onCompleted; // 完成回调
public System.Action<string> onError; // 错误回调
}

[System.Serializable]
public class StartupResourceGroup
{
public string groupName;
public string[] resourceAddresses;
public int priority; // 加载优先级
public bool preloadDependencies;
public float timeout; // 超时时间
}

[Header("启动阶段配置")]
public StartupPhaseConfig[] startupPhases;

[Header("资源组配置")]
public StartupResourceGroup[] resourceGroups;

[Header("全局设置")]
public int maxConcurrentLoads = 3;
public float globalTimeout = 60f;
public bool enableProgressUI = true;
public bool enableFallbackMechanisms = true;

[Header("性能监控")]
public bool enablePerformanceMonitoring = true;
public float performanceReportInterval = 3f;

private Dictionary<string, StartupResourceGroup> groupMap = new Dictionary<string, StartupResourceGroup>();
private List<AsyncOperationHandle> activeHandles = new List<AsyncOperationHandle>();
private Dictionary<string, UnityEngine.Object> loadedResources = new Dictionary<string, UnityEngine.Object>();
private System.DateTime startupStartTime;
private bool isStartupComplete = false;
private int completedPhases = 0;
private int totalPhases = 0;
private StartupTimeAnalyzer timeAnalyzer;
private ConcurrentLoadController loadController;
private PriorityLoadSystem prioritySystem;

[Header("回调事件")]
public System.Action<float> onProgressChanged; // 进度变化
public System.Action onStartupCompleted; // 启动完成
public System.Action<string> onStartupFailed; // 启动失败

void Start()
{
InitializeSystems();
StartCoroutine(ExecuteComprehensiveStartup());
}

private void InitializeSystems()
{
// 初始化分析器
timeAnalyzer = GetComponent<StartupTimeAnalyzer>();
if (timeAnalyzer == null)
{
timeAnalyzer = gameObject.AddComponent<StartupTimeAnalyzer>();
}

// 初始化加载控制器
loadController = GetComponent<ConcurrentLoadController>();
if (loadController == null)
{
loadController = gameObject.AddComponent<ConcurrentLoadController>();
loadController.maxConcurrentLoads = maxConcurrentLoads;
}

// 初始化优先级系统
prioritySystem = GetComponent<PriorityLoadSystem>();
if (prioritySystem == null)
{
prioritySystem = gameObject.AddComponent<PriorityLoadSystem>();
}

// 初始化资源组映射
foreach (var group in resourceGroups)
{
groupMap[group.groupName] = group;
}

totalPhases = startupPhases.Length;
startupStartTime = DateTime.Now;

Debug.Log($"启动综合优化系统,阶段数: {totalPhases}, 资源组数: {resourceGroups.Length}");
}

private IEnumerator ExecuteComprehensiveStartup()
{
Debug.Log("开始综合启动优化流程...");
timeAnalyzer.BeginPhase("Comprehensive Startup");

// 执行各个启动阶段
foreach (var phase in startupPhases)
{
timeAnalyzer.BeginPhase(phase.phaseName);

bool phaseSuccess = false;
if (phase.requiredResources.Length > 0)
{
phaseSuccess = await ExecuteStartupPhase(phase);
}
else
{
// 无资源需求的阶段,直接标记成功
phaseSuccess = true;
}

if (phaseSuccess)
{
completedPhases++;
phase.onCompleted?.Invoke();
timeAnalyzer.CompletePhase(phase.phaseName, "Phase completed successfully", "Success");

Debug.Log($"启动阶段完成: {phase.phaseName}");
}
else
{
if (!phase.optional)
{
// 关键阶段失败,启动失败
string errorMsg = $"关键启动阶段失败: {phase.phaseName}";
Debug.LogError(errorMsg);
timeAnalyzer.CompletePhase(phase.phaseName, errorMsg, "Failed");
timeAnalyzer.CompletePhase("Comprehensive Startup", errorMsg, "Failed");

onStartupFailed?.Invoke(errorMsg);
yield break;
}
else
{
// 可选阶段失败,记录但继续
string errorMsg = $"可选启动阶段失败: {phase.phaseName}";
Debug.LogWarning(errorMsg);
phase.onError?.Invoke(errorMsg);
timeAnalyzer.CompletePhase(phase.phaseName, errorMsg, "Failed");
}
}

// 更新整体进度
UpdateOverallProgress();

// 短暂延迟,避免过于密集的操作
yield return new WaitForSeconds(0.1f);
}

// 启动完成
isStartupComplete = true;
timeAnalyzer.CompletePhase("Comprehensive Startup", "All phases completed", "Success");
timeAnalyzer.CompleteStartup();

onStartupCompleted?.Invoke();

Debug.Log("综合启动优化流程完成");
}

private async System.Threading.Tasks.Task<bool> ExecuteStartupPhase(StartupPhaseConfig phase)
{
Debug.Log($"执行启动阶段: {phase.phaseName}, 资源数: {phase.requiredResources.Length}");

var loadTasks = new List<System.Threading.Tasks.Task<bool>>();

// 并发加载阶段所需资源
foreach (string resourceAddress in phase.requiredResources)
{
var task = LoadResourceWithTimeout(resourceAddress, phase.maxLoadTime);
loadTasks.Add(task);

// 控制并发数
if (activeHandles.Count >= maxConcurrentLoads)
{
await System.Threading.Tasks.Task.Delay(50);
}
}

// 等待所有资源加载完成
var results = await System.Threading.Tasks.Task.WhenAll(loadTasks);

// 检查是否有加载失败的资源
bool allSuccessful = true;
foreach (bool result in results)
{
if (!result)
{
allSuccessful = false;
break;
}
}

return allSuccessful;
}

private async System.Threading.Tasks.Task<bool> LoadResourceWithTimeout(string address, int timeoutSeconds)
{
try
{
var completionSource = new System.Threading.Tasks.TaskCompletionSource<bool>();
AsyncOperationHandle<UnityEngine.Object> handle = Addressables.LoadAssetAsync<UnityEngine.Object>(address);

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

// 监听完成
handle.Completed += (op) =>
{
if (op.Status == AsyncOperationStatus.Succeeded)
{
loadedResources[address] = op.Result;
completionSource.SetResult(true);
Debug.Log($"资源加载成功: {address}");
}
else
{
completionSource.SetResult(false);
Debug.LogError($"资源加载失败: {address}, 错误: {op.OperationException?.Message}");
}
};

// 超时处理
System.Threading.CancellationTokenSource timeoutToken = new System.Threading.CancellationTokenSource();
timeoutToken.Token.Register(() => completionSource.SetResult(false));

// 启动超时计时
_ = System.Threading.Tasks.Task.Delay(timeoutSeconds * 1000, timeoutToken.Token);

bool result = await completionSource.Task;

// 取消超时计时
timeoutToken.Cancel();

// 释放句柄
Addressables.Release(handle);

return result;
}
catch (Exception e)
{
Debug.LogError($"加载资源异常: {address}, 错误: {e.Message}");
return false;
}
}

/// <summary>
/// 预加载资源组
/// </summary>
public void PreloadResourceGroup(string groupName)
{
if (groupMap.ContainsKey(groupName))
{
var group = groupMap[groupName];

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

foreach (string address in group.resourceAddresses)
{
// 使用优先级系统加载
prioritySystem.LoadWithPriority(address, group.priority,
obj => OnResourceLoaded(address, obj),
error => OnResourceLoadError(address, error));
}
}
}

private void OnResourceLoaded(string address, UnityEngine.Object obj)
{
loadedResources[address] = obj;
Debug.Log($"资源预加载完成: {address}");
}

private void OnResourceLoadError(string address, string error)
{
Debug.LogError($"资源预加载失败: {address}, 错误: {error}");

if (enableFallbackMechanisms)
{
TryFallbackLoad(address);
}
}

private void TryFallbackLoad(string address)
{
Debug.Log($"尝试回退加载: {address}");

// 实现回退加载逻辑
// 例如:从备用服务器加载、使用默认资源等
}

private void UpdateOverallProgress()
{
float progress = totalPhases > 0 ? (float)completedPhases / totalPhases : 0;
onProgressChanged?.Invoke(progress);

if (enableProgressUI)
{
Debug.Log($"启动进度: {(progress * 100):F1}% ({completedPhases}/{totalPhases})");
}
}

/// <summary>
/// 获取启动统计
/// </summary>
public string GetStartupStatistics()
{
var stats = new System.Text.StringBuilder();
stats.AppendLine("=== 综合启动统计 ===");

var totalTime = (float)(DateTime.Now - startupStartTime).TotalSeconds;
stats.AppendLine($"总耗时: {totalTime:F3}s");
stats.AppendLine($"完成阶段: {completedPhases}/{totalPhases}");
stats.AppendLine($"加载资源数: {loadedResources.Count}");
stats.AppendLine($"活跃加载: {activeHandles.Count}");
stats.AppendLine($"并发限制: {maxConcurrentLoads}");

return stats.ToString();
}

/// <summary>
/// 立即完成启动(跳过剩余阶段)
/// </summary>
public void SkipToCompletion()
{
Debug.LogWarning("跳过剩余启动阶段,立即完成启动");

isStartupComplete = true;
timeAnalyzer.CompletePhase("Comprehensive Startup", "Skipped to completion", "Success");
timeAnalyzer.CompleteStartup();

onStartupCompleted?.Invoke();
}

/// <summary>
/// 重新开始启动流程
/// </summary>
public void RestartStartup()
{
Debug.Log("重启启动流程");

// 清理当前状态
loadedResources.Clear();
activeHandles.Clear();
completedPhases = 0;
isStartupComplete = false;

// 重新开始
StartCoroutine(ExecuteComprehensiveStartup());
}

void OnDestroy()
{
if (!isStartupComplete)
{
// 清理资源
foreach (var handle in activeHandles)
{
Addressables.Release(handle);
}
activeHandles.Clear();

// 尝试完成分析器
timeAnalyzer?.CompleteStartup();
}
}
}

通过本章的学习,您已经全面掌握了Addressables加载速度优化的各个方面,包括并发加载控制、资源加载优先级管理、首包与分包策略、启动时间优化以及WebGL平台的特殊优化技术。下一章我们将深入探讨Addressables系统的底层架构和实现原理。