}

高级运动系统实践

W 2023-08-10 21:20:29
Categories: Tags:

| 标题 | 内容 |

动画管线搭建和优化 —-

动画曲线

曲线名称 Set
Yaw Offset【未用】 动画蓝图八方向移动时设置
角色蓝图每tick读取数值 Actor.Yaw+Yaw Offset=Actor.Yaw.设置角色Yaw的偏移。
Gait_Weight 【未用】 存在于locomotion动画clip中,标明本动画是走还是跑,走是1,跑是2,这样Blendspace混合的时候Gait_Weight也会跟着被混合。
FootLock_L 曲线值决定是否打开IK和IK混合状态

变量

Character Information【角色的基础数据】

待续:

  1. MovementInput只知道它是和输入相关的,不是角色真实加速度。
    void APawn::Internal_AddMovementInput(FVector WorldAccel, bool bForce /*=false*/)
    {
    	if (bForce || !IsMoveInputIgnored())
    	{
    		ControlInputVector += WorldAccel;
    	}
    }
  2. AimYawRate为啥是取绝对值。
Character Information【EssentialValues相关】 类型 意义
Velocity Vector Get Actor Velocity节点
Acceleration Vector 自定义的 当前速度减去上上一帧速度再除以时间变化得到真实加速度
(GetVelocity - Previous Velocity )/GetWorldDeltaSeconds=Acceleration
MovementInput Vector Character Movement Component组件的默认函数Get Current Acceleration 具体含义不是很清楚。【https://docs.unrealengine.com/5.2/en-US/understanding-networked-movement-in-the-character-movement-component-for-unreal-engine/】
IsMoving bool 判断角色移动还是停止。 取GetVelocity返回的角色速度平面方向的向量的长度如果大于1.0就是在移动。
HasMovementInput bool 判断角色是否有移动输入。MovementInputAmount>0.0则判断为真
MovementInputAmount float 用途待续。 角色的GetCurrentAcceleration向量的长度除以GetMaxAcceleration
Speed float 角色水平移动速度大小。Actor的GetVelocity去掉Z向量以后的长度。
AimingRotation Rotator 当前视角的旋转。Pawn的GetControRotation函数得到。
AimYawRate float 描述摄像机从左向右旋转的速度。当前摄像机Yaw减去上一帧的Yaw再除以时间,再取绝对值。【取绝对值可能就转换成从左到右的速度了。】

Character State【角色的状态】

  1. 状态主要通过宏间接设置。【宏的作用是方便的同时设置上一状态和当前状态,先把当前记录给pre变量,再把最新的值给当前变量。】
Character Information【第二部分】 类型
RelativeVelocityDirection Vector
ZoomAmount float
MovementState 运动状态 MovementState:None ;Grounded;InAir;Mantling;Ragdoll
PrevMovementState 运动状态 MovementState:None ;Grounded;InAir;Mantling;Ragdoll
MovementAction 运动动作 LowMantle;HighMantle; Rolling;GettingUp
RotationMode 旋转模式 VelocitDirection; LookingDirection;Aiming
Gait 步态 Walking; Running ;Spring
Stance 站姿 Standing; Crouching
OverlayState 覆盖状态 Default、Masculne、Feminine、Injured、HandsTied、Rifle、Pistol1HPistol2H、Bow、Torch、Binoculars、Box、Barrel
ALS_N_WalkRun_F 资产

Anim Graph - Grounded

Anim Graph - Grounded 类型
StrideBlend float 角色的步长。速度大小为0时,步长为0.速度达到某具体值,步长为1.
StandingPlayRate float
MovementDirection
TrackedHipsDirection
RelativeAccelerationAmount Vector
ShouldMove Bool
Rotate_L Bool
Rotate_R Bool
Pivot Bool
RotateRate float
RotationScale
WalkRunBlend float
StandingPlayRate float
CrouchingPlayRate float
StrideBlend float
VelocityBlend F B L R 运动方向在角色空间内的4个标准化分量大小。经过平滑以后控制4动画的切换。
LeanAmount LR FB
FYaw float
BYaw float
LYaw float
RYaw float

Anim Graph - Aiming Values

Aiming Values 类型
SmoothedAimingRotation Rotator
SpineRotation Rotator
AimingAngle Vector 2D
SmoothedAimingAngle Vector 2D
AimSweepTime Float
InputYawOffsetTime Float
ForwardYawTime Float
LeftYawTime Float
RightYawTime Float

Rotate in Place

Rotate in Place 类型
RotateMinThreshold Float
RotateMaxThreshold Float
AimYawRateMinRange Float
AimYawRateMaxRange Float
MinPlayRate Float
MaxPlayRate Float

问题:

当步长和速度等于0的时候,姿势是idle姿势还是Rest姿势?

如何提取一个Pose并生成一段动画

Input Mapping Context如何加载

前后左右切换动作的时候,会根据VelocityBlend瞬切

  1. VelocityBlend需要经过插值平滑以后在使用。

角色向正左正右走路动画不对。

需要修改角色左右的跑步动画,现在是头超前,脚步朝向45度角,需要改为脚步朝向正右方向。

sync的使用

  1. 动画蓝图中,所有用来播放动画资产的Player动画节点都可以在Details中配置SyncGroup相关参数。
  2. Graph同步方式指的是把所有动画节点(Input)合并到Sync节点指定的GroupName。
  3. 蒙太奇也可以设置SyncGroup,需要注意,Montage只在融出时参与同步,并且默认是Leader,即其他正在播放的动画会向Montage同步。
  4. 具体生效步骤:
    1. 在动画里边创建同步标记。
    2. 动画graph中,播放动画的节点上面设置同步组名称,相同名称的动画播放节点就会自动同步。

动画修改器(AnimationModifier)

https://docs.unrealengine.com/5.1/en-US/animation-modifiers-in-unreal-engine/

批量修改 Animation Sequence 资源或者骨骼资源(比如批量添加同步标记 添加动画曲线等)
可以在Animation Sequence 资源或者骨骼资源编辑界面打开窗口 Animation Data Modifiers 选择需要执行的Modifier

A->W->D->S 切换肩膀转动太明显

向左走和向右走,肩膀转动角度太大,导致切换到前和后的时候幅度很大。也就啊是AWDS切换不好看。如下图【1.尝试下让上半身过渡慢一些,下半身过渡快一些解决2.尝试修改动作来解决】

混合遮罩

https://docs.unrealengine.com/5.1/zh-CN/blend-masks-and-blend-profiles-in-unreal-engine/

个人理解:

Weight Blend Profiles【权重混合描述】

Blend Mask【混合遮罩】

增强输入 和 宏

https://blog.csdn.net/u011254268/article/details/131434703

心得:基础物理属性要第一时间计算完毕。

理论上来讲,只把基础数据【比如速度/加速度/加速度输入/视角旋转】传递给动画蓝图就够用了,不过一些重要需要计算的依赖于基础数据也需要在角色蓝图中给出。【其实主要是这些直接或者间接依赖于物理属性的数据需要第一时间计算好,方便后面使用。】【比如IsMoving/HasInputMovement/Speed/InputMovementMount/】

Grounded-角色Gait流程:

角色蓝图中:

  1. 根据按键得到玩家期望的Desired Gait。
  2. 结合旋转模式和站蹲爬,得到期望且允许的 Allowed Gait。【期望的Gait要经过限制,比如蹲时不能冲刺】
  3. 根据Allowed Gait从配表读取Walk、Run、Sprint速度,然后设置最大walk速度。
  4. 根据实际速度和配表得到实际Gait传给动画蓝图。【动画根据实际速度来挑选动作】

Grounded-角色WalkSpeed设置流程:

角色蓝图中:

  1. 根据旋转模式【3种情形】、站蹲状态【2种情形】从配表中读取Walk、Run、Sprint速度和摩擦力,总共6种情况,选出一种作为当前Current Movement Settings。
  2. 再根据当前Gait设置Max Walk Speed。
  3. 最大加速度/制动加速度/地面摩擦力
  4. 当前速度大小根据walkspeed-runspeed-sprintSpeed映射到0-1-2-3的范围,根据0-3的数值获取最大加速度/制动加速度/地面摩擦力
    Max Acceleration:
    Braking Deceleration Walking:
    Ground Friction:

其他

Grounded-加入停步动作

停步分2种,一种是快速响应,发生在向locomotion未完全过渡完成时,即玩家快速高频停止和移。另一种是完整响应,发生在locomotion权重为1时,即玩家移动了一会儿再停止,重点在停止动作的完整性。

停步快速响应

这个比较简单,直接向站立Pose过渡即可,不过脚掌不动,双脚都打开IK不进行过渡,其他骨骼直接过渡到idle状态。

完整停步动作

首先看是否有一个脚踩在地上,如果有就lock住它的IK,让他一直不动,另一只脚继续过渡到idle姿势。

如果没有任意一只脚踩地,那么根据Foot_Position来选择左腿覆盖blend到一个踩地姿势,并lock住,其他所有骨骼继续过渡到Idle。

由于状态和状态的过度是有一个混合过程的,所以这个覆盖不是一个瞬切。

操作细节:
节点名称:Modify Curve

脚步IK层

脚步锁定逻辑

用到的数据

名称 类型 作用
Enable_FootIK_L 动画曲线 曲线值不为零时脚步IK可以锁定。
FootLock_L 动画曲线 当曲线值为1时,瞬间锁定脚步。值不为0时,通过Alpah来控制IK的混合。
ik_foot_l 骨骼 clip中该骨骼和脚踝位置是重合的,当脚步锁定瞬间这个骨骼不再运动。通过胶囊体的每tick运动来使该骨骼相对世界空间不动。已到达脚步锁定的目的。
Foot Lock L Alpha float变量 IK混合比例,只可变小不可以变大。
Foot Lock L Location Vector变量 初始值为脚步锁定的瞬间位置。它位于胶囊体空间内,但经过计算它相对世界不动。
Foot Lock Rotation Rotator变量 作用和Foot Lock L Location是类似的。

通俗理解

当FootLock_L曲线值为1时,要瞬间锁定脚步骨骼ik_foot_l让它相对于世界静止。利用ik节点的Alpha通道来融合。Alpha只可以变小不可以变大,可以缓出,不可以缓入。

物体世界空间位置旋转保持不变算法

由位置计算方式:P点世界位置 = P点局部位置 * 物体旋转 + 物体位移
W = P1R1+T1
W = P2
R2+T2
得到 P2 = (P1*R1+T1-T2) / R2

脚步台阶逻辑

名称 类型 作用
Enable_FootIK_L 动画曲线
ik_foot_l 动画曲线
root 骨骼
FootOffset_L_Target Vector 由碰撞点减去脚ik在Root平面上的投影点得到的向量。
FootOffset_L_Location Vector 默认是[0,0,0],它逐渐插值到碰撞目标位置,如果它向上插值接近碰撞目标位置,速度比较慢,说明碰撞点在脚掌的上方。
FootOffset_L_Rotation Rotator 由碰撞点的法线计算得到的旋转体
IKFootFloorLocation Vector xy是世界空间中IK骨骼的xy,z是Root骨骼在世界空间中的Z高度。

自己思考的脚步台阶逻辑

  1. 预测脚步要踩得目标位置,比如根据速度和迈步确定脚落在哪里。
  2. 脚抬起来肯定就要落下去。
  3. 从脚步抬起到最高点到脚步踩到台阶上,逐步Alpha从0过渡到1。

脚步台阶和脚步锁定的综合:

总结:

FootLock IK 实践

遇到的问题:

  1. 使用保存ik_foot_l骨骼的位置的方式去Lock脚掌的方式是没问题的,其中骨骼的组件空间位置旋转需要乘以Mesh组件的位移旋转才能正确得到世界坐标。保存世界坐标以后,还得再inverse Mesh组件的Transform,稍微麻烦些。所以最终采用的是组件空间中的向量逆向去除角色位移偏移、旋转偏移的影响,求得新的组件空间中的向量。【这里重点是先去除角色的位移影响,再去除角色的旋转影响,效果才是正确的。】
  2. Actor的旋转减去胶囊体的GetLastUpdateRotation得到的结果恒定是【0,0,0】

IK Lock实践中的具体步骤:

  1. 骨骼位置 减去 Actor的世界位置变化,那么当角色任意方向位移时,骨骼世界位置不变。
  2. 再说旋转,骨骼位置先减去Actor的世界位移变化,假设角色旋转了Rotator_delta,骨骼的新位置再unRotate一下Rotator_delta即可保持骨骼在世界中固定位置。
  3. 测试中发现:Actor的旋转减去胶囊体的GetLastUpdateRotation得到的结果恒定是【0,0,0】,只能自己存储上一tick的Actor的旋转,然后再得到旋转变化。

Animation Modifier【烘焙ik_foot关键帧】 实践

  1. 每个骨骼都有一个Track,它的名字就是骨骼名字。内部包含位移旋转缩放共计9个动画。
  2. Curve指的是存在于Skeleton文件的属性名称。

停步实践

  1. 蒙太奇播放时,会把插槽前面的连接暂停【或者说覆盖】,blend out以后才会恢复。
  2. 播放蒙太奇时,角色可以移动,但是身体动画不可以播放其他的【暂时只有一个插槽,它截断的是主干道。所以暂时是这个结论,估计截断的只是分支的话,不影响其他分支播放动画】。
  3. 停步动画是从它0.3秒开始播,blend in 0.1秒 Blend Out 0.2秒,播放速率乘以1.75倍。
  4. 蒙太奇Blend In过程中前面的逻辑一直运行,直到蒙太奇权重为1.0.
  5. 当蒙太奇权重为1时,前面的transition瞬间过渡完成,这应该算是一个优化,因为没必要再继续慢慢流转状态。

    关于状态机

停步实践

1. 播放蒙太奇的时候,状态机动画停止更新,怎么处理?

具体表现是由走到停停止,在播放停止踏步动画蒙太奇的时候,再按方向键角色会整体平移但是动画还是继续踏步。
原因:
播放蒙太奇的时候,插槽前面的状态已经停止更新了,所以必须等到蒙太奇播放结束才可以继续更新状态机。
解决方法:
播放蒙太奇的时候,插槽要选择“保持插槽前面的Pose刷新”,这样状态就可以在播放蒙太奇的时候继续更新。

2. 如何在蒙太奇播放进行中,恢复播放状态机动画?

设置如下即可解决:

  1. 进入【(N) Moving】节点时增加事件“StopTransition”
  2. 离开【(N) Not Moving】节点时增加事件“StopTransition”
  3. StopTransition事件触发所有插槽的Stop。

3. StateMachine参数详解?

4.快速停步和完整停步

快速停步效果是0.2跑步blend站立姿势,然后再播放一下踏步动画【个人认为没必要再播放这个动画】。
箭头处是松开方向键的瞬间,FootLock_L由0.0上升到了大概0.3,这是由于Not Moving的修改曲线引起的,之后曲线值又开始下降,这个应该是播放踏步动画引起的。

  1. 绝大部分时间都是走的一般停步逻辑,只有在走步持续不到0.3秒的情况下再停步才会走快速路线。【Not Moving到Moving的过渡时间是0.3秒】

5.完整停步逻辑

简述:

  1. 首先判断左脚踩地还是右脚踩地

  2. 如果是左脚踩地,那么左脚固定,播放左脚开始的停步动画。如果是右脚,则播放另外一个动画。

    完整停步逻辑根据停步瞬间脚掌是否踩地分为2种情况:

  3. 有一只完全踩地:lock对应脚掌,然后播放踏步动画。

  4. 两只脚都没有完全踩地:让脚blend过渡到踩地之后,lock脚掌播放踏步动画【这里的过渡时间由过渡规则的过渡时间决定】。

FootLock_L曲线变化与表现之间的关系:

  1. FootLock_L从0过渡到1.0:小于0时没有反应,等于1.0时,瞬间锁住。
  2. FootLock_L一直是1.0:左脚会一直锁定在原地。【此时虽然多次提取ik_footl_l的位置,但是每次提取的都是固定不变的。】
  3. FootLock_L从1过渡到0.0:左脚从alpha小于等于0.99开始逐渐变为动画完全控制。【因为modify bone节点的alpha逐渐变为0.0,也就是完全和动画的骨骼同步。】

Foot_Position作用:

当Value=-1:代表当前动作左脚完全踩地。【如果右脚完全踩地,停步动画就可以锁定左脚,右脚完全播放动画即可】
当Value=1:代表当前动作右脚完全踩地。
当Value=-0.2:代表当前动作左脚完全踩地前。【左脚还未完全踩地,需要0.1秒过渡到左脚踩地,然后再左脚锁定,右脚播放踏步动画。】
当Value=0.2:代表当前动作右脚完全踩地前。

注意事项:

  1. 某个状态机节点内的Modify Curve:它所代表的状态,受到节点权重的影响。比如Modify Curve 【曲线名字:FootLock_L】为1.0,此时状态过渡权重为0.9,那么FootLock_L曲线的实际值是0.9.
  2. 想要瞬间Modify Curve一个曲线数值,需要确保激活state过渡到这个state都是瞬间完成的,不然就会有过渡的过程。

完整停步中的先踩地再锁脚的执行的时机详解

FootLock_L和Layered Blend Per Bone是同时完成的,当FootLock_L曲线值0.2秒内变为1.0时,Blend节点也刚好完成过渡【所以左脚姿势blend过渡完成了的瞬间,左脚刚好完全锁住。但是进入节点的->N Stop L事件是在节点权重为0.0001时触发的,所以是提前播放了踏步动画。】。

状态节点的3个事件详细触发时机:

AnimStateMachineTypes.h

// The index of the notify to fire when this state is first entered (weight within the machine becomes non-zero)
  //当节点权重非0时,触发
UPROPERTY()
int32 StartNotify;

// The index of the notify to fire when this state is finished exiting (weight within the machine becomes zero)
  ////当节点权重等于0时,触发
UPROPERTY()
int32 EndNotify;

// The index of the notify to fire when this state is fully entered (weight within the machine becomes one)
  ////当节点权重等于1时,触发
UPROPERTY()
int32 FullyBlendedNotify;

AnimNode_StateMachine.cpp 【Update_AnyThread函数中】

// On the first update, call the initial state's Start Notify.
if (bFirstUpdate)
{
	//Handle enter notify for "first" (after initial transitions) state
	Context.AnimInstanceProxy->AddAnimNotifyFromGeneratedClass(GetStateInfo().StartNotify);
}

原地旋转

原地旋转原理

什么时候开始?

在瞄准状态下,摄像机和角色朝向相差50度以上,角色就会开始迈步原地旋转。

持续到什么时候?

直到进入阈值以内。【视角转速越快旋转动画播放速率越高。】。再辅以脊椎实时转动即可达到目光和角色上半身朝向完全一致。

为了实现什么效果?

瞄准的时候,上半身朝向要和视角完全保持一致,我们希望此时脚掌和地面合理交互。比如角色一直迈步原地旋转。

原地转身

原地转身效果?

在Look模式下,摄像机独立于角色旋转,当摄像机和角色朝向相差一定角度以后,延时0.75秒,角色播放一个迈步旋转动作。

  1. 大于130,播放180的转身动画
  2. 小于130,播放90的转身动画

原地转身和原地旋转的区别?

  1. 原地旋转是Aiming状态下,原地转身是look状态下。
  2. 原地旋转是为了让角色尽量持续的和视角朝向保持一致。【如果是射击游戏的话,aiming状态下,前胸和摄像机保持严格朝向一致,下半身则一直播放踏步动画。】
  3. 原地转身是当角色随意旋转摄像机,角色不做任何反应。只有当角度相差太大且延时一段时间后,角色做一个迈步旋转动作。

跳跃坠落

  1. 起跳的时候,分左右脚起跳。【通过foot_position来判断左右脚】
  2. 上升和下坠用速度的Z方向来分辨。Z<0.0则进入下坠循环。
Jumped Boolean 键盘触发它的修改,延时0.1秒以后它就会变成False
Jump Play Rate Float 跳跃瞬间,由Speed的大小进行映射。【0-600】映射到【1.2,1.5】
Movement State 空中 地面等 Character自带事件。使用NewMovementMode参数即可。
Lean Amount LR
Lean Amount FB
LandPrediction Float 当距离地面一定高度时,就要过渡到着陆动作的第一针Pose了。
准备着陆的姿势由Fall Speed下降速度决定混合度。
Jump 状态

Movement State

Movement State由Character自带事件触发更新即可。

Character自带事件。使用NewMovementMode参数。
Character.cpp -> K2_OnMovementModeChanged

/**
 * Called from CharacterMovementComponent to notify the character that the movement mode has changed.
 * @param	PrevMovementMode	Movement mode before the change
 * @param	NewMovementMode		New movement mode
 * @param	PrevCustomMode		Custom mode before the change (applicable if PrevMovementMode is Custom)
 * @param	NewCustomMode		New custom mode (applicable if NewMovementMode is Custom)
 */
UFUNCTION(BlueprintImplementableEvent, meta=(DisplayName="OnMovementModeChanged", ScriptName="OnMovementModeChanged"))
void K2_OnMovementModeChanged(EMovementMode PrevMovementMode, EMovementMode NewMovementMode, uint8 PrevCustomMode, uint8 NewCustomMode);

目的:停步的时候播放速率要降下来。

现象

角色由跑、步行、冲刺过渡到停步的时候,感觉动作很着急。

解决

猜测是因为停步的时候由正常速度的Walk或者Run在0.1秒的时间内过渡到踏步动作,融合结果会感觉很快,此时应该大幅降低walk播放速率,相当于进入一个walk的慢动作再去停步,会比较自然。

简单理解

胶囊体运动速度为0的瞬间,播放速率其实应该逐渐要接近0.

细节

比较细节的理解【这里不仅考虑停步的时候要慢下来,而且考虑由站立过渡到walk、由站立过渡到Run、由站立过渡到Sprint三种的播放速率变化。】:

Standing Play Rate 1.角色速度除以walk标准速度,得到【Stand->Walk】的速率变化。
2. 角色速度除以Run标准速度,得到【Stand->Walk】的速率变化,但是只取【Walk->Run】这段,由Weight_Gait控制切换。
3. 角色速度除以Sprint标准速度,得到【Stand->Walk】的速率变化,但是只取【Run->Sprint】这段,由Weight_Gait控制切换。