What about using AddForce?

I’m posting this after completing the course, so bear with me.

I’m hoping to incorporate this replication system into a game with aerial drones moving in 3D space using physics rather than offsets. My pawns are a similar BP/C++ hybrid. Some stuff I had to do to get that to work:

  • Stripped out all the rotation stuff, since my pawns can move in any direction regardless where they’re facing. Rotation is just replicated from client.
  • Added in third input for “MoveUp”, stripping out the old SteeringThrow variable and replacing the Throttle float with an FVector.
  • Replaced the pawn root component with a physics cube so I can apply AddForce.

Using OFFSET movement works fine and great. And I get how offsets are known values and therefore very predictable. But how could someone use AddForce instead while still maintaining the replication prediction? Or, alternatively, how could someone create realistic physics interactions with offsets? My goal is to have pawns that can be thrown around using physics interactions without that movement relying on player input.

My first few attempts have… not gone well. Was hoping for some ideas.

This is actually quite tricky.
The force you are adding will come from the client. This would be based on your acceleration and a direction. The only way I can think of handling this is for an event on the server that tracks position change after AddForce is called based on data replicated from the client. As the tick even runs, record the position of the drone and replicate that back to the client.

This would be how I would initially approach this.

I assume slow-down would need to deal with inertia and air resistance calculations as well.

Honestly, I wish I knew more about Rocket League’s implementation. I’ve watched their GDC video talking about it, but it’s pretty broad. They have the same type of pawns I’m working to achieve.

I’ve already been able to implement a simple transform replication, but that (as the course well demonstrates), leaves a lot to be desired in regards to smooth and reactive pawn movement. Autonomous proxies constantly rubberband on server updates and simulated proxies teleport around since there’s no prediction.

The key seems to be the Velocity variable, since that is what’s used to SimulateMove in the movement component and it’s also referenced in the replication component’s interpolation systems. It’s useful because it informs the exact position the pawn will move - something that AddForce does not know beforehand.

But it could, right? Addforce is just adding vectors to the pawn’s current world translation, so figuring out what that resulting vector would be should enable one to determine what the pawn’s new world translation will be for that tick. Then it’s just a matter of reversing the existing code - determine the Velocity variable from the world translation. So the real question is: How do I determine the resulting translation that will result from using AddForce to an actor? If an actor with mass X that translated vector Y the previous frame (so movement and inertia are known) - how is that affected by applying AddForce with a new vector Z? There has to be a formula to determine that.

Then, like you said, determining the drag force as well. I’d hope to implement it with the built-in linear damping system, though if it had to be done manually like in the tutorial project that works too.

Velocity is the key. But also change in direction. The issue with add force or add impulse is you need to simulate it as well as determine the actual movement and replicate it. For games like rocket league, they most likely update at around 30 times a second.

Is there a way to determine the Engine’s math for applying AddForce? I followed the functions, but hit a dead-end.

From MyDroneMovementComponent.cpp:
PhysicsCubeRef->AddForce(Force);

Leads to PrimitiveComponentPhysics.cpp:

void UPrimitiveComponent::AddForce(FVector Force, FName BoneName, bool bAccelChange)
{
	if (FBodyInstance* BI = GetBodyInstance(BoneName))
	{
		WarnInvalidPhysicsOperations(LOCTEXT("AddForce", "AddForce"), BI, BoneName);
		BI->AddForce(Force, true, bAccelChange);
	}
}

Which leads to BodyInstance.cpp:

void FBodyInstance::AddForce(const FVector& Force, bool bAllowSubstepping, bool bAccelChange)
{
	FPhysicsCommand::ExecuteWrite(ActorHandle, [&](const FPhysicsActorHandle& Actor)
	{
		if(!IsRigidBodyKinematic_AssumesLocked(Actor))
		{
			if(FPhysScene* PhysScene = GetPhysicsScene())
			{
				PhysScene->AddForce_AssumesLocked(this, Force, bAllowSubstepping, bAccelChange);
			}
		}
	});
}

Which leads to PhysScene_Chaos.cpp:

void FPhysScene_Chaos::AddForce_AssumesLocked(FBodyInstance* BodyInstance, const FVector& Force, bool bAllowSubstepping, bool bAccelChange)
{
	using namespace Chaos;

	FPhysicsActorHandle& Handle = BodyInstance->GetPhysicsActorHandle();
	if (FPhysicsInterface::IsValid(Handle))
	{
		Chaos::FRigidBodyHandle_External& Body_External = Handle->GetGameThreadAPI();
		EObjectStateType ObjectState = Body_External.ObjectState();
		Body_External.SetObjectState(EObjectStateType::Dynamic);

		if (bAccelChange)
		{
			const Chaos::FReal Mass = Body_External.M();
			const Chaos::FVec3 Acceleration = Force * Mass;
			Body_External.AddForce(Acceleration);
		}
		else
		{
			Body_External.AddForce(Force);
		}
	}
}

Which leads to SingleParticlePhysicsProxy.h:

void AddForce(const FVec3& InForce, bool bInvalidate = true)
{
	Write([&InForce, bInvalidate, this](auto* Particle)
	{
		if (auto* Rigid = Particle->CastToRigidParticle())
		{
			if (Rigid->ObjectState() == EObjectStateType::Sleeping || Rigid->ObjectState() == EObjectStateType::Dynamic)
			{
				if (bInvalidate)
				{
					SetObjectStateHelper(*GetProxy(), *Rigid, EObjectStateType::Dynamic, true);
				}
					Rigid->AddForce(InForce, bInvalidate);
			}
		}
	});
}

…At which point I have no idea what Rigid->AddForce(InForce, bInvalidate); is calling. I’d done a bit programming but was new to C++ for this course, so I’m not sure where the trail ends that has the actual calculations. Do you know?

Well, looking at the course, this looks to be using the Chaos Engine. I’m not sure if the source is included in the Unreal Engine - most likely not.

This topic was automatically closed 20 days after the last reply. New replies are no longer allowed.

Privacy & Terms