Download

How to get a bone location for the first frame of an AnimMontage?

Hello,

In my game, I need to attach an actor to the bone of a skeletal mesh. Depending on the situations, the bone I attach the actor to is placed differently, depending on the section I wish to play in the montage.

I would like, before I play the montage, to know what will be the position of the bone in world space, so I can make the attached actor move toward the correct location.

Do you know how I could achieve that?

Thanks!

Hey you can use:
USkinnedMeshComponent::GetSocketByName(FString);
Then USkeletalMeshSocket::GetSocketLocation(USkeletalMeshComponent *)

I dont have the code infront of me so i can drop a code snippet for you later today if you need/ want it.
Happy Coding.

I tried to use MeshComponent->GetBoneLocation() but it returns the current location, not the location of the first frame (even when I call this function just after Montage_Play on the animinstance. I guess the location is updated in the next frame)

I think the functions you gave to me do the same thing

Ok i need to be cerful now so i don`t give you the wrong information here.
But am 99% sure that this should return the location of the socket.

I will be off work in 2 houers if no smart minds been here before that i will post a snip of my code.
That is use to snap weapons to the correct socket location of a player.

If you look at the API you can see it has a separate member for Local Space.
So the Location you get from the method above should be World Space.
USkeletalMeshSocket::
GetSocketLocalTransform(), USkeletalMeshSocket::GetSocketTransform

Btw you can do this same thing for regular boens and they don`t have to be sockets you added to the skeleton just a FYI. :slight_smile:

Thanks for your answer, but as I suspected, I have the same result.

The problem is that despite playing the montage, the bones will be transformed only on the next tick.

I think I could manage to get the info with something like:


int bone_index = MeshComponent->GetBoneIndex( bone_name );
                FTransform transform;
                Cast< UAnimSequence >( Montage->SlotAnimTracks 0 ].AnimTrack.GetSegmentAtTime( 0.0f )->AnimReference )->GetBoneTransform( transform, bone_index, 0.0f, true );
                FVector v = transform.GetLocation();

But the linker complains it can’t find FAnimSegment

Ahe i think i misunderstod you a bit.
Have you had a look at UAnimSequence::GetBonePose(,) in UAnimSequence:: ?

Not sure but i think it may be what you are looking for.

The correct code to have the transform of the bone is :


int bone_index = MeshComponent->GetBoneIndex( bone_name );
FTransform transform;
Cast< UAnimSequence >( Montage->SlotAnimTracks 0 ].AnimTrack.AnimSegments 0 ].AnimReference )->GetBoneTransform( transform, bone_index, 0.0f, true );

Now I need to convert this in world space. But the problem is that the FTransform I get this way is relative to the parent of the bone. So I need to figure out how to get the correct transforms…

Hello,

The code you’re looking for is this.

void UAnimSequence::GetBonePose(FTransformArrayA2& OutAtoms, const FBoneContainer& RequiredBones, const FAnimExtractContext& ExtractionContext) const

This returns all transform of bone data in their parent space and there you can calculate exact bone you’d like. You also have to multiply ComponenToWorld to get worldspace since that will be still component space.

If you’re only wondering about root motion, you can use this function. That function also can be used to

FTransform UAnimSequence::ExtractRootMotion(float StartTime, float DeltaTime, bool bAllowLooping) const

>>In my game, I need to attach an actor to the bone of a skeletal mesh. Depending on the situations, the bone I attach the actor to is placed differently, depending on the section I wish to play in the montage.

Generally this isn’t the way we do. Since you never would like player to teleport, we give time to blend to the target location. We use target bone and while playing we makes player to interpolate to the target location.

Or you can use some prep animation that has correct bone transform to move to the location and when that’s done, you can transition to the animation that has to be in that position.

–Lina,

Thanks for your answer.

I was able to achieve my need with the function : UAnimSequence::GetBoneTransform, which I call recursively from the bone up to the root bone. Then I multiply the transform by the ComponentToWorld. I know for sure there will be no root motion or animation retargeting on the assets, as it is not a player, but a basket ball hoop.

There is no teleport in what I do: the attached actor is a basket ball, which I attach to a dedicated bone on a basket skeletal mesh. I have animations for succeeded and failed shots. Depending on the animations, the starting point of the bone of the ball is not located at the same place (around the rim, or on the board).

When a player shoots the ball, I compute the trajectory of the ball using ballistics equations. That’s why I want to have the location of the ball bone before I play the animation, because I use this location as the destination point of the trajectory.

When the trajectory ends, I know the ball is at the correct position, and only then I attach the ball to the ball bone of the hoop, and play the montage, with the blending doing its job of interpolating the ball nicely.

I forgot to put the code I ended with, for those interested:



void ABBHoop::Foo()
{
    UAnimMontage
        * selected_montage = ...;
    const TArray<FAnimSegment>
        & segment_table = selected_montage->SlotAnimTracks 0 ].AnimTrack.AnimSegments;
    const FName
        & section_name = selected_montage->GetSectionName( selected_montage_segment_index );
    const UAnimSequence
        & anim_sequence = *Cast< UAnimSequence >( segment_table selected_montage_segment_index ].AnimReference );

    FVector
        position = GetBallBoneFirstFrameMontageLocation( anim_sequence, FName( "XXX" ) );
}

FVector ABBHoop::GetBallBoneFirstFrameMontageLocation( 
    const UAnimSequence & anim_sequence,
    const FName & target_bone_name
    ) const
{
    FName
        bone_name = target_bone_name;
    FTransform
        global_transform;

    global_transform.SetIdentity();

    do
    {
        const int
            bone_index = MeshComponent->GetBoneIndex( bone_name );
        FTransform
            bone_transform;
        int
            track_index;

        for ( track_index = 0; track_index < anim_sequence.TrackToSkeletonMapTable.Num(); ++track_index )
        {
            if ( anim_sequence.TrackToSkeletonMapTable track_index ].BoneTreeIndex == bone_index )
            {
                break;
            }
        }

        if ( track_index == anim_sequence.TrackToSkeletonMapTable.Num() )
        {
            break;
        }

        anim_sequence.GetBoneTransform( bone_transform, track_index, 0.0f, true );
        global_transform *= bone_transform;

        bone_name = MeshComponent->GetParentBone( bone_name );
    } while ( bone_name != NAME_None );

    global_transform.SetToRelativeTransform( MeshComponent->GetComponentTransform() );

    return global_transform.GetTranslation();
}

Thanks a lot!

I know this is an old thread, but I found myself wanting to do this recently, and I didn’t have a Skeletal mesh to grab it from.

I made a BP library Function like so:



FTransform UDQBlueprintFunctionLibrary::GetBoneTransFromMontage(UAnimMontage* InMontage, FName BoneName, float InTime)
{    
    FTransform transform = FTransform();
    if (InMontage)
    {
        int bone_index = 0;

        USkeletalMesh* SkelMesh = InMontage->GetSkeleton()->GetPreviewMesh(true);
        if (SkelMesh)
        {
            bone_index = SkelMesh->RefSkeleton.FindBoneIndex(BoneName);
            UAnimSequence* Anim = Cast<UAnimSequence>(InMontage->SlotAnimTracks[0].AnimTrack.AnimSegments[0].AnimReference);

            if (bone_index >= 0)
            {
                Anim->GetBoneTransform(transform, bone_index, InTime, true);
            }
        }
    }
    return transform;
}


It’s a bit shorter. Might be helpful?