yyl
2026-04-24 64d526e0f5b9f49fe5bd6dcf043f5071d996e7ad
Main/System/Battle/Skill/SkillBase.Cast.cs
@@ -10,6 +10,17 @@
    // 技能释放主逻辑:广播事件、高亮目标、执行释放
    public virtual void Cast()
    {
#if UNITY_EDITOR
        //  [前冲诊断] Cast() 入口:记录 skillId/caster/BattleType/当前位置/当前是否还在 tween。
        //  用来判断新技能是否在上一个技能的回位 tween 还没完成时就进入 Cast()。
        {
            Vector2 castEntryPos = (caster != null && caster.GetRectTransform() != null)
                ? caster.GetRectTransform().anchoredPosition : Vector2.zero;
            bool casterTweening = caster != null && caster.GetRectTransform() != null
                && DG.Tweening.DOTween.IsTweening(caster.GetRectTransform());
            BattleDebug.LogError($"[前冲诊断] Cast 入口 skillId={skillConfig?.SkillID} caster={caster?.ObjID} battleType={tagUseSkillAttack?.BattleType} castMode={skillSkinConfig?.castMode} anchoredPos={castEntryPos} casterTweening={casterTweening}");
        }
#endif
        // 广播技能释放事件
        string guid = battleField.guid;
        // 获取释放者数据:Hero 传递 teamHero,Mingge 传递 null(因为事件监听器只处理 Hero 数据)
@@ -163,9 +174,15 @@
    // 执行移动-施法-返回序列:通用的移动攻击流程
    private void ExecuteMoveAndCastSequence(RectTransform target, Action onReturnComplete)
    {
#if UNITY_EDITOR
        BattleDebug.LogError($"[前冲诊断] ExecuteMoveAndCastSequence 开始 skillId={skillConfig?.SkillID} caster={caster?.ObjID} battleType={tagUseSkillAttack?.BattleType} CastDistance={skillSkinConfig?.CastDistance} castMode={skillSkinConfig?.castMode}");
#endif
        ShadowIllutionCreate(true);
        MoveToTarget(target, new Vector2(skillSkinConfig.CastDistance, 0), () =>
        {
#if UNITY_EDITOR
            BattleDebug.LogError($"[前冲诊断] 前冲完成 skillId={skillConfig?.SkillID} caster={caster?.ObjID} 准备 CastImpl");
#endif
            if (skillSkinConfig.CastDistance < 9999 && skillSkinConfig.SkinllSFX2 != 0)
            {
                battleField.soundManager.PlayEffectSound(skillSkinConfig.SkinllSFX2, false);
@@ -197,18 +214,52 @@
    // 移动到目标位置:处理角色的移动动画和逻辑
    protected void MoveToTarget(RectTransform target, Vector2 offset, Action _onComplete = null, float speed = 750f)
    {
#if UNITY_EDITOR
        //  [前冲诊断] 记录入口参数:CastDistance、offset、speed;以及当前 caster 的锚点位置。
        Vector2 fromPos = caster != null && caster.GetRectTransform() != null
            ? caster.GetRectTransform().anchoredPosition : Vector2.zero;
        bool mttTweening = caster != null && caster.GetRectTransform() != null
            && DG.Tweening.DOTween.IsTweening(caster.GetRectTransform());
        BattleDebug.LogError($"[前冲诊断] MoveToTarget 入口 skillId={skillConfig?.SkillID} caster={caster?.ObjID} battleType={tagUseSkillAttack?.BattleType} CastDistance={skillSkinConfig?.CastDistance} offset={offset} speed={speed} fromPos={fromPos} casterTweening={mttTweening}");
#endif
        if (skillSkinConfig.CastDistance >= 9999)
        {
#if UNITY_EDITOR
            BattleDebug.LogError($"[前冲诊断] CastDistance>=9999 直接跳过移动 skillId={skillConfig?.SkillID} caster={caster?.ObjID}");
#endif
            _onComplete?.Invoke();
            return;
        }
        caster.PlayAnimation(MotionName.run, true);
#if UNITY_EDITOR
        //  [前冲诊断] 记录 target 的名字/世界坐标/父节点 scale,便于定位镜像坐标系导致 offset 方向反转
        string targetName = target != null ? target.name : "(null)";
        Vector3 targetWorld = target != null ? target.position : Vector3.zero;
        Vector3 targetLossyScale = target != null ? (Vector3)target.lossyScale : Vector3.one;
        Vector2 targetAnchored = target != null ? target.anchoredPosition : Vector2.zero;
        BattleDebug.LogError($"[前冲诊断] target信息 skillId={skillConfig?.SkillID} caster={caster?.ObjID} casterCamp={caster?.Camp} target.name={targetName} target.anchoredPos={targetAnchored} target.worldPos={targetWorld} target.lossyScale={targetLossyScale}");
#endif
        var tweener = BattleUtility.MoveToTarget(caster.GetRectTransform(), target, offset, () =>
        {
#if UNITY_EDITOR
            Vector2 toPos = caster != null && caster.GetRectTransform() != null
                ? caster.GetRectTransform().anchoredPosition : Vector2.zero;
            BattleDebug.LogError($"[前冲诊断] MoveToTarget 完成 skillId={skillConfig?.SkillID} caster={caster?.ObjID} toPos={toPos}");
#endif
            //  tween 完成时清除 caster 上的 activeMoveTween 句柄,放开 CanCastSkillAnimation 的闸门。
            if (caster != null)
            {
                caster.activeMoveTween = null;
            }
            caster.PlayAnimation(MotionName.idle, true);
            _onComplete?.Invoke();
        }, speed);
        //  记录到 caster,让 CanCastSkillAnimation 能精确等待这一个 tween(而不是 caster 身上任意 tween)。
        if (caster != null)
        {
            caster.activeMoveTween = tweener;
        }
        battleField.battleTweenMgr.OnPlayTween(tweener);
    }
@@ -225,6 +276,16 @@
    // 攻击完成后的处理:转身、恢复状态、播放待机动画
    protected void OnAttackFinish()
    {
#if UNITY_EDITOR
        //  [前冲诊断] OnAttackFinish 入口:记录帧号和当前位置,和 Cast()/MoveToTarget 日志对齐。
        {
            Vector2 finPos = (caster != null && caster.GetRectTransform() != null)
                ? caster.GetRectTransform().anchoredPosition : Vector2.zero;
            bool finTweening = caster != null && caster.GetRectTransform() != null
                && DG.Tweening.DOTween.IsTweening(caster.GetRectTransform());
            BattleDebug.LogError($"[前冲诊断] OnAttackFinish skillId={skillConfig?.SkillID} caster={caster?.ObjID} battleType={tagUseSkillAttack?.BattleType} anchoredPos={finPos} casterTweening={finTweening}");
        }
#endif
        TurnBack(null, 1f);
        OnAllAttackMoveFinished();
        caster.PlayAnimation(MotionName.idle, true);