hch
2025-07-30 bae41593e19d32046f77ed1f036089e015380b99
117 【武将】武将系统 - 布阵临时版
18个文件已修改
832 ■■■■ 已修改文件
Main/Component/UI/Common/DragContainer.cs 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Common/DragItem.cs 124 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/ConfigParse.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/GameEngine/Common/Equation.cs 157 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/HeroManager.cs 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Hero/UIHeroController.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroHeadBaseCell.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroPosHeadCell.cs 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroPosWin.cs 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroScenePosCell.cs 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroSelectBehaviour.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/HeroUI/HeroUIManager.cs 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamBase.cs 68 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamConst.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamHero.cs 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamManager.cs 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Team/TeamType.cs 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Utility/ComponentExtersion.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Component/UI/Common/DragContainer.cs
@@ -6,11 +6,12 @@
using System.Collections;
    public class DragContainer:MonoBehaviour {
public class DragContainer : MonoBehaviour
{
    public int pos;
    }
}
Main/Component/UI/Common/DragItem.cs
@@ -1,4 +1,4 @@
//--------------------------------------------------------
//--------------------------------------------------------
//    [Author]:           玩个游戏
//    [  Date ]:           Monday, July 31, 2017
//--------------------------------------------------------
@@ -6,53 +6,103 @@
using System.Collections;
using UnityEngine.EventSystems;
using System;
using UnityEngine.Events;
    public class DragItem:MonoBehaviour,ICanvasRaycastFilter,IBeginDragHandler,IDragHandler,IEndDragHandler {
/// <summary>
/// 拖拽组件,实现拖拽功能并限制拖拽范围
/// </summary>
public class DragItem:MonoBehaviour,ICanvasRaycastFilter,IBeginDragHandler,IDragHandler,IEndDragHandler
{
    // 拖拽区域限制
    [SerializeField]
    RectTransform m_Area;
    public RectTransform area { get { return m_Area; } }
        [SerializeField]
        RectTransform m_Area;
        public RectTransform area { get { return m_Area; } }
    // 是否允许射线检测
    [SerializeField]
    bool m_RaycastTarget = true;
    public bool raycastTarget {
        get { return m_RaycastTarget; }
        private set { m_RaycastTarget = value; }
    }
        [SerializeField]
        bool m_RaycastTarget = true;
        public bool raycastTarget {
            get { return m_RaycastTarget; }
            private set { m_RaycastTarget = value; }
        }
    [SerializeField] public int pos;
        RectTransform m_RectTransform;
        public RectTransform rectTransform { get { return this.transform as RectTransform; } }
    private Vector3 orgScale;
        public void OnBeginDrag(PointerEventData eventData) {
            raycastTarget = false;
            this.transform.position = UIUtility.ClampWorldPosition(area,eventData);
        }
    // 当前对象的RectTransform组件
    public RectTransform rectTransform { get { return this.transform as RectTransform; } }
        public void OnDrag(PointerEventData eventData) {
            this.transform.position = UIUtility.ClampWorldPosition(area,eventData);
        }
    /// <summary>
    /// 开始拖拽时调用
    /// </summary>
    public void OnBeginDrag(PointerEventData eventData)
    {
        raycastTarget = false;
        this.transform.position = UIUtility.ClampWorldPosition(area, eventData);
        orgScale = rectTransform.localScale;
        rectTransform.localScale = orgScale * 1.2f;
    }
        public void OnEndDrag(PointerEventData eventData) {
            raycastTarget = true;
            if(eventData.pointerEnter == null) {
                return;
    /// <summary>
    /// 拖拽过程中调用
    /// </summary>
    public void OnDrag(PointerEventData eventData) {
        this.transform.position = UIUtility.ClampWorldPosition(area,eventData);
        // 检测是否进入目标区域
        if (eventData.pointerEnter != null) {
            var target = eventData.pointerEnter.transform;
            if (target != null) {
                // 进入目标区域,放大 1.2 倍
                target.localScale = Vector3.one * 1.2f;
            }
            var container = eventData.pointerEnter.GetComponent<DragContainer>();
            if(container == null) {
                return;
        } else {
            // 离开目标区域,恢复大小
            if (eventData.pointerPress != null) {
                var target = eventData.pointerPress.transform;
                if (target != null) {
                    target.localScale = Vector3.one;
                }
            }
            var contain = UIUtility.RectTransformContain(eventData.pointerEnter.transform as RectTransform,rectTransform);
            if(contain) {
                rectTransform.position = eventData.pointerEnter.transform.position;
            }
        }
        public bool IsRaycastLocationValid(Vector2 sp,Camera eventCamera) {
            return raycastTarget;
        }
    }
    /// <summary>
    /// 结束拖拽时调用
    /// </summary>
    public Action<int, int> onEndDragEvent;
    public void OnEndDrag(PointerEventData eventData) {
        raycastTarget = true;
        rectTransform.localScale = orgScale;
        if (eventData.pointerEnter == null)
        {
            onEndDragEvent?.Invoke(pos, -1);
            return;
        }
        var container = eventData.pointerEnter.GetComponent<DragContainer>();
        if(container == null) {
            onEndDragEvent?.Invoke(pos, -1);
            return;
        }
        // var contain = UIUtility.RectTransformContain(eventData.pointerEnter.transform as RectTransform,rectTransform);
        // if(contain) {
        //     rectTransform.position = eventData.pointerEnter.transform.position;
        // }
        onEndDragEvent?.Invoke(pos, container.pos);
    }
    /// <summary>
    /// 判断射线检测是否有效
    /// </summary>
    public bool IsRaycastLocationValid(Vector2 sp,Camera eventCamera) {
        return raycastTarget;
    }
}
Main/Config/ConfigParse.cs
@@ -187,31 +187,53 @@
        }
        s = s.Replace(" ", string.Empty);
        if (!userDataRegex.IsMatch(s))
        {
        var dict = JsonMapper.ToObject<Dictionary<string, string[]>>(s);
        if (dict == null || dict.Count == 0)
            return null;
        }
        else
        Dictionary<int, List<int>> result = new Dictionary<int, List<int>>();
        foreach (var item in dict)
        {
            Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();
            foreach (Match match in userDataRegex.Matches(s))
            List<int> list = new List<int>();
            for (int i = 0; i < item.Value.Length; i++)
            {
                int id = int.Parse(match.Groups[1].Value);
                string str = match.Groups[2].Value;
                string[] vals = str.Split(',');
                List<int> list = new List<int>();
                for (int i = 0; i < vals.Length; i++)
                {
                    int intval = int.Parse(vals[i].Replace('\'', ' '));
                    list.Add(intval);
                }
                if (!dics.ContainsKey(id))
                {
                    dics.Add(id, list);
                }
                list.Add(int.Parse(item.Value[i]));
            }
            return dics;
            if (list.Count != 0)
                result[int.Parse(item.Key)] = list;
        }
        return result;
        //if (!userDataRegex.IsMatch(s))
        //{
        //    return null;
        //}
        //else
        //{
        //    Dictionary<int, List<int>> dics = new Dictionary<int, List<int>>();
        //    foreach (Match match in userDataRegex.Matches(s))
        //    {
        //        int id = int.Parse(match.Groups[1].Value);
        //        string str = match.Groups[2].Value;
        //        string[] vals = str.Split(',');
        //        List<int> list = new List<int>();
        //        for (int i = 0; i < vals.Length; i++)
        //        {
        //            int intval = int.Parse(vals[i].Replace('\'', ' '));
        //            list.Add(intval);
        //        }
        //        if (!dics.ContainsKey(id))
        //        {
        //            dics.Add(id, list);
        //        }
        //    }
        //    return dics;
        //}
    }
    public static Dictionary<int, List<int>> ParseJsonDict(string jsonStr)
Main/Core/GameEngine/Common/Equation.cs
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
@@ -6,15 +6,30 @@
using UnityEngine;
 
/// <summary>
/// 公式计算模块,支持动态变量替换和多种运算符计算。
/// </summary>
public class Equation : Singleton<Equation>
{
    /// <summary>
    /// 支持的运算符列表。
    /// </summary>
    public static readonly List<char> operatorList = new List<char>() { '*', '-', '+', '/', '^', '!', '@', '%', ';', '#', '$', '~', '&' };
    /// <summary>
    /// 用于临时字符串构建的共享实例。
    /// </summary>
    public static StringBuilder textBuilder = new StringBuilder();
    /// <summary>
    /// 存储变量名和值的键值对列表。
    /// </summary>
    private List<KeyValuePair<string, string>> keyValueDic = new List<KeyValuePair<string, string>>();
    public readonly Regex replaecRegex = new Regex(@"[a-zA-Z]{2,50}");
    /// <summary>
    /// 用于匹配变量名的正则表达式。
    /// </summary>
    public static readonly Regex replaecRegex = new Regex(@"[a-zA-Z]{2,50}");
    public T Eval<T>(string equation) where T : struct
    {
@@ -34,6 +49,11 @@
        return val;
    }
    /// <summary>
    /// 替换公式中的变量名为对应的值。
    /// </summary>
    /// <param name="equation">原始公式字符串。</param>
    /// <returns>替换后的公式字符串。</returns>
    private string GetEquation(string equation)
    {
        try
@@ -41,35 +61,30 @@
            MatchCollection matchArray = replaecRegex.Matches(equation);
            textBuilder.Length = 0;
            int length = 0;
            if (matchArray != null)
            {
                for (int i = 0; i < matchArray.Count; i++)
                foreach (Match match in matchArray)
                {
                    Match match = matchArray[i];
                    textBuilder.Append(equation.Substring(length, match.Index - length));
                    // 添加非变量部分
                    textBuilder.Append(equation, length, match.Index - length);
                    length = match.Index + match.Length;
                    bool _replace = false;
                    for (int j = 0; j < keyValueDic.Count; j++)
                    {
                        if (match.Value.Equals(keyValueDic[j].Key))
                        {
                            textBuilder.Append(keyValueDic[j].Value);
                            _replace = true;
                            break;
                        }
                    }
                    if (!_replace)
                    {
                        textBuilder.Append(0);
                    }
                    // 查找变量对应的值
                    var foundPair = keyValueDic.Find(x => x.Key.Equals(match.Value));
                    string replacement = foundPair.Equals(default(KeyValuePair<string, string>)) ? "0" : foundPair.Value;
                    textBuilder.Append(replacement ?? "0");
                }
            }
            textBuilder.Append(equation.Substring(length, equation.Length - length));
            // 添加剩余部分
            textBuilder.Append(equation, length, equation.Length - length);
        }
        catch (Exception e)
        {
            Debug.Log(e.Message);
            Debug.LogError($"公式变量替换失败: {e.Message}");
        }
        return textBuilder.ToString();
    }
@@ -129,6 +144,9 @@
            }
            return false;
        }
        /// <summary>
        /// 解析公式中的子表达式(括号内的部分)。
        /// </summary>
        private void Subsection()
        {
            try
@@ -137,39 +155,37 @@
                int startIndex = 0;
                int length = 0;
                int index = 0;
                for (int i = 0; i < equation.Length; i++)
                {
                    if (equation[i] == '(')
                    {
                        i = i + 1;
                        i++;
                        startIndex = i;
                        // 提取括号内的子表达式
                        while (i < equation.Length && (equation[i] != ')' || bracketCnt > 0))
                        {
                            if (equation[i] == '(')
                            {
                                bracketCnt++;
                            }
                            else if (equation[i] == ')')
                            {
                                bracketCnt--;
                            }
                            if (equation[i] == '(') bracketCnt++;
                            else if (equation[i] == ')') bracketCnt--;
                            length++;
                            i = i + 1;
                            i++;
                        }
                        // 处理子表达式
                        subEquations.Add(new EquationParse(equation.Substring(startIndex, length)));
                        length = 0;
                        equationBuilder.Append('=');
                        equationBuilder.Append(index);
                        equationBuilder.Append('=');
                        equationBuilder.Append($"={index}=");
                        index++;
                        length = 0;
                        continue;
                    }
                    equationBuilder.Append(equation[i]);
                }
            }
            catch (Exception e)
            {
                Debug.Log(e.Message);
                Debug.LogError($"子表达式解析失败: {e.Message}");
            }
        }
@@ -270,57 +286,38 @@
            }
        }
        /// <summary>
        /// 根据运算符类型计算结果。
        /// </summary>
        /// <param name="leftValue">左操作数。</param>
        /// <param name="rightValue">右操作数。</param>
        /// <param name="operatorType">运算符类型。</param>
        /// <returns>计算结果。</returns>
        public static double GetResult(string leftValue, string rightValue, OperatorType operatorType)
        {
            double _leftValue = 0, _rightValue = 0;
            try
            if (!double.TryParse(leftValue, out double _leftValue) || !double.TryParse(rightValue, out double _rightValue))
            {
                double.TryParse(leftValue, out _leftValue);
                double.TryParse(rightValue, out _rightValue);
                Debug.LogError($"无法将字符串转换为数值: {leftValue} 或 {rightValue}");
                return 0;
            }
            catch (Exception)
            {
                Debug.LogErrorFormat("字符串不能转为数值{0}   {1}", leftValue, rightValue);
            }
            switch (operatorType)
            {
                case OperatorType.Plus:
                    return _leftValue + _rightValue;
                case OperatorType.Subtract:
                    return _leftValue - _rightValue;
                case OperatorType.Ride:
                    return _leftValue * _rightValue;
                case OperatorType.Divide:
                    if (_rightValue == 0)
                    {
                        _rightValue = 1;
                    }
                    return _leftValue / _rightValue;
                case OperatorType.Pow:
                    return Math.Pow(_leftValue, _rightValue);
                case OperatorType.Min:
                    return Math.Min(_leftValue, _rightValue);
                case OperatorType.Max:
                    return Math.Max(_leftValue, _rightValue);
                case OperatorType.Remain:
                    return _leftValue % _rightValue;
                case OperatorType.Random:
                    {
                        return UnityEngine.Random.Range((float)_leftValue, (float)_rightValue);
                    }
                case OperatorType.Floor:
                    return Math.Floor(_leftValue);
                case OperatorType.Ceil:
                    return Math.Ceiling((float)_leftValue);
                case OperatorType.RandomInt:
                    {
                        return UnityEngine.Random.Range((int)_leftValue, (int)_rightValue);
                    }
                case OperatorType.Sqrt:
                    return Math.Sqrt(_leftValue);
                case OperatorType.Plus:      return _leftValue + _rightValue;
                case OperatorType.Subtract:  return _leftValue - _rightValue;
                case OperatorType.Ride:      return _leftValue * _rightValue;
                case OperatorType.Divide:    return _rightValue == 0 ? _leftValue : _leftValue / _rightValue;
                case OperatorType.Pow:       return Math.Pow(_leftValue, _rightValue);
                case OperatorType.Min:       return Math.Min(_leftValue, _rightValue);
                case OperatorType.Max:       return Math.Max(_leftValue, _rightValue);
                case OperatorType.Remain:    return _leftValue % _rightValue;
                case OperatorType.Random:    return UnityEngine.Random.Range((float)_leftValue, (float)_rightValue);
                case OperatorType.Floor:     return Math.Floor(_leftValue);
                case OperatorType.Ceil:      return Math.Ceiling((float)_leftValue);
                case OperatorType.RandomInt: return UnityEngine.Random.Range((int)_leftValue, (int)_rightValue);
                case OperatorType.Sqrt:      return Math.Sqrt(_leftValue);
                default:                     return 0;
            }
            return 0;
        }
        public static OperatorType GetOperatorType(char _operator)
        {
Main/System/Hero/HeroManager.cs
@@ -90,10 +90,17 @@
    {
        return heroInfoDict.Values.ToList();
    }
    public List<string> GetHeroGuidList()
    public List<string> GetHeroGuidList(int job = 0, int country = 0)
    {
        return heroInfoDict.Keys.ToList();
        if (job == 0 && country == 0)
            return heroInfoDict.Keys.ToList();
        return heroInfoDict.Keys.Where((x) =>
        {
            HeroInfo heroInfo = heroInfoDict[x];
            return heroInfo.heroConfig.Class == job && heroInfo.heroConfig.Country == country;
        }).ToList();
    }
    public List<HeroInfo> GetPowerfulHeroList()
Main/System/Hero/UIHeroController.cs
@@ -35,7 +35,7 @@
            instanceGO = pool.Request();
            instanceGO.transform.SetParent(transform);
            //transform 的Pivot Y是0,让instanceGO 居中
            instanceGO.transform.localPosition = new Vector3(0, transform.GetComponent<RectTransform>().sizeDelta.y * 0.5f);
            instanceGO.transform.localPosition = new Vector3(0, instanceGO.GetComponent<RectTransform>().sizeDelta.y * 0.5f);
            //instanceGO.transform.localPosition = Vector3.zero;
            instanceGO.transform.localScale = Vector3.one;
Main/System/HeroUI/HeroHeadBaseCell.cs
@@ -195,7 +195,7 @@
        }
        countryImg.SetSprite("herocountry" + heroConfig.Country);
        lvText.text = lv.ToString();
        lvText.text = lv == 0 ? "": Language.Get("L1094") + lv;
        awakeLvRect.SetActive(awakelv > 0);
        awakeLvText.text = awakelv.ToString();
Main/System/HeroUI/HeroPosHeadCell.cs
@@ -1,4 +1,5 @@
using UnityEngine;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class HeroPosHeadCell : MonoBehaviour
@@ -10,7 +11,43 @@
    public void Display(int index)
    {
        var guid = HeroUIManager.Instance.heroOnTeamSortList[index];
        var hero = HeroManager.Instance.GetHero(guid);
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        selectRect.SetActive(team.GetHero(guid) != null);
        heroHeadBaseCell.Init(hero.heroId, hero.SkinID, hero.heroStar, hero.awakeLevel, hero.heroLevel, () =>
        {
            Click(hero, index);
        });
        nameText.text = hero.breakLevel == 0 ? hero.heroConfig.Name : Language.Get("herocardbreaklv", hero.heroConfig.Name, hero.breakLevel);
        jobImg.SetSprite("herojob" + hero.heroConfig.Class);
    }
    void Click(HeroInfo hero, int index)
    {
        //检查是否可上阵,查找上阵位置,显示勾选,飞入上阵位置
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        int pos;
        if (selectRect.gameObject.activeSelf)
        {
            selectRect.SetActive(false);
            if (team.RemoveHero(hero, out pos))
            {
                //通知刷新(下阵)
                HeroUIManager.Instance.NotifyOnTeamPosChangeEvent(new List<int>() { pos }, -1, Vector3.zero);
            }
            return;
        }
        team.AddHero(hero, out pos);
        if (pos != -1)
        {
            selectRect.SetActive(true);
            //通知刷新,上阵,图片飞入(上阵位置)
            HeroUIManager.Instance.NotifyOnTeamPosChangeEvent(new List<int>() { pos }, index, heroHeadBaseCell.transform.position);
        }
    }
}
Main/System/HeroUI/HeroPosWin.cs
@@ -2,9 +2,10 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using DG.Tweening;
/// <summary>
/// 武将布阵: functionOrder:布阵类型
/// 武将布阵: 如果可以同时打开多个布阵界面 则使用 functionOrder:布阵类型
/// </summary>
public class HeroPosWin : UIBase
{
@@ -21,6 +22,7 @@
    [SerializeField] ScrollerController heroListScroller;
    [SerializeField] Transform heroListEmpty;
    [SerializeField] Toggle showConnTipToggleBtn;
    private bool isToggleOn = false;
    [SerializeField] HeroSelectBehaviour fiterManager;  //武将筛选
    [SerializeField] Button oneKeyOnBtn;     //一键上阵
@@ -32,7 +34,10 @@
    //羁绊
    [SerializeField] HeroConnectionCell connetionForm;
    [SerializeField] HeroHeadBaseCell flyHead;
    [SerializeField] CanvasGroup flyAlphaTween;
    Sequence sequence;
    protected override void InitComponent()
    {
@@ -43,7 +48,7 @@
                return;
            }
            HeroUIManager.Instance.selectTeamType = TeamType.Arena;
            HeroUIManager.Instance.selectTeamType = (TeamType)HeroUIManager.Instance.GetSelectTeamTypeByAttackType(0);
            Refresh();
        });
        defendTeamBtn.AddListener(() =>
@@ -52,15 +57,26 @@
            {
                return;
            }
            HeroUIManager.Instance.selectTeamType = TeamType.ArenaDefense;
            HeroUIManager.Instance.selectTeamType = (TeamType)HeroUIManager.Instance.GetSelectTeamTypeByAttackType(1);
            Refresh();
        });
        showConnTipToggleBtn.AddListener((value) =>
        {
            isToggleOn = showConnTipToggleBtn.isOn;
        });
        oneKeyOnBtn.AddListener(OneKeyOnPos);
        saveBtn.AddListener(SaveTeam);
        backBtn.AddListener(CloseWindow);
    }
    protected override void OnPreOpen()
    {
        HeroUIManager.Instance.SortHeroOnTeamList();    //打开界面和保存(服务端)更新阵容是刷新
        heroListScroller.OnRefreshCell += OnRefreshCell;
        HeroUIManager.Instance.OnTeamPosChangeEvent += TeamChangeEvent;
        CreateScroller();
        Refresh();
    }
@@ -76,22 +92,47 @@
        OnBattleTeamAttrPer();
        RefreshOnTeamCountry();
        RefreshOnTeamBtn();
        RefreshTeamHero();
        if (HeroUIManager.Instance.heroOnTeamSortList.Count == 0)
        {
            heroListEmpty.SetActive(true);
            heroListScroller.SetActive(false);
        }
        else
        {
            heroListEmpty.SetActive(false);
            heroListScroller.SetActive(true);
        }
        showConnTipToggleBtn.isOn = isToggleOn;
        fiterManager.Display(0, 0, 0, SelectJobCountry);
        flyAlphaTween.alpha = 0;
        flyHead.transform.localScale = Vector3.zero;
        fightPowerText.text = "1234k";
    }
    void SelectJobCountry(int job, int country)
    {
        HeroUIManager.Instance.selectTeamPosJob = job;
        HeroUIManager.Instance.selectTeamPosCountry = country;
        HeroUIManager.Instance.SortHeroOnTeamList();
    }
    void OnRefreshCell(ScrollerDataType type, CellView cell)
    {
        var _cell = cell as HeroCardLineCell;
        var _cell = cell as HeroPosLineCell;
        _cell.Display(cell.index);
    }
    void CreateScroller()
    {
        heroListScroller.Refresh();
        for (int i = 0; i < HeroUIManager.Instance.heroSortList.Count; i++)
        for (int i = 0; i < HeroUIManager.Instance.heroOnTeamSortList.Count; i++)
        {
            if (i % 4 == 0)
            if (i % 5 == 0)
            {
                heroListScroller.AddCell(ScrollerDataType.Header, i);
            }
@@ -108,6 +149,8 @@
        {
            for (int i = 0; i < team.serverHeroes.Length; i++)
            {
                if (team.serverHeroes[i] == null)
                    continue;
                var hero = HeroManager.Instance.GetHero(team.serverHeroes[i].guid);
                if (hero != null)
                {
@@ -181,7 +224,102 @@
    }
    void RefreshTeamHero()
    {
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        for (int i = 0; i < sceneHero.Length; i++)
        {
            var teamHero = team.tempHeroes[i];
            if (teamHero == null)
            {
                sceneHero[i].SetActive(false);
            }
            else
            {
                sceneHero[i].SetActive(true);
                sceneHero[i].Display(teamHero.guid, i);
                //按scenePosImgs 顺序排序对应位置
                sceneHero[i].transform.position = scenePosImgs[i].transform.position;
            }
        }
    }
    /// <summary>
    /// 点击上阵,拖拉,一键等触发;
    /// </summary>
    /// <param name="posList">要刷新的布阵位置</param>
    /// <param name="flyFrom">大于-1代表是点击上阵,需要表现飞行效果</param>
    void TeamChangeEvent(List<int> posList, int flyFrom, Vector3 startPos)
    {
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        foreach (var i in posList)
        {
            var teamHero = team.tempHeroes[i];
            if (teamHero == null)
            {
                sceneHero[i].SetActive(false);
            }
            else
            {
                sceneHero[i].SetActive(true);
                sceneHero[i].Display(teamHero.guid, i, flyFrom >= 0);
                //按scenePosImgs 顺序排序对应位置
                sceneHero[i].transform.position = scenePosImgs[i].transform.position;
            }
        }
        //表现飞入,连续点击不同头像触发的话则重置
        if (flyFrom > -1)
        {
            var flyHero = HeroManager.Instance.GetHero(HeroUIManager.Instance.heroOnTeamSortList[flyFrom]);
            if (flyHero == null)
                return;
            //先停止 sequence
            if (sequence != null)
            {
                sequence.Kill();
            }
            sequence = DOTween.Sequence();
            flyAlphaTween.alpha = 1;
            flyHead.Init(flyHero.heroId, flyHero.SkinID);
            flyHead.transform.position = startPos;
            var targetPos = scenePosImgs[posList[0]].transform.position;
            flyHead.transform.localScale = Vector3.one;
            sequence.Append(flyHead.transform.DOMove(targetPos, HeroUIManager.clickFlyPosTime).SetEase(Ease.OutQuad))
                .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));
        }
    }
    void OneKeyOnPos()
    {
        var guidList = HeroUIManager.Instance.SelectRecommend();
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        team.RemoveAllHeroes();
        for (int i = 0; i < guidList.Count; i++)
        {
            team.AddHero(HeroManager.Instance.GetHero(guidList[i]), i);
        }
        List<int> posList = new List<int>();
        for (int i = 0; i < guidList.Count; i++)
        {
            posList.Add(i);
        }
        TeamChangeEvent(posList, -1, Vector3.zero);
        heroListScroller.m_Scorller.RefreshActiveCellViews();
    }
    void SaveTeam()
    {
        var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
        team.SaveTeam();
    }
}
Main/System/HeroUI/HeroScenePosCell.cs
@@ -1,5 +1,9 @@
using System;
using System.Linq;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
//布阵中的 武将角色
public class HeroScenePosCell : MonoBehaviour
@@ -12,19 +16,69 @@
    [SerializeField] Text lvText;
    [SerializeField] UIHeroController heroModel;
    [SerializeField] Image posCircleImg;
    [SerializeField] UIAlphaTween suggestForm;
    [SerializeField] DragItem dragObj;
    [SerializeField] Transform objForfly;  //点击飞入的时候的显隐控制
    public void Display(string guid)
    public void Display(string guid, int index, bool isFly = false)
    {
        var hero = HeroManager.Instance.GetHero(guid);
        this.transform.localScale = Vector3.one;
        lvText.text = Language.Get("L1094") + hero.heroLevel.ToString();
        lvText.text = Language.Get("L1099", hero.heroLevel);
        var heroConfig = hero.heroConfig;
        countryImg.SetSprite("herocountry" + heroConfig.Country);
        heroModel.Create(heroConfig.SkinIDList[hero.SkinIndex], heroConfig.UIScale);
        heroModel.Create(heroConfig.SkinIDList[hero.SkinIndex]);
        nameText.text = hero.breakLevel == 0 ? heroConfig.Name : Language.Get("herocardbreaklv", heroConfig.Name, hero.breakLevel);
        posCircleImg.SetSprite("heroposcircle" + heroConfig.Quality);
        //不是推荐位则提示
        if (heroConfig.AtkDistType == 1 && TeamConst.TeamPos2Array.Contains(index) ||
            heroConfig.AtkDistType == 2 && TeamConst.TeamPos1Array.Contains(index))
        {
            suggestForm.SetActive(true);
            jobTip.text = Language.Get("heroClass" + heroConfig.Class);
            posTip.text = Language.Get("heroAtkDistType" + heroConfig.AtkDistType);
        }
        else
        {
            suggestForm.SetActive(false);
        }
        dragObj.onEndDragEvent += SwitchPos;
        heroBtn.AddListener(() =>
        {
            var team = TeamManager.Instance.GetTeam(HeroUIManager.Instance.selectTeamType);
            team.RemoveHero(index);
            //通知刷新(下阵)
            HeroUIManager.Instance.NotifyOnTeamPosChangeEvent(new List<int>() { index }, -1, Vector3.zero);
        });
        if (isFly)
        {
            //点击飞入 延迟显示
            objForfly.SetActive(false);
            DelayShow();
        }
        else
        {
            objForfly.SetActive(true);
        }
    }
    void SwitchPos(int pos1, int pos2)
    {
        Debug.Log("交换位置:" + pos1 + "   " + pos2);
    }
    async UniTask DelayShow()
    {
        //延迟0.5秒显示
        await UniTask.Delay(TimeSpan.FromSeconds(HeroUIManager.clickFlyPosTime));
        objForfly.SetActive(true);
    }
}
Main/System/HeroUI/HeroSelectBehaviour.cs
@@ -89,8 +89,8 @@
    //刷新展开收起状态
    void RefreshFolState()
    {
        unFoldBtn.SetActive(foldState == 1);
        foldBtn.SetActive(foldState == 0);
        unFoldForm.SetActive(foldState == 1);
        foldForm.SetActive(foldState == 0);
    }
}
Main/System/HeroUI/HeroUIManager.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
@@ -8,12 +9,11 @@
//武将相关界面的操作数据管理
public class HeroUIManager : GameSystemManager<HeroUIManager>
{
    //武将列表界面
    #region 武将列表界面
    public List<string> heroSortList { get; private set; } = new List<string>();  //上阵为主线的列表
    #endregion
    public List<string> heroOnTeamSortList { get; private set; } = new List<string>();    //不同上阵的列表排序
    public TeamType selectTeamType = TeamType.Story;  //当前选中的是哪个阵容, 布阵相关逻辑使用
    public void OnBeforePlayerDataInitialize()
    {
@@ -35,8 +35,12 @@
    }
    public bool waitResortHeroList = false;
    public void QueryUnLockHeroPack()
    {
        //解锁更多的武将背包
    }
    //刷新时机, 打开武将界面 或者 关闭功能界面
    public void SortHeroList()
@@ -86,10 +90,21 @@
        return heroA.heroId.CompareTo(heroB.heroId);
    }
    #region 布阵界面
    public List<string> heroOnTeamSortList { get; private set; } = new List<string>();    //不同上阵的列表排序
    public TeamType selectTeamType = TeamType.Story;  //当前选中的是哪个阵容, 布阵相关逻辑使用
    public int selectTeamPosJob = 0;    //布阵界面 筛选职业
    public int selectTeamPosCountry = 0;    //布阵界面 筛选国家
    public const float clickFlyPosTime = 0.5f;  //点击列表中的武将图标时, 飞入布阵的时间
    public event Action<List<int>, int, Vector3> OnTeamPosChangeEvent; //布阵变化 位置,列表的起飞索引,起飞坐标
    public void SortHeroOnTeamList()
    {
        heroOnTeamSortList = HeroManager.Instance.GetHeroGuidList();
        heroOnTeamSortList = HeroManager.Instance.GetHeroGuidList(selectTeamPosJob, selectTeamPosCountry);
        heroOnTeamSortList.Sort(CmpHeroByTeamType);
    }
@@ -136,10 +151,6 @@
    public void QueryUnLockHeroPack()
    {
        //解锁更多的武将背包
    }
    //上阵队伍中各个国家的武将数量
    public Dictionary<HeroCountry, int> GetCountryHeroCountByTeamType(TeamType teamType)
@@ -151,6 +162,8 @@
        {
            for (int i = 0; i < team.serverHeroes.Length; i++)
            {
                if (team.serverHeroes[i] == null)
                    continue;
                var hero = HeroManager.Instance.GetHero(team.serverHeroes[i].guid);
                if (hero != null)
                {
@@ -197,7 +210,81 @@
            return AttackType == 0 ? (int)TeamType.Arena : (int)TeamType.ArenaDefense;
        }
        return (int)TeamType.Story;
    }
    public void NotifyOnTeamPosChangeEvent(List<int> posList, int flyIndex, Vector3 startPos)
    {
        OnTeamPosChangeEvent?.Invoke(posList, flyIndex, startPos);
    }
    //推荐阵容
    public List<string> SelectRecommend()
    {
        //推荐阵容的算法逻辑
        //自动选择优先级:武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        var tmpList = HeroManager.Instance.GetHeroGuidList();
        tmpList.Sort(CmpHeroByTeamType);
        //推荐最多6个,存在相同heroid,则跳过
        List<string> selectHeroList = new List<string>();
        List<int> selectHeroIDList = new List<int>();
        for (int i = 0; i < tmpList.Count; i++)
        {
            if (selectHeroList.Count >= TeamConst.MaxTeamHeroCount)
                break;
            string guid = tmpList[i];
            HeroInfo heroInfo = HeroManager.Instance.GetHero(guid);
            if (selectHeroIDList.Contains(heroInfo.heroId))
                continue;
            //如果重复了,跳过
            if (selectHeroList.Contains(guid))
                continue;
            selectHeroList.Add(guid);
            selectHeroIDList.Add(heroInfo.heroId);
        }
        return selectHeroList;
    }
    int CmpHeroRecommend(string guidA, string guidB)
    {
        HeroInfo heroA = HeroManager.Instance.GetHero(guidA);
        HeroInfo heroB = HeroManager.Instance.GetHero(guidB);
        if (heroA == null || heroB == null)
        {
            return 0;
        }
        // 排序规则:武将等级>突破等级>武将觉醒阶级>武将品质>武将吞噬星级>武将ID
        if (heroA.heroLevel != heroB.heroLevel)
        {
            return heroA.heroLevel > heroB.heroLevel ? -1 : 1;
        }
        if (heroA.breakLevel != heroB.breakLevel)
        {
            return heroA.breakLevel > heroB.breakLevel ? -1 : 1;
        }
        if (heroA.awakeLevel != heroB.awakeLevel)
        {
            return heroA.awakeLevel > heroB.awakeLevel ? -1 : 1;
        }
        if (heroA.Quality != heroB.Quality)
        {
            return heroA.Quality > heroB.Quality ? -1 : 1;
        }
        if (heroA.heroStar != heroA.heroStar)
        {
            return heroA.heroStar > heroB.heroStar ? -1 : 1;
        }
        return heroA.heroId.CompareTo(heroB.heroId);
    }
    #endregion
}
Main/System/Team/TeamBase.cs
@@ -28,7 +28,7 @@
    private int ShapeType;
    public int ServerShapeType { get; private set; }
    public TeamHero[] tempHeroes = new TeamHero[TeamConst.MaxTeamHeroCount];
    public TeamHero[] tempHeroes { get; private set; }  = new TeamHero[TeamConst.MaxTeamHeroCount];
    public TeamHero[] serverHeroes { get; private set; } = new TeamHero[TeamConst.MaxTeamHeroCount];
@@ -99,7 +99,7 @@
    {
        if (IsEmpty())
        {
            Debug.LogError("Cannot save an empty team. You should at least have one hero in the team.");
            SysNotifyMgr.Instance.ShowTip("HeroFunc3");
            return;
        }
@@ -109,6 +109,7 @@
        savePack.PosCnt = (byte)GetTeamHeroCount();
        savePack.HeroPosList = new CB412_tagCSHeroLineupSave.tagCSHeroLineupPos[savePack.PosCnt];
        int index = 0;
        foreach (var hero in tempHeroes)
        {
            if (hero != null)
@@ -122,15 +123,17 @@
                    continue;
                }
                savePack.HeroPosList[posNum] = new CB412_tagCSHeroLineupSave.tagCSHeroLineupPos
                savePack.HeroPosList[index] = new CB412_tagCSHeroLineupSave.tagCSHeroLineupPos
                {
                    ItemIndex = (ushort)heroInfo.itemHero.gridIndex,
                    PosNum = (byte)posNum
                    PosNum = (byte)(posNum + 1)
                };
                index++;
            }
        }
        GameNetSystem.Instance.SendInfo(savePack);
        SysNotifyMgr.Instance.ShowTip("HeroFunc4");
        //非主线阵容客户端自己做战力变化,主线阵容服务端战力变更会同步推送
    }
    public void OnChangeShapeType(int newShapeType)
@@ -144,7 +147,7 @@
        ShapeType = newShapeType;
    }
    //  hero info could be null if the hero is removed from the team
    public void RefreshServerData(int shapeType, int positionIndex, HeroInfo heroInfo)
    {
        TeamHero teamHero = heroInfo == null ? null : new TeamHero(heroInfo, positionIndex, this);
@@ -185,6 +188,19 @@
        return null;
    }
    public TeamHero GetHeroByHeroID(int heroId)
    {
        foreach (var hero in tempHeroes)
        {
            if (hero != null && hero.heroId == heroId)
            {
                return hero;
            }
        }
        return null;
    }
    public TeamHero GetServerHero(string guid)
    {
        foreach (var hero in serverHeroes)
@@ -210,6 +226,7 @@
        return false;
    }
    //客户端从0开始,服务端从1开始
    public int GetEmptyPosition()
    {
        for (int i = 0; i < tempHeroes.Length; i++)
@@ -223,13 +240,13 @@
    }
    //  布阵接口
    public void SetTeamHero(int posNum, TeamHero hero)
    private void SetTeamHero(int posNum, TeamHero hero)
    {
        tempHeroes[posNum] = hero;
    }
    //  布阵接口
    public void SetServerTeamHero(int posNum, TeamHero hero)
    private void SetServerTeamHero(int posNum, TeamHero hero)
    {
        serverHeroes[posNum] = hero;
        tempHeroes[posNum] = hero;
@@ -259,41 +276,44 @@
    //  add只可能是点下面卡牌
    public bool AddHero(HeroInfo heroInfo)
    public bool AddHero(HeroInfo heroInfo, out int pos)
    {
        pos = -1;
        if (heroInfo == null || heroInfo.itemHero == null) return false;
        //  如果当前英雄已经在队伍里了 就不处理了
        if (GetHero(heroInfo.itemHero.guid) != null)
        // 同一英雄 只能上阵一个
        if (GetHeroByHeroID(heroInfo.heroId) != null)
        {
            return false; // Hero already in team
            SysNotifyMgr.Instance.ShowTip("HeroFunc2");
            return false;
        }
        else
        {
            int emptyPosition = GetEmptyPosition();
            pos = GetEmptyPosition();
            if (emptyPosition < 0)
            if (pos < 0)
            {
                Debug.LogError("No empty position available in the team.");
                SysNotifyMgr.Instance.ShowTip("HeroFunc1");
                return false; // No empty position available
            }
            TeamHero teamHero = new TeamHero(heroInfo, GetEmptyPosition(), this);
            TeamHero teamHero = new TeamHero(heroInfo, pos, this);
            SetTeamHero(teamHero.positionNum, teamHero);
            return true;
        }
    }
    public bool RemoveHero(HeroInfo heroInfo)
    public bool RemoveHero(HeroInfo heroInfo, out int pos)
    {
        pos = -1;
        if (heroInfo == null || heroInfo.itemHero == null) return false;
        TeamHero teamHero = GetHero(heroInfo.itemHero.guid);
        if (teamHero != null)
        {
            pos = teamHero.positionNum;
            //  从当前队伍里移除该英雄
            SetTeamHero(teamHero.positionNum, null);
            return true;
@@ -319,6 +339,18 @@
        return false; // Hero not found
    }
    public bool RemoveHero(int pos)
    {
        SetTeamHero(pos, null);
        return true;
    }
    public void RemoveAllHeroes()
    {
        tempHeroes = new TeamHero[TeamConst.MaxTeamHeroCount];
    }
    public void SwapPosition(int index1, int index2)
    {
        if (index1 < 0 || index1 >= tempHeroes.Length || index2 < 0 || index2 >= tempHeroes.Length)
Main/System/Team/TeamConst.cs
@@ -4,4 +4,9 @@
{
    public const int MaxTeamHeroCount = 6;//最大布阵数量
    public const int MaxTeamSlotCount = 7;//最大槽位数量 包含boss的中间位置 布阵的时候不能用这个
    public static readonly int[] TeamPos1Array  = new int[] {0, 1, 2}; //前排位
    public static readonly int[] TeamPos2Array = new int[] {3, 4, 5};  //后排位
}
Main/System/Team/TeamHero.cs
@@ -14,7 +14,7 @@
    {
        get; private set;
    }
    public int positionNum;
    public int positionNum; //注意服务端的1号位是1,客户端在使用时是0,通信和策划沟通用1
//  战场数据
    public int ObjID = 0;// 战斗单位唯一ID
@@ -41,7 +41,7 @@
        maxHp = (long)fightObj.MaxHPEx * GeneralDefine.HundredMillion + (long)fightObj.MaxHP;
        rage = (int)fightObj.AngreXP;
        positionNum = fightObj.PosNum;
        positionNum = fightObj.PosNum - 1;
        //  【重要】战斗构成里没有卡牌的guid
        guid = string.Empty;
@@ -76,12 +76,7 @@
    public void OnSwapPosition(TeamHero teamHero)
    {
        int tempPosNum = positionNum;
        positionNum = teamHero.positionNum;
        teamHero.positionNum = tempPosNum;
        teamBase.tempHeroes[positionNum] = this;
        teamBase.tempHeroes[teamHero.positionNum] = teamHero;
        teamBase.SwapPosition(positionNum, teamHero.positionNum);
        Update();
    }
Main/System/Team/TeamManager.cs
@@ -47,7 +47,7 @@
                // 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...] 
                int teamType = teamMsg / 10000;
                int shapeType = (teamMsg % 10000) / 100;
                int positionIndex = teamMsg % 100;
                int positionIndex = teamMsg % 100 - 1;    //布阵位置:服务端为 1  客户端为0
                if (teamTypeShapeTypePositionDict.ContainsKey((TeamType)teamType))
                {
@@ -86,7 +86,7 @@
                else
                {
                    //  队伍里有这个英雄,但是在队伍信息里没有了 置空 (被移出队伍)
                    team.SetTeamHero(teamHero.positionNum, null);
                    team.RemoveHero(teamHero.positionNum);
                }
            }
            //    原来队伍里没这个英雄
@@ -97,7 +97,7 @@
                if (teamTypeShapeTypePositionDict.ContainsKey(team.teamType))
                {
                    KeyValuePair<int, int> shapeTypePosition = teamTypeShapeTypePositionDict[team.teamType];
                    team.RefreshServerData(shapeTypePosition.Key, shapeTypePosition.Value, null);
                    team.RefreshServerData(shapeTypePosition.Key, shapeTypePosition.Value, heroInfo);
                }
            }
        }
@@ -128,7 +128,7 @@
            // 所在阵容信息列表 [阵容类型*10000+阵型类型*100+位置编号, ...] 
            int teamType = teamMsg / 10000;
            int shapeType = (teamMsg % 10000) / 100;
            int positionIndex = teamMsg % 100;
            int positionIndex = teamMsg % 100 - 1; //布阵位置:服务端为 1  客户端为0
            TeamBase team = GetTeam((TeamType)teamType);
@@ -152,7 +152,7 @@
        {
            team = new TeamBase(teamType);
            // team.CreateDefault(HeroManager.Instance.GetPowerfulHeroList());
            // teamDict.Add(teamType, team);
            teamDict.Add(teamType, team);
        }
        return team;
Main/System/Team/TeamType.cs
@@ -6,9 +6,9 @@
    //    PVE
    Story = 1,
    //    PVP 进攻
    Arena = 21,
    Arena = 2,
    //    PVP 防守
    ArenaDefense = 22,
    Tower = 3,
    ArenaDefense = 3,
    Tower = 4,
}
Main/Utility/ComponentExtersion.cs
@@ -120,6 +120,7 @@
        {
            return;
        }
        _toggle.onValueChanged.RemoveAllListeners();
        _toggle.onValueChanged.AddListener(_action);
    }