Download

SetActorLocation Jitter for PlayerCharacter in UE4 Multiplayer

I’m trying to implement a basic dash attack in a small multiplayer project I’m working on. This is my first multiplayer project and it’s a lot for me to take in. I’ve tried different methods and looked up plenty of guides, and while I’ve found multiple methods that work more or less in single player, I haven’t found anything that works in multiplayer for me.

My current implementation involves directly using SetActorLocation. I take the current player position, add a fixed amount to it, then set their location to the new position. In PIE, this functionality works okay, but there’s occasionally a jitter. In simulated network play (non-PIE), an extreme jitter almost always occurs.

Jitter in the default online subsystem

ezgif.com-gif-maker

I have a couple theories why this is happening. The first is that using SetActorLocation is interfering with usual player movement replication. It’s expecting the player to be in a particular position because of the default physics, and it’s correcting the position because it thinks the movement shouldn’t be possible. The second is that the player’s Punch_Start_Pos when local Local_AttackMovement( ) is activated is different from the Server’s Punch_Start_Pos when Server_AttackMovement( ). This would make sense due to the lag between the servers. The third is that SetActorLocation is not actually replicating. I don’t know why it wouldn’t be replicating, but the dash attack actually working might just be a fluke.

Here’s the code that I’ve made so far. It’s probably nowhere near perfect, any help is appreciated:

// When attack is pressed...

void ABrawlTownCharacter::AttackMovement(float Speed, float DeltaTime)
{

	if ((Controller != NULL) && (Speed != 0.0f) && Should_Move_Punch)
	{
		// find out which way is forward
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		// get forward vector
		FVector Direction_X = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

		Punch_End_Pos = Punch_Start_Pos + (Direction_X * 150);

		// Calculates the rotation needed for the player to face the direction they are heading.
		FRotator New_Rotation = UKismetMathLibrary::FindLookAtRotation(Punch_Start_Pos, Punch_End_Pos);

		Punch_End_Pos.Z = GetActorLocation().Z;

		SetActorLocation(Punch_End_Pos, true);
		SetActorRotation(New_Rotation);

	}

}

void ABrawlTownCharacter::Local_AttackMovement(float Speed, float DeltaTime)
{
	AttackMovement(Speed, DeltaTime);
	Server_AttackMovement(Speed, DeltaTime);
}

bool ABrawlTownCharacter::Server_AttackMovement_Validate(float Speed, float DeltaTime)
{
	return true; // Add validation stuff later
}

void ABrawlTownCharacter::Server_AttackMovement_Implementation(float Speed, float DeltaTime)
{
	AttackMovement(Speed, DeltaTime);
}

Jitter is server correction. You’re not telling the server to explicitly do the movement.

Set location locally for client responsiveness, but also RPC the server to do it as well.

1 Like

Hi Rev, thanks for the input! I’m not really sure I understand your advice in this context though.

I realized I wasn’t clear with the first post. This is how the script goes:
1 ) Pressing the right button activates Local_AttackMovement( )
2 ) Local_AttackMovement( ) first runs AttackMovement( ) on the local client.
3 ) Local_AttackMovement( ) then calls Server_AttackMovement( ) which is supposed to run on the server.
4 ) Server_AttackMovement( ) runs AttackMovement( ). Since this is part of the Server function, it should run on the server. It will then replicate to other clients, since the server has authority.

So, as far as I understand, I am “telling the server to explictily do the movement” by setting location locally and then doing the same for the server through Server_AttackMovement( ). Am I doing something wrong here? Are the functions formatted incorrectly?

Flow seems correct. Are you using the character class (Character movement component) or a custom set up?

What’s your RPC event setup? Should be similar to the following.

Client Input: Switch has authority (Remote] → Attack_Movement[] → Server Attack Movement
Server Attack Movement [Run on Server]: Switch has authority (Auth] → Attack_Movement[]

An added flow control option…
Server Attack Movement [Run on Server]: Switch has authority (Auth] → Branch(Is Server)[true] → Attack_Movement[]

1 Like

Yes, I am using the Character movement component set up.

My RPC event setup is pretty much what you described there, except I didn’t check for Switch has authority. You can see the set up in the code attached above. I was doing some testing earlier and it seems that when this glitch occurs, the client wants to move too many times and goes too far. For example, moving 4 times instead of 3.

What’s weird is that whereas before it would show me that they moved too many times, the SetActorLocation( ) actually does trigger the “right” amount of times with how the code is now on both the client and server.

For reference, here it is now:

void ABrawlTownCharacter::AttackMovement(float Speed, float DeltaTime)
{


	if ((Controller != NULL) && (Speed != 0.0f) && Should_Move_Punch)
	{
		const FRotator Rotation = Controller->GetControlRotation();
		const FRotator YawRotation(0, Rotation.Yaw, 0);

		FVector Direction_X = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

		Punch_Start_Pos = GetActorLocation();

		Punch_End_Pos = Punch_Start_Pos + (Direction_X * 150); // TODO: Magic Number, Give variable later

		FRotator New_Rotation = UKismetMathLibrary::FindLookAtRotation(Punch_Start_Pos, Punch_End_Pos);

		Punch_End_Pos.Z = GetActorLocation().Z;

		if (!Has_Attack_Moved)
		{
			SetActorLocation(Punch_End_Pos, true);
			Has_Attack_Moved = true;
		}

		SetActorRotation(New_Rotation);


	}

}

void ABrawlTownCharacter::Local_AttackMovement(float Speed, float DeltaTime)
{

	AttackMovement(Speed, DeltaTime);
	Server_AttackMovement(Speed, DeltaTime);

}

// After validated...
void ABrawlTownCharacter::Server_AttackMovement_Implementation(float Speed, float DeltaTime)
{
	if (HasAuthority())
	{
		AttackMovement(Speed, DeltaTime);	
	}

}

Okay, so I decided to go back to square one with this. There were too many outside factors that were complicating the issue.

Right now I’m just working on a basic constant-speed on-demand movement that isn’t dependent on anything else other than the player holding a mouse button down. When the button is pressed, both the client and server set the bool IsDashActive to true. Every tick, the actor checks if IsDashActive is true, and executes the following code if that’s the case:

void ABrawlTownCharacter::Dash()
{
	if (IsDashActive && IsLocallyControlled())
	{
			FVector NowLoc = GetActorLocation();

			const FRotator Rotation = GetControlRotation();
			const FRotator YawRotation(0, Rotation.Yaw, 0);
			FVector NowForward = FRotationMatrix(YawRotation).GetUnitAxis(EAxis::X);

			const float DisForward = 900.0f;

			FVector NewLoc = NowLoc + (DisForward * NowForward * GetWorld()->GetDeltaSeconds());

			//FVector InterpLoc = FMath::VInterpTo(NowLoc, NewLoc, GetWorld()->GetDeltaSeconds(), 1);
			SetActorLocation(NewLoc, true);

			Server_Dash(NowLoc, NewLoc);
	}
}

void ABrawlTownCharacter::Server_Dash_Implementation(FVector NowLoc, FVector NewLoc)
{
	SetActorLocation(NowLoc, true);
}

As you can see here, I’m using SetActorLocation( ); in the client, and then sending the NewLocation to the Server and just setting it instead of calculating it. However, I’m still getting jitter from the Server correcting it. On top of that, this current implementation doesn’t allow the host to move themselves.

I think I could fix the second issue more easily, but I can’t really wrap my head around fixing the first one. What am I doing wrong here that’s causing the jittering? Importantly, when I disable default movement replication ( Using SetReplicatingMovement(false); ), it replicates just fine. It must have something to do with a conflict in the default movement replication, but what could it possibly be?

I thought DeltaTime would solve the issue, but actually DeltaTime had no effect on the jittering.