Bone transform at certain time/key

So I finally got around to verifying this. Here are my findings, but anyone feel free to add/correct any of this:

If you are looking at an UAnimSequence (whether you have the anim sequence as a UPARAMETER or playing it directly on a Blueprint (or anim blueprint)), then it’s fairly straight forward:

	int BoneIdx = GetMesh()->GetBoneIndex(TEXT("root"));
	if(BoneIdx != INDEX_NONE)
	{
		int32 const TrackIndex = MyAnimSequence->GetSkeleton()->GetAnimationTrackIndex(BoneIdx, MyAnimSequence);
		MyAnimSequence->GetBoneTransform(BoneTrans, TrackIndex, DesiredTime, true);
	}

If you’re looking an an AnimMontage, then it’s more difficult because one montage may have several sequences. If you’re looking for a value in one of the sequences (at a particular time):

FName SlotNodeName = "DefaultSlot";
int SlotIndex = -1;
for (int32 I = 0; I < MyMontage->SlotAnimTracks.Num(); ++I)
{
	if (MyMontage->SlotAnimTracks[I].SlotName == SlotNodeName)
	{
		SlotIndex = I;
		break;
	}
}

if (SlotIndex >= 0)
{
	FAnimSegment* Result = nullptr;
	for (FAnimSegment& Segment : MyMontage->SlotAnimTracks[SlotIndex].AnimTrack.AnimSegments)
	{
		if (Segment.AnimStartTime <= DesiredTime && DesiredTime <= Segment.StartPos + Segment.GetLength())
		{
			Result = &Segment;
			break;
		}
	}

	if (Result != nullptr)
	{
		FTransform BoneTrans;
		int BoneIdx = GetMesh()->GetBoneIndex(TEXT("root"));
		UAnimSequence *AnimSeq = Cast<UAnimSequence>(Result->AnimReference);
		int32 const TrackIndex = AnimSeq->GetSkeleton()->GetAnimationTrackIndex(BoneIdx, AnimSeq);
		AnimSeq->GetBoneTransform(BoneTrans, TrackIndex, DesiredTime, true);
	}
}

You’ll notice I’m making assumptions about what bone to use (“root” in my example) and didn’t error check that BoneIdx != INDEX_NONE. Also notice the part that finds the appropriate segment is basically a copy of a function that already exists. If you do:

FAnimSegment* Segment = MyMontage->SlotAnimTracks[SlotIndex].AnimTrack.GetSegmentAtTime(DesiredTime);

it should give the same result, except that you’ll get a link error if you add that call because that function is not part of ENGINE_API (if you’re working from source, you can fix that).

On a Montage you can also get a list of all the sequences used by calling:

TArray<UAnimSequence *> AnimSeqs;
MyMontage->GetAllAnimationSequencesReferred(AnimSeqs)

you can iterate looking for the right one and then get the bone transform from it.

If you’re working with a montage but you’re not looking for a specific animation sequence but rather the end result pose (however many sequences are included in the montage), then this is the way:

	FName SlotNodeName = "DefaultSlot";
	float DesiredAnimTime = 0.0f;
	FCompactPose Pose;
	Pose.SetBoneContainer(&GetMesh()->GetAnimInstance()->RequiredBones);
	FBlendedCurve Curve;
	Curve.InitFrom(GetMesh()->SkeletalMesh->Skeleton);

	FAnimExtractContext ExtractionContext(DesiredAnimTime, false);

	MyMontage->GetAnimationData(SlotNodeName)->GetAnimationPose(Pose, Curve, ExtractionContext);
	FCompactPoseBoneIndex BoneIndex(0);
	RetVec = Pose[BoneIndex].GetTranslation(); // or GetRotation() or GetScale3D()

One hitch here is that “GetAnimationData” and “GetAnimationPose” are not part of ENGINE_API (so you’ll get unresolved symbol errors), which you can fix if you’re working from source, but if you’re not, you can make your own local version of those functions (actually GetAnimationPose calls another GetAnimationData part of the FAnimSegment class which you’ll also need to replicate).

EDIT: All the code above give you the transform for the bone in bone space. If you need it in component space, then you have to multiply down the hierarchy until you get to the root. On the last piece of code, for example, FCompactPose has the index of the parent bone: Pose->GetParentBoneIndex()

You can get the parent on any of them using the same mechanic. On the first piece of code for a sequence, MyAnimSequence->GetBonePose will get you the pose that you can then use to get the parent.

1 Like