How do you Move a player to a Location in Multiplayer

So there are are plenty of resources on moving a player to a location you click on in single player. This does not work in Multiplayer. So to reproduce this start simple:

Default setup:

  1. Create a Top down Game
  2. Play in Dedicated Server Mode

Observe:

  1. Click to move the player.
  2. Observe player does not move.

This works fine n Single Player but fails in multiplayer. Also holding the move down works because of the MovementComponent Input replication.

Multiplayer Setup:
So problem with first setup according to the internet is SimpleMoveToLocation needs to happen on the server. Easy enough.

In the Topdown Example (specifically C++) go to the TopDownController::OnSetDestinationReleased() This is where the SimpleMoveToLocation is being fired.

Change it to:

void ATopDownPlayerController::OnSetDestinationReleased()
{
	// If it was a short press
	if (FollowTime <= ShortPressThreshold)
	{
		if(this->GetLocalRole() <  ENetRole::ROLE_Authority)
		{
			// We move there and spawn some particles
			this->SERVER_MoveToLocation(CachedDestination);
		}
		else
		{
			UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, CachedDestination);
		}

		UNiagaraFunctionLibrary::SpawnSystemAtLocation(this, FXCursor, CachedDestination, FRotator::ZeroRotator, FVector(1.f, 1.f, 1.f), true, true, ENCPoolMethod::None, true);
	}

	FollowTime = 0.f;
}

Also add the function SERVER_MoveToLocation

void ATopDownPlayerController::SERVER_MoveToLocation_Implementation(FVector const & newLocation)
{
	this->CachedDestination = newLocation;
	// We move there and spawn some particles
	UAIBlueprintHelperLibrary::SimpleMoveToLocation(this, CachedDestination);
}

Again player just stands still and does not move. My only theory on this is that the MovementComponet itself is stopping the movement.

Going to try and bump this. Does no one actually know what is up with this??? It seems like it could be a problem.

Well after years I have solved this problem in two methods. These both worked in Multiplayer.

Method 1:
The first method I figured out was to create an AIPlayerController based on the AIController that can swap in when you want to make an automated move. There are some considerations to take with this as you will need to override OnUnPossess() of the PlayerController so that it does not reset the ViewTarget and maintains it. This worked fairly well, you’ll have to send the point coordinate up to the server and make the AIController move the player that way.

Method 2:
The easier method is to directly take over the input of the player and use the Navigation System to plot a course.

UNavigationPath * tpath = nullptr;
UNavigationSystemV1 * navSys = UNavigationSystemV1::GetCurrent(world);

tpath = navSys->FindPathToActorSynchronously(world, this->PawnPlayer->GetActorLocation(), this->TargetActor);

You can use the above code with a Nav Mesh to find the desired path to the target. The catch to this is, you will need to replicate the path points on the server down to the client which is easy enough to accomplish. Then from your pawns current position calculate the relative movement direction to get the player to move toward each navigation point.

// Calcualte the differnece between the target location and the player
FVector const location = this->PawnPlayer->GetActorLocation();
FVector diffLocationToTarget = targetLocation - location;
diffLocationToTarget.Z = 0.0f;
diffLocationToTarget.Normalize();

// Get the controler looking rotation
FRotator const rotation = this->playerController->GetControlRotation();
FRotator const yawRotation(0.0f, rotation.Yaw, 0.0f);

// Determine the forward direction and right direction we want to move
FVector const forwardDirection = FRotationMatrix(yawRotation).GetUnitAxis(EAxis::X);
FVector const rightDirection = FRotationMatrix(yawRotation).GetUnitAxis(EAxis::Y);

// Use Inverse Transform to get what the input directions should be
FTransform transform;
transform.SetRotation(yawRotation.Quaternion());
FVector const inputDir = transform.InverseTransformVector(diffLocationToTarget);
	
// Apply the input directions we want to use for movement.
this->PawnPlayer->AddMovementInput(forwardDirection, inputDir.X);
this->PawnPlayer->AddMovementInput(rightDirection, inputDir.Y);