Download

Projectile Replication: on the Client the movement is choppy

Hello.

I made a quick video to show the issue:

As you can see, when I fire the Projectile (independently if I spawn it on the Server or on the Client):

  • Server sees the Projectile movement very smooth (as it should be)
  • **Client **sees the Projectile shaking, and the Movement is very very choppy

My first thought was there is a Replication lag. But I’m not sure, there is no reason to be.

Any ideas?

I have taken a look at this thread jerky projectile movement on client - Blueprint Visual Scripting - Unreal Engine Forums

I did the same thing here:

491e5f34e67f84a377d7efd22f21f1aad7fe8029.jpeg

The **ServerTrans **variable and the UpdateTransform() function are defined as follows:



UPROPERTY(Replicated)
        FTransform ServerTrans;

UFUNCTION(Reliable, NetMulticast)
	void UpdateTransform();
	void UpdateTransform_Implementation();


Nothing to do, the projectile has a strange choppy movement.

Are you using the ProjectileMovementComponent?

Yes, I am. I’ve been studying UE4 Framework from the ShooterGame example and there I’ve seen they use a **ProjectileMovementComponent **for their Projectile class.

Let the Projectile also move on the Clients. So you don’t see lags. If it’s not smooth, then it means the Projectile fights the Client Version and the Server Version of the Location.
Just do the same stuff you do on the Server on the Clients.

PS: :smiley: Please don’t tag me on every Network thread you create. It’s kinda spamming me already. I check out threads even without tagging me.

So I need to spawn the Projectile on both the Server and the connected clients. This leads me to think I should make a NetMulticast RPC function for the Spawning of the Projectile.

The spawn happens in the Weapon_Projectile class:

OLD VERSION (here ServerFireProjectile_Implementation was a ServerRPC function, not a MulticastRPC)



void AWeapon_Projectile::FireWeapon()
{
	FVector Origin = FVector(MyOwner->GetActorLocation().X + 10, MyOwner->GetActorLocation().Y, MyOwner->GetActorLocation().Z);//GetMuzzleLocation();
	FVector ShootDir = MyOwner->GetBaseAimRotation().Vector();	

	// Spawn projectile at WeaponSocket on the **[SERVER]**
	ServerFireProjectile(Origin, ShootDir);
}

void AWeapon_Projectile::ServerFireProjectile_Implementation(FVector Origin, FVector ShootDir)
{
	FTransform SpawnTM(MyOwner->GetMesh()->GetSocketLocation((FName("WeaponSocket"))));

	AProjectile* Projectile = Cast<AProjectile>(UGameplayStatics::BeginDeferredActorSpawnFromClass(this, ProjectileConfig.ProjectileClass, SpawnTM));
	
	if (Projectile)
	{
		Projectile->Instigator = Instigator;
		Projectile->SetOwner(this);
		Projectile->InitVelocity(ShootDir);

		UGameplayStatics::FinishSpawningActor(Projectile, SpawnTM);
	}
}


**
CURRENT VERSION (here ServerFireProjectile_Implementation is a MulticastRPC)**



void AWeapon_Projectile::FireWeapon()
{
	FVector Origin = FVector(MyOwner->GetActorLocation().X + 10, MyOwner->GetActorLocation().Y, MyOwner->GetActorLocation().Z);//GetMuzzleLocation();
	FVector ShootDir = MyOwner->GetBaseAimRotation().Vector();
	
	if (Role == ROLE_Authority)
	{
		// Spawn projectile at WeaponSocket on **[ALL THE PLAYERS]**
		ServerFireProjectile(Origin, ShootDir);
	}
}

void AWeapon_Projectile::ServerFireProjectile_Implementation(FVector Origin, FVector ShootDir)
{
	FTransform SpawnTM(MyOwner->GetMesh()->GetSocketLocation((FName("WeaponSocket"))));

	AProjectile* Projectile = Cast<AProjectile>(UGameplayStatics::BeginDeferredActorSpawnFromClass(this, ProjectileConfig.ProjectileClass, SpawnTM));
	
	if (Projectile)
	{
		Projectile->Instigator = Instigator;
		Projectile->SetOwner(this);
		Projectile->InitVelocity(ShootDir);

		UGameplayStatics::FinishSpawningActor(Projectile, SpawnTM);
	}
}


In the Current version, the Weapon spawns 2 projectiles on the Client and the choppy-movement issue is still there.

If I had had X players, I would have ended up with X projectiles spawned

I never said to spawn 2 Projectiles. I said you should update the Location of the Projectile on both.

What is the difference between what you suggested and what I did in the post number #2?

I update the Location of the projectile on ALL THE PLAYERS since UpdateTransformation() is a NetMulticast function.

That you do it on the Server and then Multicast it and I suggested to ALSO do it locally on the Clients.

Client should move the projectile themselves, so you don’t have this weird lag.

I decided to go with the OnRepNotify approach.

Look:

  1. In the tick function I update the ServerTrans variable on the Server
  2. Since the ServerTrans has been modified, the OnRep_ServerTrans() is called
  3. I call a NetMulticast function to make ALL PLAYERS know the new Projectile position

Screenshot:

0a3158bb780930fc01f93c10c845c4c37a114053.jpeg

The Projectile’s movement is now smooth, but spawns twice! <.<
**
If I fire the projectile on the ListenServer window, this doesn’t happen**

b17ee733cf37e34470d37e07c6c3d6a252b550f0.jpeg

Why don’t you just do what i told you? :stuck_out_tongue: Can’t help you if you don’t follow my solution steps.

  • Spawn the Projectile Replicated on the Server
  • Calculate the Movement on Client and Server (no replication yet)
  • Let the Server replicate his Movement

That should prevent the lag, since the Client already calculate the correct movement by himself.

#1 Spawn the Projectile Replicated on the Server [DONE]

#2 and #3 there’s nothing to calculate. Just setting a variable:

926a53139087dae203642f57b01b773d26ef97e5.jpeg

#2 - Calculate the Movement on Client and Server -> ok, I set the Variable both on the Client and the Server
#3 -Let the Server replicate his Movement -> ok, I replicate the variable value for all the players

Is this what you meant?

Anyway, I still get 2 projectiles.

UPDATE:

I’ve adjusted the Projectile spawn, now it’s sure it only happens on the Server.

So yeah, I don’t get the multiple-spawn anymore.

But I still see the projectile moving choppy (the code is still the same as above)

Also, with some tests, I see that SOMETIMES the OnImpact() function is called TWICE: once by the Server (as it always happened before), and once on the Client

I say sometimes because the 90% of the time it gets only called by the Server. And before I had this issue, also it was only called by the Server.

What’s going on? Why is the Client joining the OnImpact() function now? And why my projectile still keeps moving that way?

bump + Update:

this is strange;
playing around I discovered that** the OnRep_ServerTrans()** function doesn’t get called!



UPROPERTY(**Replicated**)
	FTransform ServerTrans;

UFUNCTION()
	void OnRep_ServerTrans();




**DOREPLIFETIME**(AProjectile, ServerTrans);




void AProjectile::**OnRep_ServerTrans**()
{
	GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Magenta, FString::Printf(TEXT("CALLED!!!!!!!!!!!!!!!!!!!!!!!!")));

	if (Role < ROLE_Authority)
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Magenta, FString::Printf(TEXT("CALLED BY CLIENT!!!!!!!!!!!!!!!!!!!!!!!!!")));
		SetActorTransform(ServerTrans);
	}		

	else
		GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Magenta, FString::Printf(TEXT("CALLED BY SERVER!!!!!!!!!!!!!!!!!!!!!")));
}


Things get more strange as we go further…

Yes, you are not telling it to. :stuck_out_tongue:

UPROPERTY(ReplicatedUsing=OnRep_ServerTrans)

Also, if you need the calling machine (in this case the server) to also execute this function it will need to be called manually.


ServerTrans = FTransform();
OnRep_ServerTrans();

What a noob, thanks! I completely forgot that!

Yea, I know, OnRep functions are only called by the client.

BY THE WAY

My **OnImpact() **function (which gets called everytime the Projectile impacts on something) SOMETIMES gets called **both **on the Client and the Server.
The 95% of the time, it gets only called on the Server.

09964b69f3636d580bc70b48abf177b6661bb81d.jpeg

What could the reason be for this? I remind you that the Projectile gets spawned ONLY once by the Server

Is the projectile marked as Replicated? If so, a copy will be created on all clients as well. So OnImpact will fire there too.
Now why it’s not consistent (does not always fire on the client) is probably a different issue.

Are you by any chance deleting the projectile at OnImpact? This could cause the projectile to be deleted on the client before it actually hits something.

Yes, it was replicating. But why does the ShooterGame set the Projectile class to replicate?
Since a projectile is created and destroyed on the server, why would one choose to replicate it?

Thus the question arises: should I replicate it?

Yes, the OnImpact destroys the projectile. That was the reason :stuck_out_tongue:

Depends on what you need. If you want the projectile to be visible / existing on the clients, you replicate it.
For example, OnImpact on clients could be used to spawn some hit particle effect.