【UE4】Enable / Disable RootMotion at RunTime in Montage

MeshComp->GetAnimInstance()->SetRootMotionMode(ERootMotionMode::IgnoreRootMotion);

一、问题描述

  UE4 中每个 montage 都有一个 Slot(可以有多个),每个Slot 里边可以由多个 Segments,每个Segments 是一段动画,多个 Segments 代表这个 Montage 动画是由多段动画组成的,如下所示:
在这里插入图片描述
  也有可能整个Montage就一段动画,如下:
在这里插入图片描述
  点击对应的动画条,可以再 AM_xx 的窗口的 Details 面板中看到这个 Animation Segment,可以打开对应 Animation Reference:
在这里插入图片描述
  每个动画有自己单独的 RootMotion 的选项:
在这里插入图片描述
  如果勾选了 EnableRootMotion,代表由 RootMotion 来控制角色移动和转向,玩家输入的移动和转向指令就不生效了(详见 UCharacterMovementComponent::PerformMovement):

void UCharacterMovementComponent::PerformMovement(float DeltaSeconds)
{
    
    
	// ...
	
	// Apply Root Motion to Velocity
	if( CurrentRootMotion.HasOverrideVelocity() || HasAnimRootMotion() )
	{
    
    
		// Animation root motion overrides Velocity and currently doesn't allow any other root motion sources
		if( HasAnimRootMotion() )
		{
    
    
			// Convert to world space (animation root motion is always local)
			USkeletalMeshComponent * SkelMeshComp = CharacterOwner->GetMesh();
			if( SkelMeshComp )
			{
    
    
				// Convert Local Space Root Motion to world space. Do it right before used by physics to make sure we use up to date transforms, as translation is relative to rotation.
				RootMotionParams.Set( ConvertLocalRootMotionToWorld(RootMotionParams.GetRootMotionTransform()) );
			}
		}
	}
	
	// ...
}

  问题:如果Montage 由多段动画组成,其中有的动画 EnableRootMotion,有的没有,最终整个Montage 过程中都会 EnableRootMotion,导致想在其中一段动画过程中可以移动无法实现
  分析:
  如果技能 Montage 中只有一个 Segment,且这个 Anim Segment 的 RootMotion 开启了,那么在整个技能过程中,都会启用 RootMotion。
  如果 Montage 中有多个 Segment,但只有一个 Segment 的动画启用了 RootMotion,那么应该只有对应段启用,其他 segment 的时候不启用 RootMotion,即可以玩家控制移动。但是,在 UE4 中,AnimMontage 在判断是否有 RootMotion 的时候,判断代码如下(Animation/AnimMontage.cpp):

bool UAnimMontage::HasRootMotion() const
{
    
    
	for (const FSlotAnimationTrack& Track : SlotAnimTracks)
	{
    
    
		if (Track.AnimTrack.HasRootMotion())
		{
    
    
			return true;
		}
	}
	return false;
}

  其中 FSlotAnimationTrack 就是一个 Slot,目前项目中所有技能都只有一个,Track 里边就是 Segments,如果由三段动画组成,那就有三个 Segment,每个 Track 判断是不是有RootMotion 的代码如下(Animation/AnimCompositeBase.cpp):

bool FAnimTrack::HasRootMotion() const
{
    
    
	for (const FAnimSegment& AnimSegment : AnimSegments)
	{
    
    
		if (AnimSegment.bValid && AnimSegment.AnimReference && AnimSegment.AnimReference->HasRootMotion())
		{
    
    
			return true;
		}
	}
	return false;
}

  也就是只要有一个 Segments 有 RootMotion,那整个 Track 就有 RootMotion!
  对于每一个 Segments 的 AnimReference 判断 HasRootMotion,就是直接返回 bEnableRootMotion(Animation/AnimSequence.h)。

virtual bool HasRootMotion() const override {
    
     return bEnableRootMotion; }

  这个变量是在 A_xx 文件中的选项,即上述动画 RootMotion 的选项。
  也就是只要有一个 Segments 的动画勾选了 EnableRootMotion,那整个 Montage Track 就有 RootMotion!

二、解决方案

  所以我们需要在适当的时候开启/关闭 RootMotion,让玩家可以控制角色移动和转向。
  功能和 AnimNotifyState 一起做的,所以直接写在 Notify 功能里:在 NotifyBegin 的时候调用

MeshComp->GetAnimInstance()->SetRootMotionMode(ERootMotionMode::IgnoreRootMotion);

  即忽略 RootMotion,然后在 NotifyEnd 的时候重置回来即可。

MeshComp->GetAnimInstance()->SetRootMotionMode(ERootMotionMode::RootMotionFromMontagesOnly);

  这样就可以做到让多个 segment 组成的 Montage(其中有 EnableRootMotion的),其中一部分可以自主控制移动,一部分可以通过 RootMotion 控制移动的效果。

猜你喜欢

转载自blog.csdn.net/Bob__yuan/article/details/109380199