yyl
7 天以前 4e41975b0c62fa10ab571b0c9ad0690754762b6a
125 战斗 飘字更新
14个文件已修改
4个文件已添加
766 ■■■■ 已修改文件
Main/Config/PartialConfigs/SkillConfig.Partial.cs 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Core/NetworkPackage/CustomServerPack/CustomHB426CombinePack.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/BattleField.cs 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleField/RecordActions/BuffMountAction.cs 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleHUDWin.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObject.cs 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/BattleObject/BattleObjectFactory.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Buff/BattleObjectBuffMgr.cs 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Motion/MotionBase.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/Skill/SkillBase.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/BattleFloatingUIController.cs 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/BattleFloatingUIController.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/BattleHeroInfoBar.cs 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/BattleTips.cs 152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/DamageContent.cs 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/DamageLine.cs 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/IBattleFloatingUI.cs 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/System/Battle/UIComp/IBattleFloatingUI.cs.meta 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Main/Config/PartialConfigs/SkillConfig.Partial.cs
@@ -12,6 +12,13 @@
public partial class SkillConfig : ConfigBase<int, SkillConfig>
{
//增益: 3 5
// 减益: 4 6 14
    public static readonly int[] GainSkillType = new int[] { 3, 5 }; // 1-普通攻击 2-被动技能 3-专属技能
    public static readonly int[] DebuffSkillType = new int[] { 4, 6, 14 };
    public SkillType skillType;
    public SkillCastMode castMode;
@@ -45,6 +52,16 @@
        tempDic[SkillLV] = this;
    }
    public bool IsGainBuff()
    {
        return Array.Exists(GainSkillType, type => type == (int)skillType);
    }
    public bool IsDebuff()
    {
        return Array.Exists(DebuffSkillType, type => type == (int)skillType);
    }
    public MotionName GetMotionName()
    {
        return Enum.Parse<MotionName>(SkillMotionName);
Main/Core/NetworkPackage/CustomServerPack/CustomHB426CombinePack.cs
@@ -236,6 +236,10 @@
        {
            battleField.PlayRecord(skillAction);
        }
        else
        {
            battleField.DistributeNextPackage();
        }
    }
Main/System/Battle/BattleField/BattleField.cs
@@ -8,50 +8,35 @@
public class BattleField
{
    public Action<int, int> OnRoundChange;
    public Action<bool> OnBattlePause;
    public Action<float> OnSpeedRatioChange; // 添加战斗速度变化的回调
    public Action OnBattleRun; // 添加战斗运行时的回调
    public BattleObjMgr battleObjMgr;
    public BattleEffectMgr battleEffectMgr;
    public BattleTweenMgr battleTweenMgr;
    public RecordPlayer recordPlayer;
    public IOperationAgent operationAgent;
    public byte turnMax;
    public int round = 0;
    public string guid = string.Empty;//等于string.Empty的时候代表是StoryBattleField 是主线副本
    public string guid = string.Empty;
    public int MapID = 0;
    public int FuncLineID = 0;
    public float speedRatio = 1.1f;
    public JsonData extendData;
    public bool IsBattleFinish
    {
        get;
        protected set;
    }
    public bool IsBattleFinish { get; protected set; }
    public bool rejectNewPackage = false;
    private bool m_IsPause = false;
    public bool IsPause
    {
        get
        {
            return m_IsPause;
        }
        get { return m_IsPause; }
        set
        {
            if (value)
            {
                m_IsPause = value;
@@ -71,18 +56,12 @@
    }
    public BattleRootNode battleRootNode;
    private BattleMode battleMode;
    public event Action<BattleMode> ChangeBattleModeEvent;
    public Action<bool> OnBattlePause;
    protected List<TeamBase> redTeamList = null;
    protected List<TeamBase> blueTeamList = null;
    protected int redTeamIndex = 0;
    protected int blueTeamIndex = 0;
    public BattleField(string _guid)
@@ -161,11 +140,17 @@
    public void SetSpeedRatio(float ratio)
    {
        bool isChange = speedRatio != ratio;
        speedRatio = ratio;
        battleObjMgr.SetSpeedRatio(ratio);
        recordPlayer.SetSpeedRatio(ratio);
        battleEffectMgr.SetSpeedRatio(ratio);
        battleTweenMgr.SetSpeedRatio(ratio);
        if (isChange)
        {
            OnSpeedRatioChange?.Invoke(ratio);
        }
    }
    protected virtual void SetBattleStartState()
@@ -210,6 +195,7 @@
        }
        if (recordPlayer == null || battleObjMgr == null)
            return;
        recordPlayer.Run();
        battleObjMgr.Run();
        battleEffectMgr.Run();
@@ -221,6 +207,9 @@
        }
        operationAgent.Run();
        // 触发 UI 层的更新回调
        OnBattleRun?.Invoke();
    }
Main/System/Battle/BattleField/RecordActions/BuffMountAction.cs
@@ -35,7 +35,16 @@
                if (null != skillConfig && obj != null)
                {
                    //  飘字
                    obj.heroInfoBar.ShowTips(skillConfig.SkillName);
                    BattleHeroInfoBar.TipsInfo tipsInfo = new BattleHeroInfoBar.TipsInfo();
                    tipsInfo.message = skillConfig.SkillName;
                    tipsInfo.useArtText = false;
                    tipsInfo.followCharacter = true;
                    tipsInfo.scaleRatio = 1f;
                    tipsInfo.textColor = skillConfig.IsDebuff() ?
                                            Color.red : skillConfig.IsGainBuff() ? Color.green : Color.white;
                    tipsInfo.showBackground = true;
                    obj.heroInfoBar.ShowTips(tipsInfo);
                }
            }
Main/System/Battle/BattleHUDWin.cs
@@ -7,36 +7,23 @@
using System;
using LitJson;
//  这个界面是 persistent的界面
public class BattleHUDWin : UIBase
{
    // 组件引用
    // private List<HUDContent> damageList = new List<HUDContent>();
    // private List<BuffContent> buffList = new List<BuffContent>();
    private GameObjectPoolManager.GameObjectPool damagePrefabPool;
    private GameObjectPoolManager.GameObjectPool buffIconPrefabPool;
    private GameObjectPoolManager.GameObjectPool buffLabelPrefabPool;
    public Transform damageNode;
    public Transform buffIconNode;
    public Transform buffLabelNode;
    private BattleField battleField;
    private List<DamageContent> damageContentList = new List<DamageContent>();
    // 生命周期
    protected override void InitComponent()
    {
        base.InitComponent();
        // 初始化组件引用 绑定按钮等UI组件事件
    }
    protected override void OnPreOpen()
@@ -45,8 +32,6 @@
        EventBroadcast.Instance.AddListener<BattleDmgInfo>(EventName.BATTLE_DAMAGE_TAKEN, OnDamageTaken);
        EventBroadcast.Instance.AddListener<string, JsonData>(EventName.BATTLE_END, OnBattleEnd);
        damagePrefabPool = GameObjectPoolManager.Instance.RequestPool(UILoader.LoadPrefab("DamageContent"));
        // buffIconPrefabPool = GameObjectPoolManager.Instance.RequestPool();
        // buffLabelPrefabPool = GameObjectPoolManager.Instance.RequestPool(ResManager.Instance.LoadAsset<GameObject>("UIComp", "BuffContent"));
    }
    private void OnBattleEnd(string guid, JsonData data)
@@ -83,8 +68,13 @@
    protected override void OnClose()
    {
        base.OnClose();
        battleField.OnBattlePause -= OnBattlePause;
        battleField = null;
        if (battleField != null)
        {
            battleField.OnBattlePause -= OnBattlePause;
            battleField.OnBattleRun -= OnBattleRun;
            battleField.OnSpeedRatioChange -= OnSpeedRatioChange;
            battleField = null;
        }
    }
    protected override void NextFrameAfterOpen()
@@ -108,13 +98,13 @@
        GameObject damageContent = damagePrefabPool.Request();
        DamageContent content = damageContent.GetComponent<DamageContent>();
        damageContent.transform.SetParent(damageNode, false);
        damageContent.transform.localPosition = new Vector3(damageContent.transform.localPosition.x, damageContent.transform.localPosition.y, 0);
        content.SetDamage(damageInfo, () => RemoveDamageContent(content));
        damageContentList.Add(content);
        var heroRect = damageInfo.hurtObj.heroRectTrans;
        if (heroRect == null)
        {
            damagePrefabPool.Release(damageContent);
            return;
        }
        var contentRect = content.GetComponent<RectTransform>();
        var contentParentRect = contentRect.parent as RectTransform;
@@ -130,7 +120,19 @@
            null,
            out anchoredPos);
        contentRect.anchoredPosition = anchoredPos;
        // 设置初始位置和结束位置
        content.beginPos = anchoredPos;
        content.endPos = anchoredPos + new Vector2(0, 150);
        // 设置速度比例
        if (battleField != null)
        {
            content.SetRatio(battleField.speedRatio, 1f);
        }
        // 设置伤害数据并开始播放
        content.SetDamage(damageInfo, () => RemoveDamageContent(content));
        damageContentList.Add(content);
    }
    public void SetBattleField(BattleField _battleField)
@@ -138,15 +140,26 @@
        if (battleField != null)
        {
            battleField.OnBattlePause -= OnBattlePause;
            battleField.OnBattleRun -= OnBattleRun;
            battleField.OnSpeedRatioChange -= OnSpeedRatioChange;
        }
        ClearContent(string.Empty, true);
        battleField = _battleField;
        battleField.OnBattlePause += OnBattlePause;
        battleField.OnBattleRun += OnBattleRun;
        battleField.OnSpeedRatioChange += OnSpeedRatioChange;
    }
    private void OnSpeedRatioChange(float newSpeedRatio)
    {
        foreach (var content in damageContentList)
        {
            content.SetRatio(newSpeedRatio, 1f);
        }
    }
    private void OnBattlePause(bool isPause)
    {
        //  游戏暂停
        if (isPause)
        {
            foreach (var content in damageContentList)
@@ -154,7 +167,6 @@
                content.Stop();
            }
        }
        //  游戏恢复
        else
        {
            foreach (var content in damageContentList)
@@ -163,4 +175,15 @@
            }
        }
    }
    private void OnBattleRun()
    {
        for (int i = damageContentList.Count - 1; i >= 0; i--)
        {
            if (i < damageContentList.Count)
            {
                damageContentList[i].Run();
            }
        }
    }
}
Main/System/Battle/BattleObject/BattleObject.cs
@@ -504,6 +504,7 @@
    public void SetSpeedRatio(float ratio)
    {
        motionBase.SetSpeedRatio(ratio);
        heroInfoBar.SetSpeedRatio(ratio);
    }
Main/System/Battle/BattleObject/BattleObjectFactory.cs
@@ -11,7 +11,7 @@
    private static float m_modelScaleRate = 0f;
    private static float modelScaleRate
    public static float modelScaleRate
    {
        get
        {
Main/System/Battle/Buff/BattleObjectBuffMgr.cs
@@ -94,23 +94,24 @@
    //  删除buff
    public void RemoveBuff(HB429_tagSCBuffDel vNetData)
    {
        HB428_tagSCBuffRefresh buffData = null;
        bool isRemove = false;
        if (buffDataDict.TryGetValue(vNetData.BuffID, out buffData))
        {
            isRemove = true;
            buffDataDict.Remove(vNetData.BuffID);
        }
        if (!isRemove)
        {
            return;
        }
        var tempvNetData = vNetData;
        BuffUnmountAction buffRemoveAction = new BuffUnmountAction(battleObject.battleField, new List<HB429_tagSCBuffDel>() { vNetData }, () =>
        {
            HB428_tagSCBuffRefresh buffData = null;
            bool isRemove = false;
            if (buffDataDict.TryGetValue(tempvNetData.BuffID, out buffData))
            {
                isRemove = true;
                buffDataDict.Remove(tempvNetData.BuffID);
            }
            if (!isRemove)
            {
                return;
            }
            bool isRemoveEffect = false;
            int remainCnt = -1;
@@ -143,7 +144,7 @@
                if (buffGroupStateDict[BattleConst.HardControlGroup.ToString()].Contains(skillConfig.BuffState))
                {
                    BattleDebug.LogError("[BattleObjectBuffMgr]移除对象 " + battleObject.ObjID + " 的buff id " + vNetData.BuffID + " BuffState is " + skillConfig.BuffState + " 是否删除了字典内的内容 " + isRemove.ToString() + " 是否删除了特效 " + isRemoveEffect.ToString() + " pack uid 是 " + vNetData.packUID);
                    BattleDebug.LogError("[BattleObjectBuffMgr]移除对象 " + battleObject.ObjID + " 的buff id " + tempvNetData.BuffID + " BuffState is " + skillConfig.BuffState + " 是否删除了字典内的内容 " + isRemove.ToString() + " 是否删除了特效 " + isRemoveEffect.ToString() + " pack uid 是 " + vNetData.packUID);
                }
            }
@@ -173,21 +174,21 @@
            return;
        }
        // bool isNew = false;
        if (buffDataDict.ContainsKey(vNetData.BuffID))
        {
            buffDataDict[vNetData.BuffID] = vNetData;
        }
        else
        {
            // isNew = true;
            buffDataDict.Add(vNetData.BuffID, vNetData);
        }
        var tempvNetData = vNetData;
        if (vNetData.IsAdd != 0)
        if (tempvNetData.IsAdd != 0)
        {
            BuffMountAction buffMountAction = new BuffMountAction(battleObject.battleField, new List<HB428_tagSCBuffRefresh>() { vNetData }, () =>
            BuffMountAction buffMountAction = new BuffMountAction(battleObject.battleField, new List<HB428_tagSCBuffRefresh>() { tempvNetData }, () =>
            {
                if (buffDataDict.ContainsKey(tempvNetData.BuffID))
                {
                    buffDataDict[tempvNetData.BuffID] = tempvNetData;
                }
                else
                {
                    buffDataDict.Add(tempvNetData.BuffID, tempvNetData);
                }
                if (battleObject.IsDead())
                {
                    return;
@@ -198,7 +199,7 @@
                    //  已经存在相同的buff特效
                    if (buffEffectDict.TryGetValue(skillConfig.BuffEffect, out KeyValuePair<BattleEffectPlayer, HashSet<uint>> pair))
                    {
                        pair.Value.Add(vNetData.BuffID);
                        pair.Value.Add(tempvNetData.BuffID);
                    }
                    else
                    {
@@ -209,7 +210,7 @@
                        effect.BindBone(battleObject.motionBase.skeletonAnim, effect.effectConfig.nodeName);
                        HashSet<uint> buffIdSet = new HashSet<uint>();
                        buffIdSet.Add(vNetData.BuffID);
                        buffIdSet.Add(tempvNetData.BuffID);
                        buffEffectDict.Add(skillConfig.BuffEffect, new KeyValuePair<BattleEffectPlayer, HashSet<uint>>(effect, buffIdSet));
                    }
@@ -217,7 +218,7 @@
                if (skillConfig != null && buffGroupStateDict[BattleConst.HardControlGroup.ToString()].Contains(skillConfig.BuffState))
                {
                    BattleDebug.LogError("[BattleObjectBuffMgr]添加对象 " + battleObject.ObjID + " 的buff id " + vNetData.BuffID + " pack uid 是 " + vNetData.packUID + " BuffState is " + skillConfig.BuffState);
                    BattleDebug.LogError("[BattleObjectBuffMgr]添加对象 " + battleObject.ObjID + " 的buff id " + tempvNetData.BuffID + " pack uid 是 " + tempvNetData.packUID + " BuffState is " + skillConfig.BuffState);
                }
                OnBuffChanged();
@@ -233,6 +234,14 @@
        }
        else
        {
            if (buffDataDict.ContainsKey(tempvNetData.BuffID))
            {
                buffDataDict[tempvNetData.BuffID] = tempvNetData;
            }
            else
            {
                buffDataDict.Add(tempvNetData.BuffID, tempvNetData);
            }
            //  已经存在的buff 刷新
            OnBuffChanged();
        }
Main/System/Battle/Motion/MotionBase.cs
@@ -5,7 +5,7 @@
public class MotionBase
{
    public static float MotionTimeScale = 1f;
    public float MotionTimeScale = 1f;
    public static List<string> AttackMotionList = new List<string>
    {
        MotionName.attack.ToString().ToLower(),
Main/System/Battle/Skill/SkillBase.cs
@@ -150,7 +150,7 @@
            
            if (hintConfig != null)
            {
                caster.heroInfoBar.ShowTips(((char)hintConfig.prefix).ToString(), true);
                caster.heroInfoBar.ShowTips(((char)hintConfig.prefix).ToString(), true, false, 1.25f);
                // Debug.Break();
            }
        }
Main/System/Battle/UIComp/BattleFloatingUIController.cs
New file
@@ -0,0 +1,118 @@
using UnityEngine;
using System;
/// <summary>
/// 战斗飘字UI控制器:处理缩放、透明度、位置动画逻辑
/// </summary>
[SerializeField]
public class BattleFloatingUIController
{
    // Position Settings
    public Vector2 beginPos = Vector2.zero;
    public Vector2 endPos = new Vector2(0, 150);
    // Time Settings
    public float scaleChangeTime = 1f / BattleConst.skillMotionFps * 8f + 0.1f; // 7~8帧 (8/30=0.2667秒)
    public float totalShowTime = 1f / BattleConst.skillMotionFps * 24f + 0.1f; // 总时间约24帧 (8+16=24帧)
    // Normal Settings
    public Vector3 normalBeginScale = new Vector3(2f, 2f, 2f);
    public Vector3 normalEndScale = new Vector3(1f, 1f, 1f);
    // Critical Settings
    public Vector3 critBeginScale = new Vector3(3f, 3f, 3f);
    public Vector3 critEndScale = new Vector3(1.5f, 1.5f, 1.5f);
    // Color Settings
    public Color beginColor = new Color(1f, 1f, 1f, 0.5f);
    public Color endColor = new Color(1f, 1f, 1f, 1f);
    private RectTransform rectTransform;
    private float timer = 0f;
    private float speedRatio = 1f;
    private float scaleRatio = 1f;
    private bool isCritical = false;
    private Action onFinishCallback;
    private Action<Color> applyColorCallback;
    private GameObject gameObject;
    // 添加只读属性以对外暴露
    public float Timer => timer;
    public float SpeedRatio => speedRatio;
    public float ScaleRatio => scaleRatio;
    public BattleFloatingUIController(RectTransform rect, GameObject go, Action<Color> applyColor)
    {
        rectTransform = rect;
        gameObject = go;
        applyColorCallback = applyColor;
    }
    public void SetRatio(float speed, float scale)
    {
        speedRatio = speed;
        scaleRatio = scale;
    }
    public void Play(bool isCrit, Action onComplete = null)
    {
        isCritical = isCrit;
        onFinishCallback = onComplete;
        timer = 0f;
        Vector3 beginScale = isCritical ? critBeginScale : normalBeginScale;
        rectTransform.anchoredPosition = beginPos;
        rectTransform.localScale = beginScale * scaleRatio;
        gameObject.SetActive(true);
    }
    public void Run()
    {
        if (!gameObject.activeSelf)
            return;
        if (timer >= totalShowTime)
        {
            gameObject.SetActive(false);
            onFinishCallback?.Invoke();
            onFinishCallback = null;
            return;
        }
        // 整个过程都往上飘
        float moveProgress = timer / totalShowTime;
        rectTransform.anchoredPosition = Vector2.Lerp(beginPos, endPos, moveProgress);
        Vector3 currentBeginScale = isCritical ? critBeginScale : normalBeginScale;
        Vector3 currentEndScale = isCritical ? critEndScale : normalEndScale;
        // 阶段1: 7~8帧内缩放和透明度变化
        if (timer < scaleChangeTime)
        {
            float scaleProgress = timer / scaleChangeTime;
            rectTransform.localScale = Vector3.Lerp(currentBeginScale, currentEndScale, scaleProgress) * scaleRatio;
            Color currentColor = Color.Lerp(beginColor, endColor, scaleProgress);
            applyColorCallback?.Invoke(currentColor);
        }
        // 阶段2: 保持缩放和透明度,继续往上飘
        else
        {
            rectTransform.localScale = currentEndScale * scaleRatio;
            applyColorCallback?.Invoke(endColor);
        }
        timer += 1f / BattleConst.skillMotionFps * speedRatio;
    }
    public void Stop()
    {
        // 可以添加暂停逻辑
    }
    public void Resume()
    {
        // 可以添加恢复逻辑
    }
}
Main/System/Battle/UIComp/BattleFloatingUIController.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 60d705fe96fb5584683592ecfef1ba81
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant:
Main/System/Battle/UIComp/BattleHeroInfoBar.cs
@@ -12,6 +12,11 @@
        public string message;
        public bool useArtText;
        public bool followCharacter;
        public float scaleRatio;
        public Color textColor = Color.white;
        public bool showBackground = false;
    }
    protected BattleObject battleObject;
@@ -87,14 +92,20 @@
        }
        tipsList.Clear();
    }
    public void ShowTips(string message, bool useArtText = false, bool followCharacter = false)
    public void ShowTips(string message, bool useArtText = false, bool followCharacter = true, float scaleRatio = 1f)
    {
        messages.Add(new TipsInfo
        {
            message = message,
            useArtText = useArtText,
            followCharacter = followCharacter
            followCharacter = followCharacter,
            scaleRatio = scaleRatio
        });
    }
    public void ShowTips(TipsInfo tipsInfo)
    {
        messages.Add(tipsInfo);
    }
    public void SetActive(bool active)
@@ -102,28 +113,45 @@
        gameObject.SetActive(active);
    }
    public void PopUpTipsDirectly(string message, bool useArtText = false, bool followCharacter = false)
    public void PopUpTipsDirectly(TipsInfo tipsInfo)
    {
        GameObject prefab = textTips.gameObject;
        GameObject go = GameObject.Instantiate(prefab, followCharacter ? transform : battleObject.battleField.battleRootNode.transform);
        GameObject go = GameObject.Instantiate(prefab, tipsInfo.followCharacter ? transform : battleObject.battleField.battleRootNode.transform);
        BattleTips tips = go.GetComponent<BattleTips>();
        if (!followCharacter)
        if (!tipsInfo.followCharacter)
        {
            go.transform.position = prefab.transform.position;
            tips.beginPos = go.transform.localPosition;
            tips.endPos = tips.endPos + new Vector2(go.transform.localPosition.x, go.transform.localPosition.y);
            var contentRect = go.GetComponent<RectTransform>();
            var contentParentRect = contentRect.parent as RectTransform;
            var infoBarRect = GetComponent<RectTransform>();
            Vector3 worldTargetPos = infoBarRect.transform.TransformPoint(infoBarRect.rect.center);
            Vector2 anchoredPos;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(
                contentParentRect,
                RectTransformUtility.WorldToScreenPoint(null, worldTargetPos),
                null,
                out anchoredPos);
            tips.UpdatePositions(anchoredPos, anchoredPos + new Vector2(0, 150));
            // 同时更新缩放
            Vector3 newBeginScale = tips.normalBeginScale * tipsInfo.scaleRatio;
            Vector3 newEndScale = tips.normalEndScale * tipsInfo.scaleRatio;
            tips.UpdateScales(newBeginScale, newEndScale);
        }
        tips.SetSpeedRatio(battleObject.battleField.speedRatio);
        tips.SetRatio(battleObject.battleField.speedRatio, 1f);
        tips.SetText(message, useArtText);
        tips.SetText(tipsInfo.message, tipsInfo.useArtText, false, tipsInfo.textColor);
        tips.ShowBackground(tipsInfo.showBackground);
        tips.OnFinish = () =>
        {
            //  TODO YYL 考虑池化
            tipsList.Remove(tips);
            GameObject.DestroyImmediate(tips.gameObject);
        };
@@ -189,12 +217,20 @@
        if (messages.Count > 0 && timer >= PopUpInterval)
        {
            // 播放飘字
            TipsInfo message = messages[0];
            TipsInfo tipsInfo = messages[0];
            messages.RemoveAt(0);
            PopUpTipsDirectly(message.message, message.useArtText, message.followCharacter);
            PopUpTipsDirectly(tipsInfo);
            timer = 0f;
        }
    }
    public void SetSpeedRatio(float ratio)
    {
        for (int i = 0; i < tipsList.Count; i++)
        {
            tipsList[i].SetRatio(ratio, 1f);
        }
    }
}
Main/System/Battle/UIComp/BattleTips.cs
@@ -2,106 +2,136 @@
using System;
using UnityEngine.UI;
public class BattleTips : MonoBehaviour
public class BattleTips : MonoBehaviour, IBattleFloatingUI
{
    public Vector2 beginPos = Vector2.zero;
    public Vector2 endPos = new Vector2(0, 150);
    public float scaleChangeTime = 0.2667f; // 7~8帧 (8/30=0.2667秒)
    public float totalShowTime = 0.8f; // 总时间约24帧 (8+16=24帧)
    public float timer = 0f;
    public Vector3 beginScale = new Vector3(4f, 4f, 4f);
    public Vector3 endScale = new Vector3(2f, 2f, 2f);
    public Color beginColor = new Color(1f, 1f, 1f, 0.5f);
    public Color endColor = new Color(1f, 1f, 1f, 1f);
    public RectTransform rectTransform;
    public Text tipText;
    public Text artText;
    public Action OnFinish;
    public Image background;
    private float speedRatio = 1f;
    public Vector3 normalBeginScale = new Vector3(2f, 2f, 2f);
    public Vector3 normalEndScale = new Vector3(1f, 1f, 1f);
    public void SetSpeedRatio(float ratio)
    public Action OnFinish; // 保留 OnFinish
    [SerializeField]
    private BattleFloatingUIController controller;
    void Awake()
    {
        speedRatio = ratio;
        InitController();
    }
    public void SetText(string text, bool useArtText = false)
    private void InitController()
    {
        //  初始放大200% 透明度50% 7~8帧内缩回100% 透明度到100% 再往上飘14~16帧 然后消失 (30帧/秒)
        if (controller != null) return;
        // 8+16/30=0.8秒
        controller = new BattleFloatingUIController(rectTransform, gameObject, ApplyColor);
        controller.beginPos = beginPos;
        controller.endPos = endPos;
        controller.normalBeginScale =   normalBeginScale;
        controller.normalEndScale = normalEndScale;
    }
        rectTransform.anchoredPosition = beginPos;
        rectTransform.localScale = beginScale;
    public void SetRatio(float speed, float scale)
    {
        InitController(); // 确保 controller 已初始化
        controller.SetRatio(speed, scale);
    }
        timer = 0f;
        gameObject.SetActive(true);
    public void SetText(string text, bool useArtText = false, bool isCrit = false, Color textColor = default)
    {
        if (textColor == default)
        {
            textColor = Color.white;
        }
        InitController();
        if (useArtText)
        {
            artText.text = text;
            artText.color = beginColor;
            tipText.gameObject.SetActive(false);
            artText.gameObject.SetActive(true);
        }
        else
        {
            tipText.text = text;
            tipText.color = beginColor;
            artText.gameObject.SetActive(false);
            tipText.gameObject.SetActive(true);
        }
        controller.beginColor = new Color(textColor.r, textColor.g, textColor.b, controller.beginColor.a);
        controller.endColor = new Color(textColor.r, textColor.g, textColor.b, controller.endColor.a);
        ApplyColor(controller.beginColor);
        Play(isCrit);
    }
    //  不要使用update
    public void Run()
    public void Play(bool isCrit, Action onComplete = null)
    {
        if (!gameObject.activeSelf)
            return;
        if (timer >= totalShowTime)
        InitController(); // 确保 controller 已初始化
        // 合并 OnFinish 和 onComplete
        Action combinedCallback = () =>
        {
            gameObject.SetActive(false);
            OnFinish?.Invoke();
            OnFinish = null;
            return;
        }
            onComplete?.Invoke();
        };
        controller.Play(isCrit, combinedCallback);
    }
        // 整个过程都往上飘
        float moveProgress = timer / totalShowTime;
        rectTransform.anchoredPosition = Vector2.Lerp(beginPos, endPos, moveProgress);
    public void Run()
    {
        if (controller == null) return; // 防止在 Awake 前调用
        controller.Run();
    }
        // 阶段1: 7~8帧内缩放从beginScale到endScale,透明度从50%到100%
        if (timer < scaleChangeTime)
        {
            float scaleProgress = timer / scaleChangeTime;
            rectTransform.localScale = Vector3.Lerp(beginScale, endScale, scaleProgress);
    public void Stop()
    {
        if (controller == null) return;
        controller.Stop();
    }
            Color currentColor = Color.Lerp(beginColor, endColor, scaleProgress);
            if (tipText.gameObject.activeSelf)
                tipText.color = currentColor;
            if (artText.gameObject.activeSelf)
                artText.color = currentColor;
        }
        // 阶段2: 缩放完成后,保持endScale和100%透明度,继续往上飘
        else
        {
            rectTransform.localScale = endScale;
            if (tipText.gameObject.activeSelf)
                tipText.color = endColor;
            if (artText.gameObject.activeSelf)
                artText.color = endColor;
        }
    public void Resume()
    {
        if (controller == null) return;
        controller.Resume();
    }
        timer += 1f / (float)BattleConst.skillMotionFps * speedRatio;
    private void ApplyColor(Color color)
    {
        if (tipText.gameObject.activeSelf)
            tipText.color = color;
        if (artText.gameObject.activeSelf)
            artText.color = color;
    }
    public void ShowBackground(bool showBackground)
    {
        // Implement the logic to show or hide the background
        background.enabled = showBackground;
    }
    public void UpdatePositions(Vector2 begin, Vector2 end)
    {
        InitController();
        beginPos = begin;
        endPos = end;
        controller.beginPos = begin;
        controller.endPos = end;
    }
    public void UpdateScales(Vector3 beginScale, Vector3 endScale)
    {
        InitController();
        normalBeginScale = beginScale;
        normalEndScale = endScale;
        controller.normalBeginScale = beginScale;
        controller.normalEndScale = endScale;
    }
}
Main/System/Battle/UIComp/DamageContent.cs
@@ -4,71 +4,115 @@
using System;
using Cysharp.Threading.Tasks;
public class DamageContent : MonoBehaviour
public class DamageContent : MonoBehaviour, IBattleFloatingUI
{
    public GameObject line;
    public GameObject line;
    public RectTransform parent;
    public RectTransform parent;
    public Vector2 beginPos = Vector2.zero;
    public Vector2 endPos = new Vector2(0, 150);
    protected List<DamageLine> damageLineList = new List<DamageLine>();
    protected List<DamageLine> damageLineList = new List<DamageLine>();
    private BattleDmgInfo battleDmgInfo;
    private BattleFloatingUIController controller;
    public PositionTween posTween;
    //  飘血优化:初始放大200%,透明度50%,7~8帧内缩放回100%,透明度回到100%,再往上飘14~16帧【30帧/秒】,暴击初始放大300%,缩回150%
    //    战斗帧BattleConst.skillMotionFps 1秒=30帧
    public ScaleTween scaleTween;
    void Awake()
    {
        line.SetActive(false);
    }
    private BattleDmgInfo battleDmgInfo;
    private void InitController()
    {
        if (controller != null) return;
    void Awake()
    {
        line.SetActive(false);
    }
        RectTransform rectTransform = GetComponent<RectTransform>();
        controller = new BattleFloatingUIController(rectTransform, gameObject, ApplyColor);
        // 使用当前设置的 beginPos 和 endPos
        controller.beginPos = beginPos;
        controller.endPos = endPos;
        // controller.scaleChangeTime = scaleChangeTime;
        // controller.totalShowTime = totalShowTime;
        // controller.normalBeginScale = normalBeginScale;
        // controller.normalEndScale = normalEndScale;
        // controller.critBeginScale = critBeginScale;
        // controller.critEndScale = critEndScale;
        // controller.beginColor = beginColor;
        // controller.endColor = endColor;
    }
    public async void SetDamage(BattleDmgInfo _damageInfo, Action _onComplete)
    {
        battleDmgInfo = _damageInfo;
    public void SetRatio(float speed, float scale)
    {
        InitController();
        controller.SetRatio(speed, scale);
    }
        var damages = battleDmgInfo.battleDamageList;
    public async void SetDamage(BattleDmgInfo _damageInfo, Action _onComplete)
    {
        battleDmgInfo = _damageInfo;
        for (int i = damages.Count; i < damageLineList.Count; i++)
        {
            damageLineList[i].SetActive(false);
        }
        var damages = battleDmgInfo.battleDamageList;
        posTween.Play(_onComplete);
        for (int i = damages.Count; i < damageLineList.Count; i++)
        {
            damageLineList[i].SetActive(false);
        }
        if (battleDmgInfo.IsCrit())
        {
            scaleTween.Play();
        }
        // 使用控制器的Play方法
        bool isCrit = battleDmgInfo.IsCrit();
        Play(isCrit, _onComplete);
        for (int i = 0; i < damages.Count; i++)
        {
            if (i >= damageLineList.Count)
            {
                GameObject newLine = GameObject.Instantiate(line, parent);
                damageLineList.Add(newLine.GetComponent<DamageLine>());
            }
            damageLineList[i].SetActive(true);
            damageLineList[i].SetDamage(damages[i]);
            await UniTask.Delay(100);
        }
    }
        for (int i = 0; i < damages.Count; i++)
        {
            if (i >= damageLineList.Count)
            {
                GameObject newLine = GameObject.Instantiate(line, parent);
                damageLineList.Add(newLine.GetComponent<DamageLine>());
            }
            damageLineList[i].SetActive(true);
            damageLineList[i].SetDamage(damages[i]);
            await UniTask.Delay(100);
        }
    }
    public void Stop()
    {
        posTween.Stop();
        if (battleDmgInfo.IsCrit())
        {
            scaleTween.Stop();
        }
    }
    public void Play(bool isCrit, Action onComplete = null)
    {
        InitController();
        // 每次Play前更新controller的位置设置
        controller.beginPos = beginPos;
        controller.endPos = endPos;
        controller.Play(isCrit, onComplete);
    }
    public void Resume()
    {
        posTween.Resume();
        if (battleDmgInfo.IsCrit())
        {
            scaleTween.Resume();
        }
    }
    public void Run()
    {
        if (controller == null) return;
        controller.Run();
    }
    public void Stop()
    {
        if (controller == null) return;
        controller.Stop();
    }
    public void Resume()
    {
        if (controller == null) return;
        controller.Resume();
    }
    private void ApplyColor(Color color)
    {
        for (int i = 0; i < damageLineList.Count; i++)
        {
            if (damageLineList[i].gameObject.activeSelf)
            {
                damageLineList[i].SetColor(color);
            }
        }
    }
}
Main/System/Battle/UIComp/DamageLine.cs
@@ -1,6 +1,7 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class DamageLine : MonoBehaviour
{
@@ -26,4 +27,25 @@
        damageTypeLabel.SetActive(false);
        damageValueLabel.text = BattleUtility.DisplayDamageNum(damage);
    }
    public void SetColor(Color color)
    {
        var text = GetComponent<Text>();
        if (text != null)
        {
            text.color = color;
        }
        var texts = GetComponentsInChildren<Text>();
        foreach (var t in texts)
        {
            t.color = color;
        }
        var images = GetComponentsInChildren<Image>();
        foreach (var img in images)
        {
            img.color = color;
        }
    }
}
Main/System/Battle/UIComp/IBattleFloatingUI.cs
New file
@@ -0,0 +1,14 @@
using UnityEngine;
using System;
/// <summary>
/// 战斗飘字UI接口
/// </summary>
public interface IBattleFloatingUI
{
    void Play(bool isCrit, Action onComplete = null);
    void Run();
    void Stop();
    void Resume();
    void SetRatio(float speed, float scale);
}
Main/System/Battle/UIComp/IBattleFloatingUI.cs.meta
New file
@@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: a7d43ddbe5932644c82d307b759d51d8
MonoImporter:
  externalObjects: {}
  serializedVersion: 2
  defaultReferences: []
  executionOrder: 0
  icon: {instanceID: 0}
  userData:
  assetBundleName:
  assetBundleVariant: