yyl
2025-12-26 5e4c574228c7cfadab0931a798d43b085bfb8844
125 战斗 休息状态 未加载/被清空预加载资源的问题
5个文件已修改
250 ■■■■■ 已修改文件
Main/System/Battle/BattleField/BattleField.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/StoryBattleField.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleResources/BattleCacheManager.cs 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleResources/BattlePreloadManager.cs 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamHero.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/BattleField.cs
@@ -242,16 +242,19 @@
        EventBroadcast.Instance.Broadcast<string>(EventName.BATTLE_START, guid);
    }
    private void PreloadResources(List<TeamBase> redTeamList, List<TeamBase> blueTeamList)
    protected void PreloadResources(List<TeamBase> redTeamList, List<TeamBase> blueTeamLis)
    {
        if (blueTeamList == null || blueTeamList.Count <= 0)
        {
            return;
        }
        bool runDirectly = blueTeamLis == null;
        // 传递战场GUID
        PreloadResAction preloadAction = new PreloadResAction(this, redTeamList, blueTeamList);
        recordPlayer.PlayRecord(preloadAction);
        if (runDirectly)
        {
            preloadAction.Run();
        }
        else
        {
            recordPlayer.PlayRecord(preloadAction);
        }
    }
    protected virtual void LoadMap(int mapID)
Main/System/Battle/BattleField/StoryBattleField.cs
@@ -171,6 +171,7 @@
    protected void ReloadTeam()
    {
        PreloadResources(new List<TeamBase>() { TeamManager.Instance.GetTeam(TeamType.Story) }, null);
        battleObjMgr.ReloadTeam(TeamManager.Instance.GetTeam(TeamType.Story), BattleCamp.Red);
    }
Main/System/Battle/BattleResources/BattleCacheManager.cs
@@ -72,9 +72,10 @@
            
            if (!globalSpineCache.ContainsKey(key))
            {
                // 把 ResourceIdentifier 的 IsPersistent 传递给 CachedResource
                globalSpineCache[key] = new ResourceReference
                {
                    CachedResource = new BattleResCache.CachedResource(res, null, false)
                    CachedResource = new BattleResCache.CachedResource(res, null, res.IsPersistent)
                };
            }
            
@@ -91,9 +92,10 @@
            
            if (!globalAudioCache.ContainsKey(key))
            {
                // 把 ResourceIdentifier 的 IsPersistent 传递给 CachedResource
                globalAudioCache[key] = new ResourceReference
                {
                    CachedResource = new BattleResCache.CachedResource(res, null, false)
                    CachedResource = new BattleResCache.CachedResource(res, null, res.IsPersistent)
                };
            }
            
@@ -106,7 +108,9 @@
    /// <summary>
    /// 注销战场(卸载该战场的所有资源引用)
    /// </summary>
    public void UnregisterBattlefield(string battleGuid)
    /// <param name="battleGuid">战场GUID</param>
    /// <param name="forceUnloadPersistent">是否强制卸载常驻(红队)资源,默认为 false</param>
    public void UnregisterBattlefield(string battleGuid, bool forceUnloadPersistent = false)
    {
        // 处理Spine资源
        var spineKeysToRemove = new List<string>();
@@ -116,7 +120,15 @@
            
            if (kvp.Value.RefCount == 0)
            {
                spineKeysToRemove.Add(kvp.Key);
                // 仅在非常驻或被强制卸载时才真正移除资源
                if (forceUnloadPersistent || kvp.Value.CachedResource.CanUnload())
                {
                    spineKeysToRemove.Add(kvp.Key);
                }
                else
                {
                    Debug.Log($"BattleCacheManager: Skipped unloading persistent spine resource: {kvp.Key}");
                }
            }
        }
        
@@ -142,7 +154,15 @@
            
            if (kvp.Value.RefCount == 0)
            {
                audioKeysToRemove.Add(kvp.Key);
                // 仅在非常驻或被强制卸载时才真正移除资源
                if (forceUnloadPersistent || kvp.Value.CachedResource.CanUnload())
                {
                    audioKeysToRemove.Add(kvp.Key);
                }
                else
                {
                    Debug.Log($"BattleCacheManager: Skipped unloading persistent audio resource: {kvp.Key}");
                }
            }
        }
        
@@ -185,12 +205,13 @@
            {
                Directory = directory,
                AssetName = assetName,
                Type = BattleResCache.ResourceType.Spine
                Type = BattleResCache.ResourceType.Spine,
                IsPersistent = false // on-demand 加载的默认非常驻
            };
            
            globalSpineCache[key] = new ResourceReference
            {
                CachedResource = new BattleResCache.CachedResource(identifier, asset, false)
                CachedResource = new BattleResCache.CachedResource(identifier, asset, identifier.IsPersistent)
            };
        }
        
@@ -219,12 +240,13 @@
            {
                Directory = directory,
                AssetName = assetName,
                Type = BattleResCache.ResourceType.Audio
                Type = BattleResCache.ResourceType.Audio,
                IsPersistent = false // on-demand 加载的默认非常驻
            };
            
            globalAudioCache[key] = new ResourceReference
            {
                CachedResource = new BattleResCache.CachedResource(identifier, asset, false)
                CachedResource = new BattleResCache.CachedResource(identifier, asset, identifier.IsPersistent)
            };
        }
        
@@ -236,6 +258,9 @@
    /// </summary>
    public void UpdateResourceReference(string key, BattleResCache.CachedResource resource, string battleGuid, string ownerId)
    {
        // 确保 CachedResource 的 IsPersistent 与 Identifier 一致
        resource.IsPersistent = resource.Identifier.IsPersistent;
        if (resource.Identifier.Type == BattleResCache.ResourceType.Spine)
        {
            if (globalSpineCache.ContainsKey(key))
@@ -259,6 +284,161 @@
        
        return $"Spine: {spineTotal}, Audio: {audioTotal}";
    }
    /// <summary>
    /// 获取已注册到某个战场的所有 OwnerId(可选择只返回常驻资源的 owner)
    /// </summary>
    public HashSet<string> GetRegisteredOwners(string battleGuid, bool onlyPersistent = false)
    {
        var owners = new HashSet<string>();
        foreach (var kvp in globalSpineCache)
        {
            if (!kvp.Value.BattlefieldOwners.ContainsKey(battleGuid))
                continue;
            if (onlyPersistent && kvp.Value.CachedResource.IsPersistent == false)
                continue;
            foreach (var owner in kvp.Value.BattlefieldOwners[battleGuid])
                owners.Add(owner);
        }
        foreach (var kvp in globalAudioCache)
        {
            if (!kvp.Value.BattlefieldOwners.ContainsKey(battleGuid))
                continue;
            if (onlyPersistent && kvp.Value.CachedResource.IsPersistent == false)
                continue;
            foreach (var owner in kvp.Value.BattlefieldOwners[battleGuid])
                owners.Add(owner);
        }
        return owners;
    }
    /// <summary>
    /// 仅注销战场中指定的 owner(按 owner granularity 移除),仅卸载因此变为无人引用的资源
    /// </summary>
    /// <param name="battleGuid">战场 GUID</param>
    /// <param name="ownersToRemove">要移除的 OwnerId 集合</param>
    public void UnregisterBattlefieldOwners(string battleGuid, HashSet<string> ownersToRemove)
    {
        if (ownersToRemove == null || ownersToRemove.Count == 0)
        {
            Debug.Log($"BattleCacheManager: UnregisterBattlefieldOwners called with empty owners for {battleGuid}");
            return;
        }
        var spineKeysToRemove = new List<string>();
        foreach (var kvp in globalSpineCache)
        {
            if (!kvp.Value.BattlefieldOwners.ContainsKey(battleGuid))
                continue;
            var owners = kvp.Value.BattlefieldOwners[battleGuid];
            bool changed = false;
            foreach (var owner in ownersToRemove)
            {
                if (owners.Contains(owner))
                {
                    owners.Remove(owner);
                    changed = true;
                }
            }
            if (owners.Count == 0)
                kvp.Value.BattlefieldOwners.Remove(battleGuid);
            // 如果全局引用数为0,说明没有任何战场/角色在使用这个资源,安全卸载
            if (kvp.Value.RefCount == 0)
            {
                spineKeysToRemove.Add(kvp.Key);
            }
            else if (changed)
            {
                Debug.Log($"BattleCacheManager: Updated spine owners for {kvp.Key} after removing owners for {battleGuid}");
            }
        }
        foreach (var key in spineKeysToRemove)
        {
            var res = globalSpineCache[key];
            if (res.CachedResource.Asset != null)
            {
                ResManager.Instance.UnloadAsset(
                    res.CachedResource.Identifier.Directory,
                    res.CachedResource.Identifier.AssetName
                );
            }
            globalSpineCache.Remove(key);
            Debug.Log($"BattleCacheManager: Unloaded spine (no owners left): {key}");
        }
        var audioKeysToRemove = new List<string>();
        foreach (var kvp in globalAudioCache)
        {
            if (!kvp.Value.BattlefieldOwners.ContainsKey(battleGuid))
                continue;
            var owners = kvp.Value.BattlefieldOwners[battleGuid];
            bool changed = false;
            foreach (var owner in ownersToRemove)
            {
                if (owners.Contains(owner))
                {
                    owners.Remove(owner);
                    changed = true;
                }
            }
            if (owners.Count == 0)
                kvp.Value.BattlefieldOwners.Remove(battleGuid);
            if (kvp.Value.RefCount == 0)
            {
                audioKeysToRemove.Add(kvp.Key);
            }
            else if (changed)
            {
                Debug.Log($"BattleCacheManager: Updated audio owners for {kvp.Key} after removing owners for {battleGuid}");
            }
        }
        foreach (var key in audioKeysToRemove)
        {
            var res = globalAudioCache[key];
            if (res.CachedResource.Asset != null)
            {
                ResManager.Instance.UnloadAsset(
                    res.CachedResource.Identifier.Directory,
                    res.CachedResource.Identifier.AssetName
                );
            }
            globalAudioCache.Remove(key);
            Debug.Log($"BattleCacheManager: Unloaded audio (no owners left): {key}");
        }
        Debug.Log($"BattleCacheManager: Unregistered specific owners for battlefield {battleGuid}. Removed owners: {ownersToRemove.Count}");
    }
    /// <summary>
    /// 判断该战场是否已注册(有任意资源引用)
    /// </summary>
    public bool HasBattleRegistered(string battleGuid)
    {
        foreach (var kvp in globalSpineCache)
        {
            if (kvp.Value.BattlefieldOwners.ContainsKey(battleGuid)) return true;
        }
        foreach (var kvp in globalAudioCache)
        {
            if (kvp.Value.BattlefieldOwners.ContainsKey(battleGuid)) return true;
        }
        return false;
    }
    
    // ===== 编辑器调试接口 =====
#if UNITY_EDITOR
Main/System/Battle/BattleResources/BattlePreloadManager.cs
@@ -38,6 +38,39 @@
        allSpineResources.AddRange(blueTeamInfo.SpineResources);
        allAudioResources.AddRange(redTeamInfo.AudioResources);
        allAudioResources.AddRange(blueTeamInfo.AudioResources);
        // 检查是否为同一战场的重复注册(比如队伍变更)——如果红队发生变更,需要强制卸载之前的常驻资源
        if (cacheManager.HasBattleRegistered(battleGuid))
        {
            var existingRedOwners = cacheManager.GetRegisteredOwners(battleGuid, true); // 只比较常驻(红队)
            var newRedOwners = new HashSet<string>();
            foreach (var r in redTeamInfo.SpineResources) if (!string.IsNullOrEmpty(r.OwnerId)) newRedOwners.Add(r.OwnerId);
            foreach (var r in redTeamInfo.AudioResources) if (!string.IsNullOrEmpty(r.OwnerId)) newRedOwners.Add(r.OwnerId);
            bool different = false;
            if (existingRedOwners.Count != newRedOwners.Count) different = true;
            else
            {
                foreach (var o in existingRedOwners) if (!newRedOwners.Contains(o)) { different = true; break; }
            }
            if (different)
            {
                // 计算哪些 Owner 被移除,只卸载这些 Owner 引用对应的资源以提高性能
                var removedOwners = new HashSet<string>(existingRedOwners);
                removedOwners.ExceptWith(newRedOwners);
                if (removedOwners.Count > 0)
                {
                    Debug.Log($"BattlePreloadManager: Detected red-team change for {battleGuid}. Removed owners: {removedOwners.Count}. Unloading affected resources only.");
                    cacheManager.UnregisterBattlefieldOwners(battleGuid, removedOwners);
                }
                else
                {
                    Debug.Log($"BattlePreloadManager: Detected red-team change for {battleGuid}, but no owners were removed (only additions). No unload necessary.");
                }
            }
        }
        
        cacheManager.RegisterBattlefieldResources(battleGuid, allSpineResources, allAudioResources);
        
Main/System/Team/TeamHero.cs
@@ -101,6 +101,7 @@
    public TeamHero(HeroInfo heroInfo, int posNum, TeamBase _teamBase)
    {
        heroId = heroInfo.itemHero.config.ID;
        heroConfig = heroInfo.heroConfig;
        SkinID = heroInfo.SkinID;
        skinConfig = heroInfo.skinConfig;
        Country = heroInfo.heroCountry;