hch
2025-08-02 0b72d489d989007a827c1f8ca33248441a6e85f9
122 子 【武将】武将系统 / 【武将】武将系统-客户端 - 布阵
24个文件已修改
1个文件已删除
1 文件已复制
2个文件已添加
1 文件已重命名
858 ■■■■ 已修改文件
Main/Component/UI/Common/GroupButtonEx.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Common/GroupButtonExManager.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Effect/UIEffectPlayer.cs 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigManager.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/HeroFetterConfig.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/HeroLineupHaloConfig.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/PlayerPropertyConfig.cs 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/ResModule/GameObjectPoolManager.cs 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroFetterInfo.cs 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Fetter.cs 45 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.Properties.cs 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroInfo.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroManager.cs 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/UIHeroController.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroConnectionCell.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroConnectionHeadCell.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationCell.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationCell.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationWin.cs 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroFormationWin.cs.meta 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroPosWin.cs 179 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroScenePosCell.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroSelectBehaviour.cs 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.cs 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Main/HomeWin.cs 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Message/ColorAnalysis.cs 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamBase.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/EnumHelper.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/UIHelper.cs 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Common/GroupButtonEx.cs
@@ -115,8 +115,6 @@
            m_Manager.SelectButton(this);
        }
        // 设置当前按钮为选中状态
        state = TitleBtnState.Click;
    }
    // 更新按钮状态
Main/Component/UI/Common/GroupButtonExManager.cs
@@ -7,6 +7,7 @@
using System.Collections.Generic;
using System;
using Cysharp.Threading.Tasks;
/// <summary>
/// 按钮组管理器,负责管理GroupButtonEx组件的组关系和状态切换
@@ -31,6 +32,18 @@
        set { 
            m_NormalTextColor = value;
        }
    }
    void OnEnable()
    {
        ExecuteNextFrame();
    }
    protected async void ExecuteNextFrame()
    {
        await UniTask.DelayFrame(1);
        UpdateAllButtonsState();
    }
    /// <summary>
@@ -70,6 +83,7 @@
        if (button == null)
            return;
        button.state = TitleBtnState.Click;
        // 取消其他按钮的选中状态
        foreach (var btn in m_Buttons)
        {
Main/Component/UI/Effect/UIEffectPlayer.cs
@@ -79,6 +79,11 @@
    protected override void PlaySpineEffect()
    {
        if (spineComp == null)
        {
            spineComp = gameObject.AddMissingComponent<SkeletonGraphic>();
        }
        if (spineComp.skeletonDataAsset == null)
        {
            //LoadAsset 已经有缓存SkeletonDataAsset
@@ -86,6 +91,7 @@
            spineComp.raycastTarget = false;
            spineComp.Initialize(true);
            spineAnimationState = spineComp.AnimationState;
            spineAnimationState.Data.DefaultMix = 0f;
            spineAnimationState.Complete -= OnSpineAnimationComplete;
            spineAnimationState.Complete += OnSpineAnimationComplete;
        }
@@ -103,7 +109,7 @@
        if (skeletonData.Animations.Count > 0)
        {
            string defaultAnimationName = skeletonData.Animations.Items[0].Name;
            spineComp.AnimationState.SetAnimation(0, defaultAnimationName, isPlaySpineLoop);
            spineAnimationState.SetAnimation(0, defaultAnimationName, isPlaySpineLoop);
        }
        else
        {
Main/Config/ConfigManager.cs
@@ -85,7 +85,8 @@
            typeof(TreasureItemLibConfig),
            typeof(TreasureSetConfig),
            typeof(TreeLVConfig),
            typeof(XBGetItemConfig)
            typeof(XBGetItemConfig),
            typeof(RichTextMsgReplaceConfig)
        };
#if UNITY_EDITOR
Main/Config/PartialConfigs/HeroFetterConfig.cs
@@ -1,18 +1,19 @@

using System.Collections.Generic;
using System.IO;
using System.Threading;
using System;
using UnityEngine;
using LitJson;
using System.Linq;
//羁绊:武将
public partial class HeroFetterConfig : ConfigBase<int, HeroFetterConfig>
{
    private Dictionary<HeroAttrType, int> attrValues = new Dictionary<HeroAttrType, int>();
    protected override void OnConfigParseCompleted()
    {
        base.OnConfigParseCompleted();
    {
        base.OnConfigParseCompleted();
        // public int[] AttrIDList;
        // public int[] AttrValueList;
@@ -34,7 +35,9 @@
            attrValues.Add((HeroAttrType)AttrIDList[i], AttrValueList[i]);
        }
    }
    }
    public int GetFetterAttr(HeroAttrType attrType)
    {
@@ -45,4 +48,6 @@
        return 0;
    }
}
Main/Config/PartialConfigs/HeroLineupHaloConfig.cs
@@ -4,7 +4,7 @@
public partial class HeroLineupHaloConfig : ConfigBase<int, HeroLineupHaloConfig>
{
    // 国家 数量
    public static Dictionary<int, Dictionary<int, HeroLineupHaloConfig>> configDics = new Dictionary<int, Dictionary<int, HeroLineupHaloConfig>>();
    private static Dictionary<int, Dictionary<int, HeroLineupHaloConfig>> configDics = new Dictionary<int, Dictionary<int, HeroLineupHaloConfig>>();
    protected override void OnConfigParseCompleted()
    {
@@ -32,4 +32,12 @@
        return configDics[country][count];
    }
    public static Dictionary<int, HeroLineupHaloConfig> GetAttrsByCountry(int country)
    {
        Dictionary<int, HeroLineupHaloConfig> attrs;
        configDics.TryGetValue(country, out attrs);
        return attrs;
    }
}
Main/Config/PartialConfigs/PlayerPropertyConfig.cs
@@ -76,7 +76,7 @@
        return GetFullDescription(property.x, property.y);
    }
    public static string GetFullDescription(int id, long value)
    public static string GetFullDescription(int id, long value, string format="{0}+{1}")
    {
        var config = Get(id);
        if (config == null)
@@ -86,18 +86,18 @@
        if (config.ShowName.Contains("%s"))
        {
            if (id == 52)
            {
                return Regex.Replace(config.ShowName, "%s", (value * 0.0001f).ToString("f2"));
            }
            else
            // if (id == 52)
            // {
            //     return Regex.Replace(config.ShowName, "%s", (value * 0.0001f).ToString("f2"));
            // }
            // else
            {
                return Regex.Replace(config.ShowName, "%s", value.ToString());
            }
        }
        else
        {
            return string.Format("{0} +{1}", config.ShowName, GetValueDescription(id, value));
            return string.Format(format, config.ShowName, GetValueDescription(id, value));
        }
    }
Main/Core/ResModule/GameObjectPoolManager.cs
@@ -2,6 +2,11 @@
using UnityEngine;
using System;
using Cysharp.Threading.Tasks;
using System.Linq;
#if UNITY_EDITOR
using UnityEngine.Profiling;
#endif
public class GameObjectPoolManager : SingletonMonobehaviour<GameObjectPoolManager>
@@ -10,6 +15,7 @@
    private bool m_ShowDebugPanel = false;
    private Vector2 m_ScrollPosition = Vector2.zero;
    private void OnGUI()
    {
        if (!m_ShowDebugPanel) return;
@@ -19,6 +25,7 @@
        m_ScrollPosition = GUILayout.BeginScrollView(m_ScrollPosition, GUILayout.Width(380), GUILayout.Height(580));
        GUILayout.Label("对象池调试面板", GUILayout.Height(30));
        LogPoolMemoryUsage();
        GUILayout.Space(10);
        foreach (var poolEntry in m_PoolDict)
@@ -49,6 +56,63 @@
            Instance.m_ShowDebugPanel = !Instance.m_ShowDebugPanel;
        }
    }
    public void LogPoolMemoryUsage()
    {
        long totalMemory = 0;
        long totalFreeMemory = 0;
        var prefabMemoryDict = new Dictionary<string, long>();
        foreach (var poolEntry in m_PoolDict)
        {
            int prefabInstanceId = poolEntry.Key;
            GameObjectPool pool = poolEntry.Value;
            string prefabName = pool.Prefab.name;
            long prefabMemory = 0;
            long freeMemory = 0;
            // 计算活跃对象的内存占用
            foreach (var gameObject in pool.m_ActiveHashSet)
            {
                if (gameObject != null)
                {
                    long memory = Profiler.GetRuntimeMemorySizeLong(gameObject);
                    prefabMemory += memory;
                }
            }
            // 计算空闲对象的内存占用
            foreach (var gameObject in pool.m_FreeQueue)
            {
                if (gameObject != null)
                {
                    long memory = Profiler.GetRuntimeMemorySizeLong(gameObject);
                    prefabMemory += memory;
                    freeMemory += memory;
                }
            }
            totalMemory += prefabMemory;
            totalFreeMemory += freeMemory;
            prefabMemoryDict[prefabName] = prefabMemory;
        }
        // 按内存占用排序
        var sortedPrefabs = prefabMemoryDict.OrderByDescending(kv => kv.Value).Take(3).ToList();
        GUILayout.Label($"总内存占用: {totalMemory / 1024} KB", GUILayout.Height(30));
        GUILayout.Label($"空闲内存占用: {totalFreeMemory / 1024} KB", GUILayout.Height(30));
        GUILayout.Label("占用最高的前3预制体名:", GUILayout.Height(30));
        foreach (var prefabInstance in sortedPrefabs)
        {
            GUILayout.BeginHorizontal("Box");
            GUILayout.Label($"{prefabInstance.Key}({prefabInstance.Value / 1024} KB)", GUILayout.Height(25));
            GUILayout.EndHorizontal();
        }
    }
#endif
    // 池统计数据
    public Dictionary<int, PoolStats> PoolStatistics { get; private set; } = new Dictionary<int, PoolStats>();
Main/System/Hero/HeroFetterInfo.cs
File was deleted
Main/System/Hero/HeroInfo.Fetter.cs
@@ -1,21 +1,44 @@
using System.Collections.Generic;
using System.Linq;
public partial class HeroInfo
{
    //  羁绊配置
    public HeroFetterConfig fetterConfig;
    public HeroFetterConfig fetterConfig;
    protected int GetIFByInheritFetterPercent(HeroAttrType attrType)
    public List<int> GetActiveFetter(HeroConfig config, TeamBase teamBase)
    {
        //    YYL TODO
        int total = 0;
        for (int i = 0; i < fetterInfoList.Count; i++)
        {
            HeroFetterInfo fetterInfo = fetterInfoList[i];
            total += fetterInfo.GetFetterAttr(attrType);
        }
        return total;
    }
        List<int> list = new List<int>();
        if (config.FetterIDList.Length == 0)
            return list;
        foreach (var fetterID in config.FetterIDList)
        {
            HeroFetterConfig fetterConfig = HeroFetterConfig.Get(fetterID);
            int count = 0;
            for (int i = 0; i < teamBase.tempHeroes.Length; i++)
            {
                TeamHero teamHero = teamBase.tempHeroes[i];
                if (null == teamHero)
                    continue;
                if (fetterConfig.HeroIDList.Contains(teamHero.heroId))
                {
                    count++;
                }
                if (count >= fetterConfig.HeroIDList.Length)
                {
                    list.Add(fetterID);
                    break;
                }
            }
        }
        return list;
    }
}
Main/System/Hero/HeroInfo.Properties.cs
@@ -115,7 +115,7 @@
        return GetStableProperties(attrType)
                    * GetCultivationPercent(attrType)
                    * GetInheritRate(attrType)
                    * GetInfluenceByInheritPercent(attrType);
                    * GetTotalPercent(attrType);
    }
@@ -139,15 +139,15 @@
    }
    //  被继承比例影响的百分比属性
    public int GetInfluenceByInheritPercent(HeroAttrType attrType)
    public int GetTotalPercent(HeroAttrType attrType)
    {
        // (1+羁绊加成%+潜能加成%+天赋加成%+觉醒效果加成%)
        int IFByInheritPercent = 100;
        IFByInheritPercent += GetIFByInheritFetterPercent(attrType); //羁绊加成 HeroInfo.Fetter
        IFByInheritPercent += GetIFByInheritBreakPercent(attrType);  //潜能加成 HeroInfo.Break
        IFByInheritPercent += GetIFByInheritTalentPercent(attrType); //天赋加成 HeroInfo.Talent
        IFByInheritPercent += GetIFByInheritAwakePercent(attrType); //觉醒加成 HeroInfo.Awake
        // IFByInheritPercent += GetIFByInheritFetterPercent(attrType); //羁绊加成 HeroInfo.Fetter
        // IFByInheritPercent += GetIFByInheritBreakPercent(attrType);  //潜能加成 HeroInfo.Break
        // IFByInheritPercent += GetIFByInheritTalentPercent(attrType); //天赋加成 HeroInfo.Talent
        // IFByInheritPercent += GetIFByInheritAwakePercent(attrType); //觉醒加成 HeroInfo.Awake
        return IFByInheritPercent;
    }
Main/System/Hero/HeroInfo.cs
@@ -41,7 +41,6 @@
    public List<HeroFetterInfo> fetterInfoList = new List<HeroFetterInfo>();
    public List<HeroTalentInfo> talentList = new List<HeroTalentInfo>();
Main/System/Hero/HeroManager.cs
@@ -40,7 +40,7 @@
    void OnBeforePlayerDataInitialize()
    {
        heroInfoDict.Clear();
    }
@@ -73,7 +73,7 @@
            heroInfoDict.TryGetValue(guid, out heroInfo);
            heroInfoDict.Remove(guid);
            if (null != heroInfo)
                onHeroDeleteEvent?.Invoke(heroInfo);
        }
@@ -95,12 +95,27 @@
    {
        if (job == 0 && country == 0)
            return heroInfoDict.Keys.ToList();
        return heroInfoDict.Keys.Where((x) =>
        List<string> retGuidList = new List<string>();
        foreach (string guid in heroInfoDict.Keys)
        {
            HeroInfo heroInfo = heroInfoDict[x];
            return heroInfo.heroConfig.Class == job && heroInfo.heroConfig.Country == country;
        }).ToList();
            HeroInfo heroInfo = heroInfoDict[guid];
            //0代表全部
            if (job == 0 || country == 0)
            {
                if (job != 0 && job == heroInfo.heroConfig.Class)
                    retGuidList.Add(guid);
                if (country != 0 && country == heroInfo.heroConfig.Country)
                    retGuidList.Add(guid);
            }
            else
            {
                if (job == heroInfo.heroConfig.Class && country == heroInfo.heroConfig.Country)
                    retGuidList.Add(guid);
            }
        }
        return retGuidList;
    }
    public List<HeroInfo> GetPowerfulHeroList()
@@ -131,7 +146,10 @@
    }
    public int GetHeroCount()
    {
        return heroInfoDict.Count;
    }
}
Main/System/Hero/UIHeroController.cs
@@ -48,6 +48,7 @@
        skeletonGraphic.Initialize(true);
        this.transform.localScale = Vector3.one * scale;
        spineAnimationState = skeletonGraphic.AnimationState;
        spineAnimationState.Data.DefaultMix = 0f;
        PlayAnimation(MotionName.idle, true);
        spineAnimationState.Complete -= OnAnimationComplete;
        spineAnimationState.Complete += OnAnimationComplete;
@@ -73,8 +74,8 @@
    {
        if (spineAnimationState == null) return;
        // 直接使用 ToString() 而不是调用 GetAnimationName
         spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
        // 直接使用 ToString() 而不是调用 GetAnimationName
        spineAnimationState.SetAnimation(0, motionName.ToString(), loop);
    }
    /// <summary>
Main/System/HeroUI/HeroConnectionCell.cs
@@ -7,9 +7,27 @@
    [SerializeField] HeroConnectionHeadCell[] heros;
    [SerializeField] Text connAttrText;
    public void Display()
    public void Display(int fetterID)
    {
        HeroFetterConfig heroFetterConfig = HeroFetterConfig.Get(fetterID);
        for (int i = 0; i < heros.Length; i++)
        {
            if (i < heroFetterConfig.HeroIDList.Length)
            {
                heros[i].SetActive(true);
                heros[i].Display(heroFetterConfig.HeroIDList[i], i);
            }
            else
            {
                heros[i].SetActive(false);
            }
        }
        string attrStr = "";
        for (int i = 0; i < heroFetterConfig.AttrIDList.Length; i++)
        {
            attrStr += PlayerPropertyConfig.GetFullDescription(heroFetterConfig.AttrIDList[i], heroFetterConfig.AttrValueList[i]) + " ";
        }
        connAttrText.text = Language.Get("L1100", heroFetterConfig.FetterName, UIHelper.AppendColor(TextColType.lightYellow, attrStr));
    }
}
Main/System/HeroUI/HeroConnectionHeadCell.cs
@@ -9,6 +9,12 @@
    [SerializeField] Text nameText;
    [SerializeField] Image connMarkImg; //链接的锁图标,第一个不显示
    /// <summary>
    ///
    /// </summary>
    /// <param name="heroID"></param>
    /// <param name="index"> 只是为了让第一个不显示锁图标用</param>
    /// <param name="guid">部分界面显示不同皮肤图标</param>
    public void Display(int heroID, int index, string guid = "")
    {
        int skinID = 0;
Main/System/HeroUI/HeroFormationCell.cs
New file
@@ -0,0 +1,71 @@
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
//阵型
public class HeroFormationCell : CellView
{
    [SerializeField] Image activeImg;
    [SerializeField] Image countryOnImg;    //上阵阵型激活国家
    [SerializeField] Image[] OnCountImgs;    //上阵数量激活
    [SerializeField] RichText attrText;
    public void Display(int index)
    {
        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType);
        var config = HeroLineupHaloConfig.GetConfig(result.x, result.y);
        bool sameCountry = result.x == (index + 1);
        activeImg.SetActive(config != null && sameCountry);
        countryOnImg.SetSprite("heroTeamCountry" + (index + 1));
        if (config == null || !sameCountry)
        {
            for (int i = 0; i < OnCountImgs.Length; i++)
            {
                OnCountImgs[i].SetActive(false);
            }
        }
        else
        {
            for (int i = 0; i < OnCountImgs.Length; i++)
            {
                if (i < result.y)
                {
                    OnCountImgs[i].SetActive(true);
                    OnCountImgs[i].SetSprite("heroTeamCountryPoint" + result.x);
                }
                else
                {
                    OnCountImgs[i].SetActive(false);
                }
            }
        }
        var attrDict = HeroLineupHaloConfig.GetAttrsByCountry(index + 1);
        var countList = attrDict.Keys.ToList();
        countList.Sort();
        string text = string.Empty;
        for (int k = 0; k < countList.Count; k++)
        {
            int count = countList[k];
            string lineText = string.Empty;
            bool isActive = sameCountry && count <= result.y;
            string countStr = isActive ? UIHelper.AppendColor(TextColType.Green, count.ToString()) : count.ToString();
            lineText = (k == 0 ? "" : "</r>") + Language.Get("herocard37", countStr, RichTextMsgReplaceConfig.GetRichReplace("Country", index + 1));
            var attrConfig = attrDict[count];
            for (int i = 0; i < attrConfig.AttrIDList.Length; i++)
            {
                string format = !isActive ? "{0}+{1}" : "{0}+" + UIHelper.AppendColor(TextColType.Green, "{1}");
                lineText += " " + PlayerPropertyConfig.GetFullDescription(attrConfig.AttrIDList[i], attrConfig.AttrValueList[i], format);
            }
            text += UIHelper.AppendColor(isActive ? TextColType.NavyBrown : TextColType.Gray, lineText);
        }
        attrText.text = text;
    }
}
Main/System/HeroUI/HeroFormationCell.cs.meta
File was renamed from Main/System/Hero/HeroFetterInfo.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 61d40aba9c2091445ae692419d4c5ebf
guid: bfc8cbe2e59ef934db88a564da0a7869
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/HeroUI/HeroFormationWin.cs
New file
@@ -0,0 +1,70 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
/// <summary>
/// 武将阵型激活界面
/// </summary>
public class HeroFormationWin : UIBase
{
    [SerializeField] Button closeBtn;
    [SerializeField] ScrollerController scroller;
    [SerializeField] Text totalAttrText;
    protected override void InitComponent()
    {
        closeBtn.AddListener(CloseWindow);
    }
    protected override void OnPreOpen()
    {
        scroller.OnRefreshCell += OnRefreshCell;
        CreateScroller();
        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType);
        var config = HeroLineupHaloConfig.GetConfig(result.x, result.y);
        if (config == null)
        {
            totalAttrText.text = "";
        }
        else
        {
            string lineText = string.Empty;
            for (int i = 0; i < config.AttrIDList.Length; i++)
            {
                string format = "{0}+" + UIHelper.AppendColor(TextColType.Green, "{1}");
                lineText += " " + PlayerPropertyConfig.GetFullDescription(config.AttrIDList[i], config.AttrValueList[i], format);
            }
            totalAttrText.text = Language.Get("herocard36") + lineText.Trim();
        }
    }
    protected override void OnPreClose()
    {
        scroller.OnRefreshCell -= OnRefreshCell;
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as HeroFormationCell;
        _cell.Display(cell.index);
    }
    void CreateScroller()
    {
        scroller.Refresh();
        for (int i = 0; i < 4; i++)
        {
            scroller.AddCell(ScrollerDataType.Header, i);
        }
        scroller.Restart();
    }
}
Main/System/HeroUI/HeroFormationWin.cs.meta
copy from Main/System/Hero/HeroFetterInfo.cs.meta copy to Main/System/HeroUI/HeroFormationWin.cs.meta
File was copied from Main/System/Hero/HeroFetterInfo.cs.meta
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 61d40aba9c2091445ae692419d4c5ebf
guid: 93fbb2862890fb440bc497898f9935ef
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
Main/System/HeroUI/HeroPosWin.cs
@@ -3,14 +3,19 @@
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
using Cysharp.Threading.Tasks;
using System.Threading;
using System;
/// <summary>
/// 武将布阵: 如果可以同时打开多个布阵界面 则使用 functionOrder:布阵类型
/// 武将布阵界面
/// </summary>
public class HeroPosWin : UIBase
{
    [SerializeField] Text[] attrOnList; //上阵属性加成
    [SerializeField] Button countryOnBtn;
    [SerializeField] Image countryOnImg;    //上阵阵型激活国家
    [SerializeField] UIEffectPlayer countryEffect;
    [SerializeField] List<Image> OnCountImgs;    //上阵数量激活
    [SerializeField] List<Image> scenePosImgs;  //场景布阵位置
    [SerializeField] HeroScenePosCell[] sceneHero;
@@ -22,7 +27,7 @@
    [SerializeField] ScrollerController heroListScroller;
    [SerializeField] Transform heroListEmpty;
    [SerializeField] Toggle showConnTipToggleBtn;
    private bool isToggleOn = false;
    [SerializeField] HeroSelectBehaviour fiterManager;  //武将筛选
    [SerializeField] Button oneKeyOnBtn;     //一键上阵
@@ -38,43 +43,75 @@
    [SerializeField] CanvasGroup flyAlphaTween;
    Sequence sequence;
    CancellationTokenSource _cts;
    Queue<int> showConnectTipQueue = new Queue<int>();
    private bool m_IsToggleOn = false;
    private bool isToggleOn
    {
        get { return m_IsToggleOn; }
        set
        {
            if (m_IsToggleOn != value)
            {
                m_IsToggleOn = value;
                LocalSave.SetBool("ShowConn" + PlayerDatas.Instance.baseData.PlayerID, value);
            }
        }
    }
    protected override void InitComponent()
    {
        attackTeamBtn.AddListener(() =>
        {
            if (HeroUIManager.Instance.selectTeamType == TeamType.Arena)
            {
                return;
            }
            HeroUIManager.Instance.selectTeamType = (TeamType)HeroUIManager.Instance.GetSelectTeamTypeByAttackType(0);
            Refresh();
            SelectTeamFunc((TeamType)HeroUIManager.Instance.GetSelectTeamTypeByAttackType(0));
        });
        defendTeamBtn.AddListener(() =>
        {
            if (HeroUIManager.Instance.selectTeamType == TeamType.ArenaDefense)
            {
                return;
            }
            HeroUIManager.Instance.selectTeamType = (TeamType)HeroUIManager.Instance.GetSelectTeamTypeByAttackType(1);
            Refresh();
            SelectTeamFunc((TeamType)HeroUIManager.Instance.GetSelectTeamTypeByAttackType(1));
        });
        mainFBBtn.AddListener(() =>
        {
            SelectTeamFunc(TeamType.Story);
        });
        jjcBtn.AddListener(() =>
        {
            SelectTeamFunc(TeamType.Arena);
        });
        tttBtn.AddListener(() =>
        {
            SelectTeamFunc(TeamType.Tower);
        });
        showConnTipToggleBtn.AddListener((value) =>
        {
            isToggleOn = showConnTipToggleBtn.isOn;
            if (isToggleOn == false)
            {
                CancelCurrentTask();
            }
        });
        oneKeyOnBtn.AddListener(OneKeyOnPos);
        saveBtn.AddListener(SaveTeam);
        backBtn.AddListener(CloseWindow);
        countryOnBtn.AddListener(() =>
        {
            UIManager.Instance.OpenWindow<HeroFormationWin>();
        });
        m_IsToggleOn = LocalSave.GetBool("ShowConn" + PlayerDatas.Instance.baseData.PlayerID, false);
    }
    protected override void OnPreOpen()
    {
        HeroUIManager.Instance.SortHeroOnTeamList();    //打开界面和保存(服务端)更新阵容是刷新
        HeroUIManager.Instance.selectTeamPosJob = 0;
        HeroUIManager.Instance.selectTeamPosCountry = 0;
        HeroUIManager.Instance.SortHeroOnTeamList();
        heroListScroller.OnRefreshCell += OnRefreshCell;
        HeroUIManager.Instance.OnTeamPosChangeEvent += TeamChangeEvent;
        CreateScroller();
@@ -83,7 +120,10 @@
    protected override void OnPreClose()
    {
        CancelCurrentTask();
        heroListScroller.OnRefreshCell -= OnRefreshCell;
        HeroUIManager.Instance.OnTeamPosChangeEvent -= TeamChangeEvent;
        TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType).RestoreTeam();
    }
@@ -94,8 +134,9 @@
        RefreshOnTeamBtn();
        RefreshTeamHero();
        RefreshFlyHead();
        RefreshConn();
        if (HeroUIManager.Instance.heroOnTeamSortList.Count == 0)
        if (HeroManager.Instance.GetHeroCount() == 0)
        {
            heroListEmpty.SetActive(true);
            heroListScroller.SetActive(false);
@@ -108,10 +149,12 @@
        showConnTipToggleBtn.isOn = isToggleOn;
        fiterManager.Display(0, 0, 0, SelectJobCountry);
        fiterManager.Display(0, HeroUIManager.Instance.selectTeamPosJob, HeroUIManager.Instance.selectTeamPosCountry, SelectJobCountry);
        fightPowerText.text = "1234k";
    }
    void RefreshFlyHead()
@@ -123,11 +166,21 @@
        flyHead.transform.localScale = Vector3.zero;
    }
    void RefreshConn()
    {
        connetionForm.SetActive(false);
        var canvasConn = connetionForm.GetComponent<Canvas>();
        canvasConn.sortingLayerID = canvas.sortingLayerID;
        canvasConn.sortingOrder = canvas.sortingOrder + 9;
        CancelCurrentTask();
    }
    void SelectJobCountry(int job, int country)
    {
        HeroUIManager.Instance.selectTeamPosJob = job;
        HeroUIManager.Instance.selectTeamPosCountry = country;
        HeroUIManager.Instance.SortHeroOnTeamList();
        CreateScroller();
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
@@ -176,14 +229,14 @@
    }
    //上阵武将国家光环激活
    void RefreshOnTeamCountry()
    void RefreshOnTeamCountry(bool playEffect = false)
    {
        Int2 result = HeroUIManager.Instance.GetMaxCountHeroCountry(HeroUIManager.Instance.selectTeamType);
        var config = HeroLineupHaloConfig.GetConfig(result.x, result.y);
        if (config == null)
        {
            countryOnImg.SetSprite("heroTeamCountry" + result.x);
            countryOnImg.SetSprite("heroTeamCountry0");
            for (int i = 0; i < OnCountImgs.Count; i++)
            {
                OnCountImgs[i].SetActive(false);
@@ -204,8 +257,9 @@
                    OnCountImgs[i].SetActive(false);
                }
            }
            if (playEffect)
                countryEffect.Play();
        }
    }
@@ -258,7 +312,7 @@
    }
    void RefreshPosScale()
    {
    {
        for (int i = 0; i < scenePosImgs.Count; i++)
        {
            scenePosImgs[i].transform.localScale = Vector3.one;
@@ -292,6 +346,7 @@
        }
        RefreshPosScale();
        heroListScroller.m_Scorller.RefreshActiveCellViews();
        RefreshOnTeamCountry(true);
        //表现飞入,连续点击不同头像触发的话则重置
        if (flyFrom > -1)
@@ -317,7 +372,72 @@
                .Join(flyHead.transform.DOScale(new Vector3(0.5f, 0.5f, 0.5f), HeroUIManager.clickFlyPosTime).SetEase(Ease.OutQuad))
                .Join(flyAlphaTween.DOFade(0f, HeroUIManager.clickFlyPosTime).SetEase(Ease.OutQuad));
            sequence.onComplete = () => { flyHead.transform.localScale = Vector3.zero; };
            if (isToggleOn)
            {
                ShowFetter(flyHero);
            }
        }
    }
    void ShowFetter(HeroInfo hero)
    {
        var heroConfig = hero.heroConfig;
        var fetterList = hero.GetActiveFetter(heroConfig, TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType));
        for (int i = 0; i < fetterList.Count; i++)
        {
            if(!showConnectTipQueue.Contains(fetterList[i]))
                showConnectTipQueue.Enqueue(fetterList[i]);
        }
        if (_cts == null)
        {
            _cts = new CancellationTokenSource();
            RunTaskAsync(_cts.Token).Forget(); // Forget() 表示不等待此任务完成
        }
    }
    async UniTask RunTaskAsync(CancellationToken token)
    {
        try
        {
            while (showConnectTipQueue.Count > 0)
            {
                await UniTask.Delay(300, cancellationToken: token);
                showConnectTipQueue.TryDequeue(out int fetterID);
                if (fetterID == 0)
                {
                    continue;
                }
                connetionForm.SetActive(true);
                connetionForm.Display(fetterID);
                //显示1.5秒后关闭
                await UniTask.Delay(1500, cancellationToken: token);
                connetionForm.SetActive(false);
            }
        }
        catch (OperationCanceledException)
        {
            Debug.Log("羁绊 协程被取消");
            connetionForm.SetActive(false);
        }
        finally
        {
            _cts?.Dispose();     // 释放资源
            _cts = null;         // 置空避免重复取消
            showConnectTipQueue.Clear();
        }
    }
    // 取消当前正在运行的任务
    public void CancelCurrentTask()
    {
        _cts?.Cancel();      // 触发取消
        _cts?.Dispose();     // 释放资源
        _cts = null;         // 置空避免重复取消
        showConnectTipQueue.Clear();
    }
    void OneKeyOnPos()
@@ -339,4 +459,19 @@
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        team.SaveTeam();
    }
    void SelectTeamFunc(TeamType type)
    {
        if (HeroUIManager.Instance.selectTeamType == type)
        {
            return;
        }
        HeroUIManager.Instance.selectTeamPosJob = 0;
        HeroUIManager.Instance.selectTeamPosCountry = 0;
        HeroUIManager.Instance.SortHeroOnTeamList();
        HeroUIManager.Instance.selectTeamType = type;
        Refresh();
        heroListScroller.m_Scorller.RefreshActiveCellViews();
    }
}
Main/System/HeroUI/HeroScenePosCell.cs
@@ -39,7 +39,7 @@
            heroConfig.AtkDistType == 2 && TeamConst.TeamPos1Array.Contains(index))
        {
            suggestForm.SetActive(true);
            jobTip.text = Language.Get("heroClass" + heroConfig.Class);
            jobTip.text = RichTextMsgReplaceConfig.GetRichReplace("Class", heroConfig.Class);
            posTip.text = Language.Get("heroAtkDistType" + heroConfig.AtkDistType);
        }
        else
Main/System/HeroUI/HeroSelectBehaviour.cs
@@ -12,16 +12,16 @@
    [SerializeField] Button unFoldBtn;  //展开按钮
    [SerializeField] GroupButtonEx[] jobsBtn;
    [SerializeField] GroupButtonEx[] countrysBtn;
    [SerializeField] GroupButtonExManager jobManager;
    [SerializeField] GroupButtonExManager countryManager;
    [SerializeField] GroupButtonExManager jobManager;
    [SerializeField] GroupButtonExManager countryManager;
    int m_Job = 0;
    int m_Country = 0;
    int foldState = 0;  //0 收起,1 展开
    //点击按钮需通知响应外部事件
    public Action<int, int> selectAction;
    private Action<int, int> selectAction;
@@ -92,5 +92,19 @@
        unFoldForm.SetActive(foldState == 1);
        foldForm.SetActive(foldState == 0);
    }
    private void LateUpdate()
    {
        if (foldState == 0)
            return;
        if (Input.GetMouseButtonDown(0))
        {
            if (!RectTransformUtility.RectangleContainsScreenPoint(this.transform as RectTransform, Input.mousePosition, CameraManager.uiCamera))
            {
                foldBtn.onClick.Invoke();
            }
        }
    }
}
Main/System/HeroUI/HeroUIManager.cs
@@ -93,7 +93,24 @@
    #region 布阵界面
    public List<string> heroOnTeamSortList { get; private set; } = new List<string>();    //不同上阵的列表排序
    public TeamType selectTeamType = TeamType.Story;  //当前选中的是哪个阵容, 布阵相关逻辑使用
    private TeamType m_SelectTeamType = TeamType.Story; //当前选中的是哪个阵容, 布阵相关逻辑使用
    public TeamType selectTeamType
    {
        get { return m_SelectTeamType; }
        set
        {
            if (m_SelectTeamType == value)
                return;
            //上一个阵容需要恢复到原状态
            if (m_SelectTeamType != TeamType.None)
            {
                TeamManager.Instance.GetTeam(m_SelectTeamType).RestoreTeam();
            }
            m_SelectTeamType = value;
        }
    }
    public int selectTeamPosJob = 0;    //布阵界面 筛选职业
    public int selectTeamPosCountry = 0;    //布阵界面 筛选国家
@@ -160,23 +177,21 @@
        var team = TeamManager.Instance.GetTeam(teamType);
        if (team != null)
        {
            for (int i = 0; i < team.serverHeroes.Length; i++)
            for (int i = 0; i < team.tempHeroes.Length; i++)
            {
                if (team.serverHeroes[i] == null)
                if (team.tempHeroes[i] == null)
                    continue;
                var hero = HeroManager.Instance.GetHero(team.serverHeroes[i].guid);
                if (hero != null)
                {
                    if (!heroCountryCount.ContainsKey(hero.heroCountry))
                    {
                        heroCountryCount.Add(hero.heroCountry, 1);
                    }
                    else
                    {
                        heroCountryCount[hero.heroCountry] += 1;
                    }
                var country = (HeroCountry)team.tempHeroes[i].heroConfig.Country;
                if (!heroCountryCount.ContainsKey(country))
                {
                    heroCountryCount.Add(country, 1);
                }
                else
                {
                    heroCountryCount[country] = heroCountryCount[country] + 1;
                }
            }
        }
@@ -226,7 +241,7 @@
        //推荐阵容的算法逻辑
        //自动选择优先级:武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        var tmpList = HeroManager.Instance.GetHeroGuidList();
        tmpList.Sort(CmpHeroByTeamType);
        tmpList.Sort(CmpHeroRecommend);
        //推荐最多6个,存在相同heroid,则跳过
Main/System/Main/HomeWin.cs
@@ -25,6 +25,8 @@
    //关卡
    [SerializeField] Button bossBtn;
    [SerializeField] Button changeHeroPosBtn;
    /// <summary>
    /// 初始化组件
    /// </summary>
@@ -34,6 +36,12 @@
        bossBtn.AddListener(() =>
        {
            UIManager.Instance.OpenWindow<MainBossEnterWin>();
        });
        changeHeroPosBtn.AddListener(() =>
        {
            HeroUIManager.Instance.selectTeamType = TeamType.Story;
            UIManager.Instance.OpenWindow<HeroPosWin>();
        });
    }
@@ -52,9 +60,6 @@
        TaskManager.Instance.OnTaskUpdate += UpdateTask;
        Refresh();
        UIManager.Instance.OpenWindow<BattleWin>();
        taskEffect.effectId = Random.Range(1007, 1008);
        taskEffect.Play();
    }
    protected override void OnOpen()
    {
Main/System/Message/ColorAnalysis.cs
@@ -9,25 +9,26 @@
    public override string Analysis(string val, bool IsRich)
    {
        if (!Color_Start_Regex.IsMatch(val) || RichTextMgr.Inst.presentRichText == null)
        {
            return val;
        }
        int index = 0;
        m_StringBuilder.Length = 0;
        var _text = RichTextMgr.Inst.presentRichText;
        if (_text.colorType == RichText.ColorType.Bright)
        {
            return val;
        }
        foreach (Match match in Color_Start_Regex.Matches(val))
        {
            m_StringBuilder.Append(val.Substring(index, match.Groups[1].Index - index));
            m_StringBuilder.Append(GetColorMap(match.Groups[1].Value));
            index = match.Groups[1].Index + match.Groups[1].Length;
        }
        m_StringBuilder.Append(val.Substring(index, val.Length - index));
        return m_StringBuilder.ToString();
        return val;
        // if (!Color_Start_Regex.IsMatch(val) || RichTextMgr.Inst.presentRichText == null)
        // {
        //     return val;
        // }
        // int index = 0;
        // m_StringBuilder.Length = 0;
        // var _text = RichTextMgr.Inst.presentRichText;
        // if (_text.colorType == RichText.ColorType.Bright)
        // {
        //     return val;
        // }
        // foreach (Match match in Color_Start_Regex.Matches(val))
        // {
        //     m_StringBuilder.Append(val.Substring(index, match.Groups[1].Index - index));
        //     m_StringBuilder.Append(GetColorMap(match.Groups[1].Value));
        //     index = match.Groups[1].Index + match.Groups[1].Length;
        // }
        // m_StringBuilder.Append(val.Substring(index, val.Length - index));
        // return m_StringBuilder.ToString();
    }
    private string GetColorMap(string _value)
@@ -36,39 +37,39 @@
        {
            _value = _value.Substring(0, 6);
        }
        switch (_value.ToLower())
        {
            case "109d06":
                return "35e122";
            case "ff6701":
                return "f8983b";
            case "006be3":
                return "31cefb";
            case "ff0303":
                return "fa0101";
            case "12a199":
                return "13a199";
            case "686868":
                return "f7f7f7";
            case "da48d5":
                return "ec4bf6";
            case "f6408d":
                return "ff7c7c";
            case "bb8800":
                return "ffde00";
            case "666666":
                return "dddddd";
            case "9460ff":
                return "7999ff";
            case "0066ff":
                return "00c6ff";
            case "00b337":
                return "66ff00";
            case "ff6600":
                return "ff9000";
            case "ff00f6":
                return "f000ff";
        }
        // switch (_value.ToLower())
        // {
        //     case "109d06":
        //         return "35e122";
        //     case "ff6701":
        //         return "f8983b";
        //     case "006be3":
        //         return "31cefb";
        //     case "ff0303":
        //         return "fa0101";
        //     case "12a199":
        //         return "13a199";
        //     case "686868":
        //         return "f7f7f7";
        //     case "da48d5":
        //         return "ec4bf6";
        //     case "f6408d":
        //         return "ff7c7c";
        //     case "bb8800":
        //         return "ffde00";
        //     case "666666":
        //         return "dddddd";
        //     case "9460ff":
        //         return "7999ff";
        //     case "0066ff":
        //         return "00c6ff";
        //     case "00b337":
        //         return "66ff00";
        //     case "ff6600":
        //         return "ff9000";
        //     case "ff00f6":
        //         return "f000ff";
        // }
        return _value;
    }
Main/System/Team/TeamBase.cs
@@ -252,6 +252,16 @@
        tempHeroes[posNum] = hero;
    }
    //  布阵接口: 恢复阵容
    public void RestoreTeam()
    {
        for (int i = 0; i < tempHeroes.Length; i++)
        {
            tempHeroes[i] = serverHeroes[i];
        }
    }
    public void AddHero(HeroInfo heroInfo, int targetPosition)
    {
        if (targetPosition < 0 || targetPosition >= tempHeroes.Length)
Main/Utility/EnumHelper.cs
@@ -1084,7 +1084,7 @@
    White = 1,
    titleSelectColor = 2,
    titleUnSelectColor = 3,
    lightYellow = 4,    //浅黄色
    Red = 5,
    Pink = 6,
    /// <summary>
Main/Utility/UIHelper.cs
@@ -515,10 +515,10 @@
    public static readonly Color s_NavyBrown = new Color32(110, 76, 49, 255);//6e4c31
    public static readonly Color s_Black = new Color32(0, 0, 0, 255);
    public static readonly Color s_NavyYellow = new Color32(255, 239, 71, 255);
    public static readonly Color s_NavyYellow = new Color32(242, 238, 2, 255); //f2ee02
    public static readonly Color s_LightGreen = new Color32(42, 227, 55, 255);//2ae337
    public static readonly Color s_LightWhite = new Color32(245, 246, 230, 255); //f5f6e6
    public static readonly Color s_Gray = new Color32(187, 187, 187, 255);  //bbbbbb
    public static readonly Color s_Gray = new Color32(132, 121, 123, 255);  //84797b
    public static readonly Color s_Gold = new Color32(255, 239, 71, 255);//ffef47
    public static readonly Color s_EarthYellow = new Color32(248, 152, 59, 255);//f8983b
@@ -645,9 +645,9 @@
            case TextColType.White:
                return bright ? s_BrightWhiteColor : s_DarkWhiteColor;  // s_BrightWhiteColor 是亮底灰色
            case TextColType.titleSelectColor:
                return new Color32(114, 157, 228, 255);
            case TextColType.titleUnSelectColor:
                return new Color32(127, 65, 57, 255);
            case TextColType.titleUnSelectColor:
                return new Color32(110, 92, 96, 255);
            case TextColType.Red:
                return bright ? s_BrightRedColor : s_DarkRedColor;
            case TextColType.Pink:
@@ -660,6 +660,8 @@
                return s_BrightGreenColor;
            case TextColType.Black:
                return s_Black;
            case TextColType.lightYellow:   //浅黄色
                return new Color32(252, 237, 185, 255);
            case TextColType.NavyYellow:
                return s_NavyYellow;
            case TextColType.LightGreen:
@@ -771,12 +773,12 @@
    public static string AppendColor(TextColType type, string msg, bool bright = false)
    {
        if (m_TextColorRegex.IsMatch(msg) && msg.ToLower().StartsWith("<color=#")
            && msg.ToLower().EndsWith("</color>"))
        {
            Match match = m_TextColorRegex.Match(msg);
            msg = match.Groups[1].Value;
        }
        // if (m_TextColorRegex.IsMatch(msg) && msg.ToLower().StartsWith("<color=#")
        //     && msg.ToLower().EndsWith("</color>"))
        // {
        //     Match match = m_TextColorRegex.Match(msg);
        //     msg = match.Groups[1].Value;
        // }
        switch (type)
        {
            case TextColType.None:
@@ -803,7 +805,11 @@
            case TextColType.LightGreen:
                return StringUtility.Contact("<color=#", "8ddc11", ">", msg, "</color>");
            case TextColType.Gray:
                return StringUtility.Contact("<color=#", "686868", ">", msg, "</color>");
                return StringUtility.Contact("<color=#", "84797b", ">", msg, "</color>");
            case TextColType.lightYellow:
                return StringUtility.Contact("<color=#", "fcedb9", ">", msg, "</color>");
            case TextColType.NavyYellow:
                return StringUtility.Contact("<color=#", "f2ee02", ">", msg, "</color>");
        }
        return msg;
    }