In a previous post on character movement I mentioned that it would be better if I changed the way ladder climbing was implemented. Instead of trying to utilize the falling physics state, a custom movement component with custom physics states should be used.

Well, I finally got around to doing just that. I also added some changes to have the movement be fully replicated for multiplayer.

Source code examples for the project are available here.

To start, movement is no longer calculated in the player’s Tick() function. Now all movement input is done during the input events for movement which correspond to the player’s MoveForward() and MoveRight() functions.

void APaperPlayer::MoveForward(float Value)
{
    CurrentVelocity.X = FMath::Clamp(Value, -1.0f, 1.0f);

    if((Controller != NULL) && (Value != 0.0f))
    {
        if(bIsOnLadder)
        {
            MovePlayerOnLadder();
        }
        else
        {
            // Get forward vector
            const FRotator Rotation = Controller->GetControlRotation();
            const FRotator YawRotation(0, Rotation.Yaw, 0);
            const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

            AddMovementInput(Direction, Value * GetCurrentSpeedScale());
        }
    }
}

If the character is on the ladder, the custom ladder movement direction code is still the same as before. The big change comes in with how the movement is applied to the character. Instead of trying to overload the falling physics with adding impulses, now the character just calls AddMovementInput() like for regular movement.

To set this up, whenever the character is overlapping the ladder a custom physics mode is enabled.

MovementComponent->SetMovementMode(MOVE_Custom, static_cast<int>(ECustomMovementType::Climb));

To accomplish this required a few steps.

  1. A new CharacterMovementComponent was created.
  2. The new movement component was added as the default for the player class.
  3. The custom movement mode was identified and created.
  4. The custom movement physics was implemented.

Creating the CharacterMovementComponent was simply a matter of creating a derived class in the engine.

Setting up the player to use the new component as the default required modifying how the constructor was handled. The constructor containing the FObjectInitializer reference had to be used in order to override the default subclass for the movement component.

APaperPlayer::APaperPlayer(const class FObjectInitializer& ObjectInitializer)
        : Super(ObjectInitializer.SetDefaultSubobjectClass<UPaperCharacterMovementComponent>(ACharacter::CharacterMovementComponentName))

Next up was identifying the custom physics mode that was going to be used. In this case I called it “Climb” and defined it inside of an enum created in the custom movement component’s header file.

UENUM(BlueprintType)
enum class ECustomMovementType : uint8
{
    Climb UMETA(DisplayName="Climb")
};

The only thing left after that was to actually implement the custom physics used to simulate climbing a ladder. To do this there are a few functions to override.

Overriding OnMovementModeChanged() will allow you to do any custom setup required when changing movement modes. I didn’t have anything that needed to be added here so nothing was done in this case.

Overriding PhysCustom() is a little more important. This function is called each tick when you are in a custom physics mode. There are equivalent functions for Falling, Walking, Flying, etc. but I didn’t want to modify any of those just yet. Simply call the base class to perform anything it has to do for custom physics modes, then perform your new custom physics corresponding to the custom movement mode that is set.

void UPaperCharacterMovementComponent::PhysCustom(float deltaTime, int32 Iterations)
{
    Super::PhysCustom(deltaTime, Iterations);

    switch(CustomMovementMode)
    {
        case static_cast<int>(ECustomMovementType::Climb):
            PhysCustomClimb(deltaTime, Iterations);
            break;

        default:
            break;
    }
}

Inside of PhysCustomClimb() I was able to implement the physics I was after by copying the physics used for flying inside of PhysFlying() in CharacterMovementComponent.cpp. The only thing I tweaked was using the GroundFriction instead of 0.5 * FluidFriction when calculating the new velocity. This gave me exactly what I was after and also provides a simple way to have custom movement be fully replicated for multiplayer.

The following gif was created to show the new replicated movement in action. The top screen is the game server. The bottom screen is a game client. As you can see, both the server and client characters’ movements are fully replicated, including the new custom physics mode for ladder climbing.

That’s all for this post. I’m thinking next I’ll take a dive through some custom network replication using C++. Maybe spawning and moving actors from both the client and game server. We’ll see what I come up with.

Until next time.

1 Comment »

Leave a Reply