← 返回项目技术总览

长按左键蓄力抬手只作用于上半身——不管是地面 WASD 跑动腿在动,还是浮空腿部做飞行动作,蓄力抬手都能很好叠加。松手接甩出→放下手。短按仍走原来的普攻连招,手感不受影响。

— 核心诉求

一、三次演进才定下最终规格

版本用户反馈修订
V1 初版—长按→One,松开→Two→衔接Three;超时自动释放
V2 PIE 后按着没松手就自动播完了,应该一直保持蓄力长按不松手→保持 One,播到 75 帧→65↔75 往复(蓄力呼吸动),累计 >2s 强制执行
V3 微调Two 太慢Two 播放速度 ×1.5 → 再 ×1.5(最终 2.25),验收满意

二、短按 vs 长按的判定窗口

左键只这一个按键,用 0.25s 判定窗口分流:

// ChangliCharacter.cpp · 左键输入守卫
void AChangliCharacter::OnAttackPressedKey()
{
    if (bIsCharging) return;                    // 蓄力中不重复触发
    bWasChargingReleased = false;
    GetWorldTimerManager().SetTimer(
        ChargeKeyPressTimer, this,
        &AChangliCharacter::OnChargeDetected, 0.25f, false);  // 0.25s 后判定
}

void AChangliCharacter::OnAttackReleasedKey()
{
    if (!bWasChargingReleased) {
        // 0.25s 内松开 → 短按,走普攻
        GetWorldTimerManager().ClearTimer(ChargeKeyPressTimer);
        DoAttack(); return;
    }
    // 长按松开 → 触发蓄力释放
    if (bIsCharging) { ReleaseCharge(); }
}

三、AnimGraph 四层结构(上半身分层关键)

原 ABP AB_ChangLi_Nyx 只有 1 个 Slot(DefaultSlot),新增分层 Slot 后变成四层:

Locomotion(StateMachine)
  └─> Slot(DefaultSlot)                          ← 原有全身蒙太奇仍走这里,不受影响
        ├─> SaveCachedPose(ChargeBasePose)
        │      ├─> UseCachedPose A ──> LayeredBoneBlend.BasePose
        │      └─> UseCachedPose B ──> Slot(UpperBody).Source
        └─> Slot(UpperBody).Pose ──> LayeredBoneBlend.BlendPoses_0
LayeredBoneBlend.Pose ──> LocalToComponentSpace ──> ModifyBone ──> TwoBoneIK×2 ──> AnimDynamics ──> KawaiiPhysics×5 ──> Root

为什么用 SaveCachedPose + UseCachedPose:

四、三段动画与时序

动画帧数时长播放速度Loop
AM_CL_Attack_01_One(蓄力抬手)762.533s1.25×是(65↔75 往复)
AM_CL_Attack_01_Two(甩出)301.0s2.25×否
AM_CL_Attack_01_Three(放下手)451.5s1.0×否

关键:没有对应蒙太奇,直接用 PlayAnimation 播动画序列。蓄力抬手 One 播到 75 帧仍未松手 → AnimBP 里 AnimBP Event BlueprintUpdateAnimation 驱动 Tick,在进度 65↔75 之间往复播放(蓄力呼吸效果)。

五、AnimBP 里的事件驱动链

// AB_ChangLi_Nyx 的 AnimGraph Event BlueprintUpdateAnimation(每帧 Tick)
// 输入:Player → IsCharging(bool)、ChargeProgress(0-1 float)
// 动作:
//   if IsCharging=true → Set Slot(UpperBody) Pose = PlayAnimation(One)
//     播放到 75 帧 → 在 65↔75 之间 Reverse → Forward 往复
//   if IsCharging=false 且 bJustReleased=true → Set Slot(UpperBody) Pose = PlayAnimation(Two, then Three)
//   else → Slot(UpperBody) 置空 → 上半身回归全身 Locomotion

六、踩坑清单