[UE4]Animation Montage动画相关
keywords: [UE4]Animation Montage动画相关
常用API
C++如何播放Montage动画
Character接口:
假设是在Character内执行,其中MontagePtr为Montage指针:
float AnimDuration = PlayAnimMontage(MontagePtr) / (GetMesh() && GetMesh()->GlobalAnimRateScale > 0 ? GetMesh()->GlobalAnimRateScale : 1);
AnimInstace接口:
float AnimDuration = Montage_Play(Param.Montage, Param.PlayRate);
如果只是播放Animation Sequence,不需要Montage,可以使用 PlayAnimation():
if (USkeletalMeshComponent *Mesh = GetMesh())
{
Mesh->PlayAnimation(AnimSequence, false);
}
注意:PlayAnimation可以直接来播放AnimSequence,但是不能混合动作。比如当播放完攻击动作后,无法自动回到空闲状态,但是通过Montage的Slot可以完成自动切换动作。
其他参考:
How can I play animations strictly from C++?
https://answers.unrealengine.com/questions/292345/how-can-i-play-animations-strictly-from-c.html
C++动态播放Montage(通过AnimSequence创建)
keywords:UE4, C++, AnimSequence, AnimMontage, Slot, 动态创建, 播放动画
首先要在动画蓝图中添加Slot节点,默认的Slot名字为DefaultSlot。这个名字会在C++代码中使用
假设已经获取了要播放的AnimSequence对象指针:AnimSeq
float AnimLength = 0.f;
if (USkeletalMeshComponent *Mesh = MyActor->FindComponentByClass<USkeletalMeshComponent>())
{
if (UAnimInstance *AnimInst = Mesh->GetAnimInstance())
{
if (UAnimMontage* Mtg = AnimInst->PlaySlotAnimationAsDynamicMontage(AnimSeq, TEXT("DefaultSlot"), 0.25f, 0.25f, 1.f, 1))
{
AnimLength = Mtg->GetPlayLength();
}
}
}
使用UAnimInstance::PlaySlotAnimationAsDynamicMontage()
播放Montage时,参数 Animation Sequence 中 Notify 仍然对动态生成的 Montage 有效,即:Montage 也会触发同样的Notify。
如果将PlaySlotAnimationAsDynamicMontage()
的返回值Montage保存起来,则这个Montage对象会保存创建时指定的LoopCount, SlotName等信息。
如果下次播放该Montage时希望修改LoopCount
、SlotName
等信息,则需要创建新的Montage对象。
使用PlaySlotAnimationAsDynamicMontage()
播放Montage时,如果不是循环播放的Montage,播放完毕后一定要执行UAnimInstance::Montage_Stop()
停掉当前Montage,否则会干扰蓝图动画的逻辑,导致切换其他Montage时无效。
C++播放Montage动画时让动作停留在最后一帧
keywords:UE4、Montage、C++、播放速度、播放速率、加速播放、减速播放
四种方式
方式1:
在想要冻结的那一帧内,将SkeletonComponent的GlobalAnimRateScale属性设置为0。
比如想要角色的骨骼定格在第三帧,那么就在动画播放到第三帧的时候将该属性设置为0。
ACharacter::GetMesh()->GlobalAnimRateScale = 0.f;
方式2:
ACharacter::GetMesh()->bNoSkeletonUpdate = true;
方式3:
ACharacter::GetMesh()->bPauseAnims = true;
注意:正常播放完AnimSequence时,只要不是设置为循环播放,播放完以后会自动停留在最后一帧。只有播放Montage时才需要设置以上属性。
方式4:
将Enable Auto Blend Out
属性设置为false,这样当Montage播放到结尾时不会自动Blend Out,而是停留在最后一帧直到手动Stop。
动画状态机切换动画时让动画停留在最后一帧
动画蓝图中的State内部播放动画的节点属性Loop
修改为false
:
如果想让动画停留在最后一帧,另一种方式是在动画的结尾添加Notify事件,在事件中修改动画的播放速率为0。
C++播放Montage时指定Section
方式1:
通过第三个参数 StartSectionName 指定 Section
float ACharacter::PlayAnimMontage(class UAnimMontage* AnimMontage, float InPlayRate = 1.f, FName StartSectionName = NAME_None);
方式2:
使用 UAnimInstance::Montage_JumpToSection()
if (USkeletalMeshComponent *Mesh = Character->GetMesh())
{
if (UAnimInstance *AnimInst = Mesh->GetAnimInstance())
{
Char->PlayAnimMontage(MontagePtr);
AnimInst->Montage_JumpToSection(FName("step_03"), MontagePtr);
}
}
执行 Montage_JumpToSection 时请确保角色动画的正处于Section所属Montage的播放状态,否则 JumpToSection 无效。
获取当前正在播放的Montage
/** Get a current Active Montage in this AnimInstance.
Note that there might be multiple Active at the same time. This will only return the first active one it finds. **/
UAnimMontage* UAnimInstance::GetCurrentActiveMontage() const
停止Montage播放
如果要停止当前正在播放的Montage,三种方式。
方式1:
void ACharacter::StopAnimMontage(class UAnimMontage* AnimMontage);
方式2:
void UAnimInstance::Montage_Stop(float InBlendOutTime, const UAnimMontage* Montage = nullptr);
If no Montage reference was gave (Montage
is nullptr
), all actived montages will be stop.
Source in Engine:
void UAnimInstance::Montage_Stop(float InBlendOutTime, const UAnimMontage* Montage)
{
if (Montage)
{
FAnimMontageInstance* MontageInstance = GetActiveInstanceForMontage(Montage);
if (MontageInstance)
{
MontageInstance->Stop(FAlphaBlend(Montage->BlendOut, InBlendOutTime));
}
}
else
{
// If no Montage reference, do it on all active ones.
for (int32 InstanceIndex = 0; InstanceIndex < MontageInstances.Num(); InstanceIndex++)
{
FAnimMontageInstance* MontageInstance = MontageInstances[InstanceIndex];
if (MontageInstance && MontageInstance->IsActive())
{
MontageInstance->Stop(FAlphaBlend(MontageInstance->Montage->BlendOut, InBlendOutTime));
}
}
}
}
方式3:
void UAnimMontage::Stop(const FAlphaBlend& InBlendOut, bool bInterrupt=true);
如果希望停止播放Montage时有动画融合效果,记得将Montage的Blend Out Time设置为大于0,比如默认值 0.25 。
一旦通过StopAnimMontage停止正在播放的Montage,角色就会恢复到待机动作(假设动画蓝图的默认动画为待机动画的话)。
设置Montage播放结束和融合结束后的回调函数
//设置Montage播放完毕(动画的最后一帧)的回调
void UAnimInstance::Montage_SetEndDelegate(FOnMontageEnded & InOnMontageEnded, UAnimMontage* Montage = NULL);
//设置Montage混出(BlendOut)开始时的回调
void UAnimInstance::Montage_SetBlendingOutDelegate(FOnMontageBlendingOutStarted & InOnMontageBlendingOut, UAnimMontage* Montage = NULL);
如果在播放Montage的同时添加一个float类型的变量(比如float AnimPlayingDelata = 0.f;
),并在Tick中执行AnimPlayingDelata += DeltaSeconds
,那么AnimPlayingDelata >= Montage->SequenceLength
将在Montage_SetEndDelegate
回调之前触发。
Blend In 和 Blend Out 区别
Engine\Source\Runtime\Engine\Classes\Animation\AnimMontage.h
/** Blend out option. This is only used when it blends out itself. If it's interrupted by other montages, it will use new montage's BlendIn option to blend out. */
UPROPERTY(EditAnywhere, Category=BlendOption)
FAlphaBlend BlendOut;
BlendOut
作用:只有当前Montage快结束时,且结束后恢复到待机动画(动画蓝图中的默认Pose),BlendOut
才会起作用,即在待机Pose和Montage快结束时的Pose之间进行融合,融合时长为BlendOut
。
如果Montage A在播放未结束时,Montage B开始播放,那么Montage A的BlendOut
不在生效,而是使用Montage B的BlendIn
。
如果某个Montage循环播放,那么BlendOut
不会起作用;如果某个Montage连续播放多次,那么BlendOut
只在最后一次播放将要结束时才生效。
如何指定动画的某个时间点作为起始播放点
UAnimInstance::PlaySlotAnimationAsDynamicMontage()
和UAnimInstance::Montage_Play()
都提供了参数InTimeToStartMontageAt
,表示从当前动作的哪个时间点还是融合,默认为0,即从第一帧开始融合。
常见问题
Montage Section切换无效的问题(2018-02-11)
keywords: UE4 Montage Section not working
问题现象:
SetNextSection在编辑器中运行正常,但是在打包版本中无效。
解决办法:
将SetNextSection替换为JumpToSection。
参考自:Montage unwanted section switching
https://answers.unrealengine.com/questions/172537/montage-unwanted-section-switching.html
如果Section的时间很短,需要将Blend In
和Blend Out
改小或者设置为 0(默认为0.25),否则 JumpToSection 无效果。
Montage 循环播放(2018-10-26)
如果用动画蓝图控制动画切换,直接用动画状态机去指定 AnimSequence 即可,不需要其他设置,因为动画蓝图会自动根据状态机去循环播放指定的 AnimSequence 。
如果用C++播放Montage,则需要注意以下细节:
-
如果是使用现成的Montage资源,需要将Montage设置为循环播放,方式如下:
打开Montage编辑窗口后,先鼠标单击位置1,然后鼠标单击位置2(都是鼠标左键单击) 然后Preview Section 进度条中就多了一个 X ,此时表示Montage为循环播放了 之后再执行ACharacter::PlayAnimMontage()
就可以自动循环播放指定Montage了。PlayAnimMontage() 只需执行一次即可。 -
如果动态创建Montage并播放,则需要将
LoopCount
设置为足够大,比如 9999。这样当前Montage就会循环播放:AnimInstance->PlaySlotAnimationAsDynamicMontage(AnimSeq, TEXT("DefaultSlot"), 0.25f, 0.25f, 1.f, 9999));
这种方式是否可行:不设置Montage循环播放,而是在Tick中不停检测上一次Montage的累计播放时长,当播完时马上执行下一次播放。答案是不可行!!实际效果是:两次的Montage播放之间会有短暂时间切换到Idle状态动画。
Montage 切换时没有融合的问题
原因:
Montage 的参数Blend In
被设置为了0。
解决办法:
将Montage 的参数Blend In
(Blend Option -> Blend In -> Blend Time)改成大于0,比如默认值0.25。
Crash on PlayAnimMontage
If a Character play a Montage which isn’t same Skeleton, client may crash.
MyCharacter->PlayAnimMontage(MyMtg, 1.f, FName("Sec01"));
人生如逆旅,我亦是行人。----苏轼《临江仙》