I came across this section while working on my driving game that uses Chaos Vehicles in UE5. Thought I would share my findings. The explanation of turning circles sparked my curiosity and came across this nice article that explains how a turning circle can be calculated based on the average steering angle from the Ackermann steering model:
I then used that formula to calculate it in my game. Since I am using chaos vehicles, I can read the max steering angle from the value configured on the wheel blueprints. My UVehicleMovementComponent
extends the UE5 UChaosWheeledVehicleMovementComponent
. I had to look at the source code of that class and some of the physics models classes to find the values I needed.
float UVehicleMovementComponent::GetTurningCircleRadius(float CurrentMaxSteeringAngle) const
{
// Based on formula from https://www.ehow.co.uk/how_7225784_calculate-turning-circle.html
// Turning circle = (track / 2) + (wheelbase / sin(average steer angle))
// Track is the length of the wheel track/axle from one wheel to another
// Wheelbase is length from center of front wheel track to centre of rear wheel track
// average steer angle is GetCurrentMaxSteeringAngle
if (CurrentMaxSteeringAngle < 0)
{
CurrentMaxSteeringAngle = GetCurrentMaxSteeringAngle();
}
if (FMath::IsNearlyZero(CurrentMaxSteeringAngle))
{
return 0.0f;
}
const auto& lWheelTrackDimensions = GetWheelLayoutDimensions();
// In ChaosWheelVehicleComponent.h
// PSteeringConfig.TrackWidth = WheelTrackDimensions.Y;
// PSteeringConfig.WheelBase = WheelTrackDimensions.X;
const float AxleLength = lWheelTrackDimensions.Y;
const float WheelbaseLength = lWheelTrackDimensions.X;
float TurningCircleRadius = 0.5f * AxleLength + WheelbaseLength / FMath::Sin(FMath::DegreesToRadians(CurrentMaxSteeringAngle));
return TurningCircleRadius;
}
float UVehicleMovementComponent::GetCurrentMaxSteeringAngle() const
{
check(VehicleSimulationPT && VehicleSimulationPT->PVehicle);
auto VehicleSim = VehicleSimulationPT->PVehicle.Get();
const auto& WheelSimArray = VehicleSim->Wheels;
// GetSteeringAngle is not declared const so this must be a non-const ref
auto& SteeringSim = VehicleSim->GetSteering();
const float SteeringFraction = GetCurrentMaxSteeringFraction();
float SteerAngleSum{};
int NumSteerWheels{};
for (int i = 0, Len = WheelSimArray.Num(); i < Len; ++i)
{
const auto& Wheel = WheelSimArray[i];
if (!Wheel.SteeringEnabled)
{
continue;
}
const float WheelMaxSteerAngle = WheelSimArray[i].MaxSteeringAngle * SteeringFraction;
if (FMath::IsNearlyZero(WheelMaxSteerAngle))
{
continue;
}
// See UChaosWheeledVehicleSimulation::ProcessSteering
const float WheelSide = VehicleSim->GetSuspension(i).GetLocalRestingPosition().Y;
const float WheelSteerAngle = SteeringSim.GetSteeringAngle(WheelMaxSteerAngle, WheelMaxSteerAngle, WheelSide);
SteerAngleSum += WheelMaxSteerAngle;
++NumSteerWheels;
}
if (NumSteerWheels == 0)
{
return 0.0f;
}
return SteerAngleSum / NumSteerWheels;
}
float UVehicleMovementComponent::GetCurrentMaxSteeringFraction() const
{
check(VehicleSimulationPT && VehicleSimulationPT->PVehicle);
return VehicleSimulationPT->PVehicle->GetSteering().GetSteeringFromVelocity(FMath::Abs(GetForwardSpeedMPH()));
}