How to temporarily restrict third person character's movement? Like for walking on a plank

Hey Feureau,

Just my thoughts-

You can start by having a trigger volume over your “wood plank” object. When entered by the player, decrease the player movement speed or clamp the speed to what you would want the speed. You can also set the animation state when you enter the trigger so the new animations begin to be used. When exited you can set everything back to how it was (or whatever else you want to do).

So, this is something commonly found in third person games. You can move around freely, but then you go find a plank or a ledge on a cliff and you have your movement constrained (with special animation for such movement) for the given path.

Like in this example from Hellblade: - YouTube

You walk up to the start of the path, in this case a plank, and when you get to a certain distance, you restrict the movement to along the path of the plank. I need this plank to be custom path too, not just a straight line.

I looked into the spline based movement klonoa/pandemonium thing but it seems that only works for moving right or left as the movement is controlled by distance in spline to right and left movement input, while in such a free moving third person character game, your camera direction determines the movement input direction. Furthermore, there seems to be a bit of a snap when you force-move the character to the spline position. So, I guess this is not exactly how this sort of movement was done?

Any ideas on how to achieve this sort of thing?

Thanks

Use a trigger volume to change the animation state and utilize a custom collision mesh that blocks the player from moving away from your path.
The only downside to this solution is that your player couldn’t fall off your path (not sure if that’s good or bad in your case).

I implemented somthing similar in my own project using splines, and trigger volume.
When the player enters the volume I disable the input of the character and give it to my scriptedpath blueprint,

void AScriptVolume::OnTriggerEnter(UPrimitiveComponent * HitComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult){
MyCharacter->DisableInput(controller);
InputComponent = NewObject<UInputComponent>(this, UInputComponent::StaticClass(), "Input Component");
InputComponent->bBlockInput = bBlockInput;

if (InputComponent)
{
InputComponent->BindAxis("MoveForward", this, &AScriptVolume::AdvanceCharacterForward);
InputComponent->BindAxis("MoveRight", this, &AScriptVolume::AdvanceCharacterRight);

EnableInput(controller);
					}
void AScriptVolume::AdvanceCharacterForward(float value)
{
	APlayerController* controller = Cast<APlayerController>(MyCharacter->Controller);
	// find out which way is forward
	const FRotator Rotation = controller->GetControlRotation();
	const FRotator YawRotation(0, Rotation.Yaw, 0);

	// get forward vector
	const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);
	inputVector += FVector2D(Direction)*value;
}

void AScriptVolume::AdvanceCharacterRight(float value)
{

	APlayerController* controller = Cast<APlayerController>(MyCharacter->Controller);
	// find out which way is right
	const FRotator Rotation = controller->GetControlRotation();
	const FRotator YawRotation(0, Rotation.Yaw, 0);

	// get right vector 
	const FVector Direction = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::Y);
	inputVector += FVector2D(Direction)*value;
}

Then every tick I calculate the direction of the input and check if its in the valid direction, in my case based on the forward and backward vectors of my character. If the input is correct a calculate the time value it has to move to in the spline.

float angleF = UKharaUtils::CalcAngleBetweenVectors2D(FVector2D(MyCharacter->GetActorForwardVector()), inputVector);
			float angleB = UKharaUtils::CalcAngleBetweenVectors2D(FVector2D(-MyCharacter->GetActorForwardVector()), inputVector);
			if (inputVector.SizeSquared() > 0 && FMath::Abs(angleF) < INPUTANGLEMARGIN || FMath::Abs(angleB) < INPUTANGLEMARGIN)
			{
				float direction = 0;
				if (FMath::Abs(angleF) < INPUTANGLEMARGIN)
				{
					direction = 1;
				} else
				{
					direction = -1;
				}
				timeValue += GetWorld()->GetDeltaSeconds() * 1 / MovementDuration*inputVector.Size()*direction;
				SplineCharacterMovement(timeValue, direction);
			}
			inputVector = FVector2D::ZeroVector;

the method SplineCharacterMovement is incharged of calculating the movement of the character

 void AScriptVolume::SplineCharacterMovement(float val, int32 direction)
 {
     FVector SplineLocation = CharacterPath->GetLocationAtDistanceAlongSpline(val*CharacterPath->GetSplineLength(), ESplineCoordinateSpace::World);
     FVector SplineTangent = CharacterPath->GetTangentAtDistanceAlongSpline(val*CharacterPath->GetSplineLength(), ESplineCoordinateSpace::World);
     FRotator CharRot= MyCharacter->GetActorRotation();
     FRotator DestRot = CharRot;
     SplineTangent.Normalize();
     float DirTangentAngleDif = UKharaUtils::CalcAngleBetweenVectors2D(FVector2D(MyCharacter->GetActorForwardVector()), FVector2D(SplineTangent));
     DestRot.Yaw += DirTangentAngleDif;
     MyCharacter->StartAutomaticMovement(Khara->GetActorRotation(), DestRot, Khara->GetActorLocation(), SplineLocation, EKharaStatesEnum::KSE_Normal, Khara->GetKharaMovementComponent()->RotationRate.Yaw, CharacterMovementSpeed, true);
     MyCharacter->GetMovementComponent()->Velocity = SplineTangent*CharacterMovementSpeed*direction;
 }
  • CharacterPath: is my splinecomponent
  • StartAutomaticMovement: is an auxiliary function to learp my
    character from one point to another
  • CharacterMovementSpeed: is a variable set in the begin play based
    on the time it take the character to
    cross the spline and the length of
    the spline