This development blog post is for the project “And I Must Scream” at Stitched Mouth Studio. Any suggestions or comments are welcome!’

With forward kinematics (FK), our character model will be floating in the air when going up and down the stairs. Especially in a side-scroller view, this can be noticed very easily considering that our levels consist quite a lot of stairs.

To achieve this, I looked through multiple different inverse kinematics (IK) implementations online and convert the blueprint solutions that I found into C++ codes to use in our project. I have all the variables initialize in C++ and expose them to use in animation blueprint.

This is what standing on stairs looked like before we applied IK.

Max_FootWithoutIK.PNG

This is what standing on stairs look like after applying IK.

Max_FootWithIK.PNG

The snippets below are the variables in C++, all with:
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = “IK”)
I removed them so it doesn’t get super long.

    UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "IK")
    bool m_enableIKAnimation;
    bool m_usingFootIK;

    // To scale effectors back according to mesh scale
    float m_meshZScale;

    // Variables to expose to animation blueprints.
    float m_animIKHipOffset;
    float m_animIKRightEffector;
    float m_animIKLeftEffector;

    float m_transformZScale;
    float m_IKCapsuleHalfHeight;

    // Extra distance at the bottom of player capsule.
    float m_IKTraceDistance;

    // Socket names set up in the skeleton
    FName m_leftFootSocketName;
    FName m_rightFootSocketName;

    float m_IKOffsetLeftFoot;
    float m_IKOffsetRightFoot;
    float m_IKOffsetHip;
    float m_IKAdjustFootOffset;

    // Interpolation speed
    float m_IKInterpSpeed;
    float m_IKHipInterpSpeed;

Inside the tick function, I check whether we want to use IK animation right now (sometimes we turn it off for other testings), and updates the IK offsets.

Inside tick function:

    this->m_dt = DeltaSeconds;

    if (this->m_enableIKAnimation) {
        if (this->m_usingFootIK) {
            this->IKProcessing(DeltaSeconds);
        }
    }

IKProcessing function simply check that if the character is moving, it doesn’t interpolates the IK values back to 0.0f. If the character is not moving, it updates the IK values.

void AAndIMustScreamCharacter::IKProcessing(float i_dt)
{
    // if the character is moving
    if (GetVelocity().Size() > 0.f) {
        IKResetVars();
    }
    else {
        IKUpdate();
    }
}

Below is the main part of the code to calculate offsets for IK. I will go through all of the functions used right here in below. Basically, it calculates the feet offsets and the hip offset, use them to adjust character’s capsule half height. Lastly, it checks whether we have interpolated the effector values used in animation blueprint close enough to our target values and stop updating if that’s true.

void AAndIMustScreamCharacter::IKUpdate()
{
    if (this->m_usingFootIK) {
        // run feet raycast to calculate offset
        this->m_IKOffsetRightFoot = this->IKFootTrace(this->m_IKTraceDistance, this->m_rightFootSocketName);
        this->m_IKOffsetLeftFoot = this->IKFootTrace(this->m_IKTraceDistance, this->m_leftFootSocketName);

        // update hip offset
        this->m_IKOffsetHip = FMath::Min(this->m_IKOffsetLeftFoot, this->m_IKOffsetRightFoot);
        this->m_IKOffsetHip = this->m_IKOffsetHip > 0.f ? 0.f : this->m_IKOffsetHip; // set to 0 if larger than 0
        this->IKUpdateFootOffset(this->m_IKOffsetHip, this->m_animIKHipOffset, this->m_IKHipInterpSpeed);
        this->IKUpdateCapsuleHalfHeight(this->m_IKOffsetHip, false);

        // update feet effector according to hip offset
        this->IKUpdateFootOffset(this->m_IKOffsetLeftFoot - this->m_IKOffsetHip, this->m_animIKLeftEffector, this->m_IKInterpSpeed);
        this->IKUpdateFootOffset(this->m_IKOffsetRightFoot - this->m_IKOffsetHip, this->m_animIKRightEffector, this->m_IKInterpSpeed);

        // disable IK when nearly Equal
        bool rightNearlyEqual = FMath::IsNearlyEqual(this->m_IKOffsetRightFoot - this->m_IKOffsetHip, this->m_animIKRightEffector, 1.f);
        bool leftNearlyEqual = FMath::IsNearlyEqual(this->m_IKOffsetLeftFoot - this->m_IKOffsetHip, this->m_animIKLeftEffector, 1.f);

        if (rightNearlyEqual && leftNearlyEqual) {
            this->m_usingFootIK = false; // disable using IK
        }
    }
}

The IKFootTrace function is probably the most important part of this whole system. This function shoots a line trace from the socket to check if it collides with other objects and calculate the offset to adjust the feet position and capsule height.

float AAndIMustScreamCharacter::IKFootTrace(float i_traceDistance, FName i_socketName)
{
    FVector actorLocation = this->GetActorLocation();
    FVector socketLocation = this->GetMesh()->GetSocketLocation(i_socketName);

    FVector starVec = FVector(socketLocation.X, socketLocation.Y, actorLocation.Z);
    FVector endVec = FVector(socketLocation.X, socketLocation.Y, actorLocation.Z - (i_traceDistance + this->m_IKCapsuleHalfHeight));

    UWorld* world = GetWorld();
    if (world != nullptr) {
        FCollisionQueryParams IK_TraceParams = FCollisionQueryParams(FName(TEXT("IK_Trace")), true, this);
        IK_TraceParams.bTraceComplex = true;
        IK_TraceParams.bTraceAsyncScene = true;
        IK_TraceParams.bReturnPhysicalMaterial = false;

        //Re-initialize hit info
        FHitResult Trace_Hit(ForceInit);

        //call GetWorld() from within an actor extending class
        bool validHit = GetWorld()->LineTraceSingleByChannel(
            Trace_Hit,         //result
            starVec,        //start
            endVec,         //end
            ECC_Visibility, //collision channel
            IK_TraceParams
        );

        if (!validHit) {
            return 0.f;
        }
        return (Trace_Hit.Location - Trace_Hit.TraceEnd).Size() - this->m_IKTraceDistance + this->m_IKAdjustFootOffset;
    }
    return 0.f;
}

IKUpdateFootOffset is just to interpolate current effector value to the target value (which can be the feet offsets or the hip offset).

FORCEINLINE void AAndIMustScreamCharacter::IKUpdateFootOffset(float i_targetVal, float & i_effectorVar, float i_interpSpeed)
{
    i_effectorVar = FMath::FInterpTo(i_effectorVar, i_targetVal, this->m_dt, i_interpSpeed);
}

IKUpdateCapsuleHalfHeight just does what the name says, it updates the charater’s capsule half height in order to ground the character’s feet.

FORCEINLINE void AAndIMustScreamCharacter::IKUpdateCapsuleHalfHeight(float i_hipShift, bool i_resetVal)
{
    float heightTarget = i_resetVal ? this->m_IKCapsuleHalfHeight : this->m_IKCapsuleHalfHeight - (FMath::Abs(i_hipShift) / 2.f);

    float nextVal = FMath::FInterpTo(this->GetCapsuleComponent()->GetScaledCapsuleHalfHeight(), heightTarget, this->m_dt, this->m_IKHipInterpSpeed);

    this->GetCapsuleComponent()->SetCapsuleHalfHeight(nextVal);
}

ResetVars is used when the character is moving, it interpolates the effector values back to 0.0f and restore the character’s capsule half height.

void AAndIMustScreamCharacter::IKResetVars()
{
    this->IKUpdateFootOffset(0.f, this->m_animIKLeftEffector, this->m_IKInterpSpeed);
    this->IKUpdateFootOffset(0.f, this->m_animIKRightEffector, this->m_IKInterpSpeed);
    this->IKUpdateFootOffset(0.f, this->m_animIKHipOffset, this->m_IKHipInterpSpeed);
    this->IKUpdateCapsuleHalfHeight(0.f, true);
}

In the next post, we will show how we set up the animation blueprints and actually make use of the two bone IK nodes with our values aquired in C++ codes.