第7章 资源预加载与缓存 发表于 2026-01-03 更新于 2026-01-03
第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); } } } 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); } } 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} " ); } } 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} 个资源" ); } public bool IsGroupPreloaded (string groupName ) { return preloadStatus.ContainsKey(groupName) && preloadStatus[groupName]; } 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 ); } 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} " ); } 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); } } 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 ) { 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); } public string GetUsageStatistics () { int totalAssets = usageRecords.Count; int frequentlyUsed = 0 ; foreach (var record in usageRecords ) { if (record .isFrequentlyUsed) { frequentlyUsed++; } } return $"总资源数: {totalAssets} , 频繁使用: {frequentlyUsed} " ; } public void SaveUsageHistory () { Debug.Log("保存使用历史" ); } public void LoadUsageHistory () { 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) { } } } 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 ; } public void WarmupSceneResources (string sceneName ) { foreach (var profile in warmupProfiles) { if (System.Array.IndexOf(profile.requiredScenes, sceneName) >= 0 ) { StartWarmup(profile.profileName); } } } public void WarmupLevelResources (int levelNumber ) { string profileName = $"Level_{levelNumber} _Warmup" ; StartWarmup(profileName); } public void WarmupUIResources () { StartWarmup("UI_Warmup" ); } public void WarmupAudioResources () { StartWarmup("Audio_Warmup" ); } public void WarmupCharacterResources () { StartWarmup("Character_Warmup" ); } public void CancelAllWarmups () { foreach (var handle in activeWarmups) { Addressables.Release(handle); } activeWarmups.Clear(); warmupQueue.Clear(); isProcessingQueue = false ; Debug.Log("已取消所有预热操作" ); } 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); } } } 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; 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); } } 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} " ); } 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 ; public int cacheExpiryHours = 24 ; public bool compressCache = true ; public string cacheDirectory = "AddressablesCache" ; public bool enableMemoryCache = true ; public long memoryCacheSize = 64 * 1024 * 1024 ; } [Header("缓存设置" ) ] public CacheSettings cacheSettings; [Header("缓存统计" ) ] public bool showCacheStats = true ; private bool isCacheInitialized = false ; void Start () { InitializeCacheSystem(); } 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 ) { Debug.Log($"设置缓存大小限制: {FormatFileSize(sizeLimit)} " ); } private void ConfigureCacheDirectory (string directory ) { string cachePath = System.IO.Path.Combine(Application.persistentDataPath, directory); Debug.Log($"缓存目录: {cachePath} " ); if (!System.IO.Directory.Exists(cachePath)) { System.IO.Directory.CreateDirectory(cachePath); } } 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 ; } private int GetCacheItemCount () { return 50 ; } public void ClearCache () { try { bool success = Addressables.ClearCache(true ); if (success) { Debug.Log("缓存清理成功" ); } else { Debug.LogError("缓存清理失败" ); } } catch (Exception e) { Debug.LogError($"缓存清理异常: {e.Message} " ); } } public void ClearExpiredCache () { Debug.Log($"清理过期缓存({cacheSettings.cacheExpiryHours} 小时前)" ); ClearCache(); } public bool HasEnoughCacheSpace (long requiredSize ) { long cacheSize = GetCacheSize(); long availableSpace = cacheSettings.maxCacheSize - cacheSize; return availableSpace >= requiredSize; } 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) { 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 ; 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); } 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(); } private void CheckCacheSize () { long currentSize = GetCurrentCacheSize(); float usageRatio = (float )currentSize / maxCacheSize; if (usageRatio > cacheCleanupThreshold) { Debug.Log($"缓存使用率过高: {(usageRatio * 100 ):F1} %, 执行清理" ); CleanupCache(); } } private long GetCurrentCacheSize () { long totalSize = 0 ; foreach (var entry in cacheEntries) { totalSize += entry.size; } return totalSize; } 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} 个条目" ); } public void PinCacheEntry (string assetAddress ) { if (cacheEntryMap.ContainsKey(assetAddress)) { cacheEntryMap[assetAddress].isPinned = true ; Debug.Log($"固定缓存条目: {assetAddress} " ); } } public void UnpinCacheEntry (string assetAddress ) { if (cacheEntryMap.ContainsKey(assetAddress)) { cacheEntryMap[assetAddress].isPinned = false ; Debug.Log($"取消固定缓存条目: {assetAddress} " ); } } 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; } } 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} " ; } private void PerformCacheMaintenance () { Debug.Log("执行缓存维护" ); CleanupExpiredEntries(); CompactCache(); Debug.Log($"缓存维护完成 - {GetCacheStatistics()} " ); } private void CleanupExpiredEntries () { 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 () { } 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 ; public bool autoCleanup = true ; public long minFreeSpaceRequired = 100 * 1024 * 1024 ; private AdvancedCacheManager cacheManager; void Start () { cacheManager = FindObjectOfType<AdvancedCacheManager>(); if (cacheManager == null ) { cacheManager = gameObject.AddComponent<AdvancedCacheManager>(); } if (autoCleanup) { InvokeRepeating("PerformSmartCleanup" , 60f , cleanupInterval); } } 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} " ); } public void AddCleanupRule (CleanupRule rule ) { var newRules = new List<CleanupRule>(cleanupRules); newRules.Add(rule); cleanupRules = newRules.ToArray(); Debug.Log($"添加清理规则: {rule.ruleName} " ); } 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} " ); } public void ManualCleanup () { PerformSmartCleanup(); } 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 ; public float memoryCacheRatio = 0.2f ; public float diskCacheRatio = 0.8f ; public long minMemoryCacheSize = 32 * 1024 * 1024 ; public long maxMemoryCacheSize = 128 * 1024 * 1024 ; 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)} " ); } 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 ; } } 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 ); 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 ); 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)} " ); } public long GetMemoryCacheSize () { return currentMemoryCacheSize; } public long GetDiskCacheSize () { return currentDiskCacheSize; } public string GetBalanceStatus () { float memoryRatio = (float )currentMemoryCacheSize / balanceConfig.totalCacheSize; float diskRatio = (float )currentDiskCacheSize / balanceConfig.totalCacheSize; return $"内存缓存: {FormatFileSize(currentMemoryCacheSize)} ({memoryRatio:P1} ), " + $"磁盘缓存: {FormatFileSize(currentDiskCacheSize)} ({diskRatio:P1} )" ; } 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)} " ); } 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 ) { 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; 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 ; 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(); } 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; 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} 秒" ; } 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 ) ; } } 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 ; } 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资源预加载与缓存管理的各个方面,包括预加载依赖、预热策略、缓存系统配置、清理策略以及内存与磁盘缓存的平衡管理。下一章我们将探讨性能分析与诊断工具的使用。