[Video] Player-Controlled Replicating Physics Movement, Simulating Physics!

will this desync if client run this on another computer ?

, correct me if I’m wrong, I haven’t really read your code so I’m only guessing… I only see one possible problem: if one of the packet a client receives is corrupt or lost, then the client will be out of sync, so the next force applied after the lost packet will make the controller in question be out of place, basically, you’ll end up with an out-of-sync client. Obviously this can be resolved with a packet updating the true position every X time, just in case ^^ (not cricticizing, actually its my way of trying to contribute, though you probably thought of this already)

Cheers and as usual, nice job! ^^

Yup see my answer below! Also you must set simulate physics locally on each machine :slight_smile:

Hi there!

Yes that’s a great question!

The answer is that I’ve tested my system in real multiplayer games and also simulating real network conditions, using the ini file settings mentioned in first post

I’ve event tested all of this while also on skype, which always really slows down my in-game experience :slight_smile:

And it works!

The trick is that I am not setting a hard location and I am never assuming where the proxy currently is, I am just telling the proxy to interpolate to the actual correct destination, as broadcasted by the the player’s machine who owns that unit.

So in other words, I am never taking anything for granted, via interpolation I am always saying “get to this location” in a smoothly interpolated way that is visually continuous and looks great!

InterpTo

So the answer you to your question is interpolation, specifically InterpTo!

The advantage of InterpTo is that I am never assuming the current location, I am only telling the unit where it needs to progressively get to, each tick, so that the motion is smooth.

For anyone interested just look up interpto in BP or in C++ :slight_smile:

in C++ its all in UnrealMathUtility.h


Clarification

Again just to clarify, I am not setting a  "hard" location meaning "instantly jump to the correct net position broadcasted by the owning player"

I am instead saying "interpolate smoothly to the net position that you were told by the owning player"

And I am also applying additional velocity forces that make it look and work even better.

I am deep into making my physics based multiplayer game with twitch reactive gameplay in a multiplayer context, so I can assure you the theory of what I am saying does work in practice! 

Enjoy!

Sounds great mate, thanks for the explanation!

Hello! Great work there, !
I’m working in a multiplayer game in which physics are essential and I’m dealing with this issue (I hope I can achieve something like your algorithm!) but I really don’t know how to proceed from where I am.
I currently have this basic functionality:

  1. In a BluePrint, I make an event only executed by the server. In that event I enable physics on the desired Character and apply the desired force.
  2. Inside this event, I trigger another event that is a Multicast (executed by all clients). In that multicast event, I do the same as in the server: enable physics and add force.

After that, I guess that in order to interpolate the position and rotation of the Character’s skeletal mesh bones (to achieve faithful replication in all the clients) I need to know information about the position and rotation of the bones (only executed by server), and then store that info in some kind of sctructure (e.g. an array) that is then read by the clients and used in their interpolation.

Problem is, I don’t know how to get the info about the server’s bones and then edit the info about the client’s bones.
Could you please give me a hint in how to do that? I would appreciate it very much.
Greetz.

Hey , did this code ever make it as part of a pull request?

I’m basing movement in my game off of Physics movements, like adding forces and torque’s etc. I am using ‘Replicate Movement’ right now which seems okay, but it’s the latency between a client hitting the button and them actually moving is way to high, I guess I need to follow a similar route to what you were doing?

Slick work !

About the cheating question, I have noticed that when trying to join each other’s multiplayer game, incoming players are not allowed into the game if they are using a different dll (i.e. the source code changed). I have played together online with people where everyone built their own dll, but the source code matched exactly. But I think that is what refers to, dll’s are verified in some way (perhaps using some compile-time CRC?). I don’t think that makes hacking the dll impossible, which is why client-to-server RPCs have _Validate and _Implementation and so you do have to validate some things yourself… hoping to learn more about that.

Sorry for the necro again, would you be able to post a tutorial on this system? I’m working on a physics-based MP game myself where I manipulate the Velocity/Angular Velocity values directly.

Although my movement replicates, I still get fairly ‘jittery’ movement. Ideally I want to send as few updates about velocity/rotation as possible, as there might be a few hundred Physics-Simulating object in the world at once!

I will be re-implementing my system soon for Abatron, for a creature who can go into a rolling mode, so once I have completed this I will be in a position to provide you with the most up-to-date pointers and methods that I’ve tested as working!

I actually like necros of my posts :slight_smile:

The video content in the original post is still valid!

Suhweeeeeeet! Look forward to it :slight_smile:

Bump. Would love a tutorial for this.

I’d love it if you could set up a tutorial , or expand on the method.

I’ve hit this snag while working on ship multiplayer, eventually the physics-run ships go out of sync, and progressively so if the ocean waves are violent enough - The client could see the server’s ship as capsized (yet still receiving input) while on the server’s side it’s just fine. Currently I’d reset the non-player ship’s location according to the actual player, but this might be a cleaner and smoother looking approach.

Thanks for all your work so far.

I would also like an update on this.

Yeah, would still like to see this. Have my own implementation working but it’s not the greatest.

Same here! :smiley:

Implementation Overview

Dear Everyone,

I was not going to sit here and write all of this up but due to all of your requests for more info I’m posting this just for you :slight_smile:

There are three core concepts of my multiplayer physics implementation!

  1. The use of VInterpTo to smoothly adjust the position of the non net owner, both physics position and physics linear velocity

  2. The fact that player controlled units should never be interpolated, only the non-owner proxies of that player controlled unit should get interpolated.

  3. Apply any additional forces requires to get the non owner to the owner’s position that include accounting for things like gravity.

#1 is how you ensure that the non owner proxy will always end up in the correct position, with an update speed that you can control and tailor to your needs via the interp speed. VInterpTo is also frame rate invariant which is essential for supporting different generations of hardware which will run the game at different frame rates.

#2 is about gameplay experience! You’d never want a client to feel like they had sluggish control or no control over their own player unit because the server is busy adjusting the client’s proxy to what the server thinks is correct. The local owner of the unit must have full control of the unit and update everyone else, including the server.

#3 is abount ensuring the physics replication will look good, by locally applying gravity and other forces to get the proxy back in sync with the net owner’s physics orientation


**Code Structure Sample**

.h



```


//This is basically a duplicate of the struct used by Actor.h for replication (wrote my own before I found out about FRepMovement)

/*
UPROPERTY(Transient, ReplicatedUsing=OnRep_ReplicatedMovement)
struct FRepMovement ReplicatedMovement;
*/

USTRUCT()
struct FJoyMoveRep
{ 
	GENERATED_USTRUCT_BODY()

	UPROPERTY()
	FVector_NetQuantize100 Location;
	
	UPROPERTY()
	FRotator Rotation;
	
	UPROPERTY()
	FVector_NetQuantize100 LinearVelocity;
	
	UPROPERTY()
	FVector_NetQuantize100 AngularVelocity;
	
	FJoyMoveRep()
	{
		Location = LinearVelocity = AngularVelocity = FVector::ZeroVector;
		Rotation = FRotator::ZeroRotator;
	}
	FJoyMoveRep(
		FVector_NetQuantize100 Loc, 
		FRotator Rot,
		FVector_NetQuantize100 Vel,
		FVector_NetQuantize100 Angular
	)	
		: Location(Loc)
		, Rotation(Rot)
		, LinearVelocity(Vel)
		, AngularVelocity(Angular)
		
	{}
};


UFUNCTION(Reliable,Server,WithValidation)
void SERVER_SendJoyMove(FJoyMoveRep NewMove);
bool SERVER_SendJoyMove_Validate(FJoyMoveRep NewMove);
void SERVER_SendJoyMove_Implementation(FJoyMoveRep NewMove);

//Interpolated by JoyMoveRep_Receive
UPROPERTY(Replicated)
FJoyMoveRep R_JoyMove;

//Guarantee Rep 
UPROPERTY(Replicated)
uint8 DoRep_JoyMove;

void JoyMoveReplication();
void JoyMoveRep_Send();
void JoyMoveRep_Receive();


//class

UFUNCTION(Reliable,Server,WithValidation)
void SERVER_SendJoyMove(FJoyMoveRep NewMove);
bool SERVER_SendJoyMove_Validate(FJoyMoveRep NewMove);
void SERVER_SendJoyMove_Implementation(FJoyMoveRep NewMove);

//Interpolated by JoyMoveRep_Receive
UPROPERTY(Replicated)
FJoyMoveRep R_JoyMove;

//Guarantee Rep 
UPROPERTY(Replicated)
uint8 DoRep_JoyMove;


```



CPP



void AJoyBallMovement::JoyMoveReplication()
{   
	if(IsLocallyControlled())
	{
		JoyMoveRep_Send();
	}
	else
	{
		//Update!
		JoyMoveRep_Receive();
	
	}
}
void AJoyBallMovement::JoyMoveRep_Send()
{
	SERVER_SendJoyMove(
		FJoyMoveRep(
			C_BodyLocation,
			C_BodyRotation,
			C_Velocity,
			C_AngularVelocity
		)
	);
}	
bool AJoyBallMovement::SERVER_SendJoyMove_Validate(FJoyMoveRep NewMove)
{
	return true;
}
void AJoyBallMovement::SERVER_SendJoyMove_Implementation(FJoyMoveRep NewMove)
{
	//Rep!
	R_JoyMove = NewMove;	//RepRetry
	DoRep_JoyMove++;			//Just to guarantee rep happens
}

//~~~
//~~~
//~~~

void AJoyBallMovement::JoyMoveRep_Receive()
{
	//Dont rep invalid data
	if(R_JoyMove.Location.IsZero()) return;
	//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	
	//implement what I outlined above, using VInterpTo to update PhysicsLinearVelocity and component location in world space

       //Also you can interpolate rotation here, these are all vars that are part of R_JoyMove
      
       //This is the part that varies from project to project, which is why I outlined the core concepts above.
	
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//							Replication List
void AJoyBallHighest::GetLifetimeReplicatedProps( TArray< FLifetimeProperty > & OutLifetimeProps ) const
{ 
	Super::GetLifetimeReplicatedProps( OutLifetimeProps );
 
	//Joy Rep Physics Movement!
	DOREPLIFETIME(AJoyBallHighest, JoyRepMoveVibes);
	DOREPLIFETIME(AJoyBallHighest, DoRep_JoyRepMove);
}

void AJoyBallMovement::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	//~~~~~~~~~~~~
	
	//Dont need to replicate movement in single player games
	if(GEngine->GetNetMode(GetWorld()) != NM_Standalone)
	{
                //Local owner updates others, 
                //   others update every tick even when not getting data over network
		JoyMoveReplication();
	}
}



**Notes on My Code Structure**

**Please note I am deliberately not using ReplicatedUsing** because I need to call JoyMoveRep_Receive() every tick to apply extra forces like gravity locally and run the VInterpTo functions

If I used ReplicateUsing, the replication function would only get called when updates made it through across the real network, the infrequency of which is the precise reason I need to interpolate locally / apply physics forces locally.

Since the R_JoyMove replication var is only getting updated based on real network conditions, I can use whatever data is stored there, from the last known update, and interpolate to correct postion and also account for local forces like gravity.

**Also note that I check whether a unit is being locally controlled** to know whether it is sending updates or receiving them.



```


void AJoyBallMovement::JoyMoveReplication()
{   
	if(IsLocallyControlled())
	{
		JoyMoveRep_Send();
	}
	else
	{
		//Update!
		JoyMoveRep_Receive();
	
	}
}


```



IsLocallyControlled() is part of Pawn class



```


bool APawn::IsLocallyControlled() const
{
	return ( Controller && Controller->IsLocalController() );
}


```



Please note that this occurs every tick because I want to continue interpolating to last known network positions even when experiencing a laggy connection to the local owner. This is what VInterpTo does!

Multiplayer Rotation Interpolation

Please note I highly prefer SLERP over RInterpTo for multiplayer physics rotation, because the spherical interploation just works out so much better in all my real multiplayer tests. It’s not quite the same as Interp To but it works great for me.

The person who mentioned a ship game with capsizing ships will need this :slight_smile:

Quaternions



FQuat::Slerp(A, B,Alpha);


Rotators



static FORCEINLINE FRotator Slerp(const FRotator& A, const FRotator& B, const float& Alpha)
{
	return FQuat::Slerp(A.Quaternion(), B.Quaternion(),Alpha).Rotator();
}


I generally use an alpha / speed of 0.1 and that has worked great in all circumstances

Enjoy!

Nice one !

So just a couple of questions regarding this, the client simulates all of their movement locally and there’s never any latency between keyboard input and the client actually moving?

The idea is that you send the Client’s Position, Rotation, Velocity and Ang Velocity on tick via RPC (but ONLY if the pawn is controlled by a player), and the Server accepts those values and sets them for all other clients as well. I guess this means that it is possible for a client to ‘cheat’ but it would be up to the Server to perform some other kind of checks itself to see if a move was possible or not.

In my games I want the client to feel like they are in total control of their character, I’ve worked on several fast paced physX multiplayer games, and you need twith-reactive gameplay as a client, and I found that only to be possible by letting the client be in control and having Server update everyone else.

Yes you’d need to do cheating checks, but keep in mind the server can STILL take control of the client if it feels the need to, it would be easy to add to my code structure to just throw in a bool to override the client from sending updates and allow server to force client back to a proper position.



void AJoyBallMovement::JoyMoveReplication()
{   
       //Add code here for Server sending cheating corrections

	if(IsLocallyControlled() && !IsCheatingNeedToOverrideClientUpdates)
	{
		JoyMoveRep_Send();
	}
	else
	{
		//Update!
		JoyMoveRep_Receive();  //if Cheating now even local gets updates from Server
	
	}
}



Every system has its pros and cons, for me allowing the client to experience twitch-reactive physX gameplay in real multiplayer games is the highest priority, and I will code as necessary to support that goal :)

:)
1 Like

I am going to necro this. I’ve sent you a pm about it. Really impressed and interested in it.

Thanks Osok!

Your list of marketplace packs is quite impressive!

:slight_smile: