Physics based pitch and yaw

I want to preface by saying I am not a very good mathematician or programmer. If I was I would have solved this by now.

I am working on a little prototype, relying on the physics system to handle most of the movement. For instance, if the player presses “D” on the keyboard, an impulse will be applied to the player to move them to the right.

All movement controls have been implemented so far, and require no feedback loop, except for the final one: pitch and yaw. My goal is to have the player point in the same direction the camera is facing. Normally, this would be done by directly rotating the player with some sort of interpolation, but with that approach they would not appropriately interact with the physics system.

So, I tried using the function, aptly named, AddTorqueInRadians(). I essentially just take the cross product between the player’s direction and the camera’s direction, multiply it by some scalar, then pop it in AddTorqueInRadians(). This is where I ran into my first problem. The player would just overshoot the intended direction indefinitely, oscillating back and forth forever.

And so I did some research, came upon the term “control theory,” tried implementing a PID controller without understanding what the hell it is, and came up with this implementation:

void AQControlledActor::PointAt(const bool isPointing)
{
	float DeltaTime = GetWorld()->GetDeltaSeconds();

	FVector ShipForward = -SkeletalMesh->GetForwardVector();
	FVector CameraForward = Camera->GetForwardVector();

	float Angle = FMath::Acos(FVector::DotProduct(ShipForward.GetSafeNormal(), CameraForward.GetSafeNormal()));

	IntegralError += Angle * DeltaTime; // Accumulate  over time
	
	float AngularVelocity = (Angle - PrevAngle) / DeltaTime;
	PrevAngle = Angle;

	FVector TorqueVector = FVector::CrossProduct(ShipForward, CameraForward) * (Kp * Angle + Kd * AngularVelocity + Ki * IntegralError);

	SkeletalMesh->AddTorqueInRadians(TorqueVector);
}

So I guess my question is two fold. Is this the correct implementation of a PID controller? Second, would you use a PID controller for this application? Is there something simpler that would work for my purposes?

Third, and unrelated: is this the right place to post a question like this?

Anyways, thanks for taking the time to read this long-winded post. I’d greatly appreciate any help from those more knowledgeable than myself.

Ended up omitting the integral term:

void AQControlledActor::PointAt(const bool isPointing)
{
	// Get the actor's location
	FVector ShipForward = -SkeletalMesh->GetForwardVector();
	FVector CameraForward = Camera->GetForwardVector();

	// Calculate the torque needed to rotate the ship towards the camera
	FVector PointingVector = FVector::CrossProduct(ShipForward, CameraForward);
	FVector AngularVelocity = SkeletalMesh->GetPhysicsAngularVelocityInRadians();
	FVector TorqueVector = SkeletalMesh->ScaleByMomentOfInertia(Kp * PointingVector - Kd * AngularVelocity);

	// Apply torque
	SkeletalMesh->AddTorqueInRadians(TorqueVector);
	
	//FVector ActorLocation = GetActorLocation();
	//DrawDebugLine(GetWorld(), ActorLocation, ActorLocation + ShipForward * 1000,
	//	FColor::Blue, false, 1, 0, 5);
	//DrawDebugLine(GetWorld(), ActorLocation, ActorLocation + CrossVector * 1000,
	//	FColor::Yellow, false, 1, 0, 5);
	//DrawDebugLine(GetWorld(), ActorLocation, ActorLocation + AngularVelocity * 1000,
	//	FColor::Orange, false, 1, 0, 5);
	//DrawDebugLine(GetWorld(), ActorLocation, ActorLocation + TorqueVector * 100,
	//	FColor::Red, false, 1, 0, 5);
	
}