Download

Multiplayer AddInputMovement with MoveToLocation

Hello all, I’m trying to replicate Diablo style movement in my game over multiplayer which is giving me a little bit of trouble at the moment. I was able to get the movement to work locally, but now as I try to get replication working I have run into some trouble. My goal is when the player releases the mouse button, the character will walk towards that point. However, when the player holds down the mouse button or presses the W key, the character will continually walk in the direction of the mouse. I have gotten the walk to destination when the player releases the mouse button working however the continually move towards the mouse is not working.

For the point and click movement, I followed this guide. I’m trying to add the move-forward movement like this:



void AMyPlayerController::SetupInputComponent() 
{
    Super::SetupInputComponent();

    // ...

    InputComponent->BindAxis("MoveForward", this, &AMyPlayerController::OnMoveForward);
}

void AMyPlayerController::OnMoveForward(float Value)
{
    FVector MouseLocation = GetWorldMouseLocation();

    // Stop move to location
    
    if (Value >= 1.f)
    {
        MoveForward(MouseLocation);
    }

}

void AMyPlayerController::MoveForward(const FVector MouseLocation)
{
    ServerMoveForward(MouseLocation);
}

bool AMyPlayerController::ServerMoveForward_Validate(const FVector MouseLocation)
{
    return true;
}

void AMyPlayerController::ServerMoveForward_Implementation(const FVector MouseLocation) 
{
    StopMovement();

    // Move towards the mouse
    GetPawn()->SetActorRotation(MouseLocation.Rotation());
    GetPawn()->AddMovementInput(MouseLocation, 1.f);
}


And



FVector AMyPlayerController::GetWorldMouseLocation() {
    int32 ViewportWidth, ViewportHeight;
    float MouseX, MouseY;

    GetViewportSize(ViewportWidth, ViewportHeight);

    // UE returns the mouse position relative to the center of the screen
    // To be able to calculate the mouse position from the top left, we must
    // substract the mouse position from half the size of the viewport
    ViewportWidth = floor(ViewportWidth / 2.f);
    ViewportHeight = floor(ViewportHeight / 2.f);

    GetMousePosition(MouseX, MouseY);

    // Calculate the actual mouse position into a vector
    FVector MousePositionVector(ViewportWidth - MouseX, ViewportHeight - MouseY, 0.f);

    // Don't remember why we have to do this...maybe for projection?
    MousePositionVector.Normalize(0.f);

    // Flip the X coordinate, to get to the top
    MousePositionVector *= FVector(1.f, 1.f, 0.f);

    // Rotate the vector to account for the camera rotation
    MousePositionVector = MousePositionVector.RotateAngleAxis(-45.f, FVector(0.f, 0.f, 1.f));

    return MousePositionVector;    
}

Basically the problem right now is that nothing happens when I trigger the MoveForward axis event

I may have solved this problem by just continually calling MoveToLocation while ‘W’ or the mouse button is held down. I don’t know what kind of impact this is going to have on performance or bandwidth, but so far it seems to be working okay. There is an issue where if the player walks holds the mouse down while up on a platform, the AI navigates off the platform, which could be annoying to the player. Also, the character mesh is not rotating towards movement direction.

Ok, I thought I would give an update just in case anyone else is interested in a solution. I removed the ProxyPlayer and am doing everything within the PlayerController. What I did to get this working was on mouse release [SetDestination], send the mouse hit location to the server. When running in multiplayer mode the NavigationSystem is only available on the server/host. I used the NavigationSystem on the server to get an array of points to the destination. After that, I send the array of points back to the client where it will cycle through them and apply AddMovementInput towards the relative vector (DestinationPoint - ActorLocation) in the Tick event. This also allows me to constantly apply AddMovementInput when the ‘W’ key or the mouse button is held down for continuous movement, giving me the correct functionality that I want. This works because AddMovementInput is automatically replicated to the server.

Unfortunately I’m still having issues with the StaticMesh attached to the Character not rotating properly, resulting in jittery rotation within 10 degrees. If anyone has some insight as to why this isn’t working, I would be grateful.



void AMyPlayerController::PlayerTick(float Delta)
{
    Super::PlayerTick(Delta);

    if (DestinationCounter > -1) 
    {
        
        //FVector tempdest = FVector(DestinationPoint.X, DestinationPoint.Y, 0.f) - FVector(GetPawn()->GetActorLocation().X, GetPawn()->GetActorLocation().Y, 0.f);
        FVector tempdest = DestinationPoint - GetPawn()->GetActorLocation();
        //GetPawn()->GetMovementComponent()->AddInputVector(tempdest, false);
        //GetPawn()->SetActorRotation(tempdest.Rotation());
        GetPawn()->AddMovementInput(tempdest, Delta);

        const float Distance = FVector::Dist(DestinationPoint, GetPawn()->GetActorLocation());
        if (Distance <= 64.f)
        {
            if (DestinationCounter < CurrentDestPoints.Max())
            {
                DestinationPoint = CurrentDestPoints[DestinationCounter];
                GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("DestinationPoint: x: %f, y: %f, z: %f"), DestinationPoint.X, DestinationPoint.Y, DestinationPoint.Z));
                DestinationCounter++;
            }
            else
            {
                DestinationCounter = -1;
            }
        }
    }
}

EDIT: Okay, somehow I got character rotation to work perfectly…not sure exactly how or why it works. All I did was instead of adding the StaticMesh in the Blueprint, I added it as a property in the C++ code.