Calculate the curve angle of a Path3D to calculate “centrifugal force”?

Godot Version

v4.4.1.stable.official [49a5bc7b6]

Background

I’m creating a train game and I’m using Path3D for the railways. I wanted some code to fake “centrifugal force”, so trains will fall off the railway if they go around a curve too quickly.

I put “centrifugal force” in quotes as what I’m actually calculating is a tilt_angle. When the tilt angle gets higher than a specified value that is when the train falls off.

My approach

I assumed I could do this by calculating the curve angle, using these steps:

  1. Calculate how far the train would move if it was moving in a straight line
  2. Find the closest point on the curve to the straight line point
  3. Get the direction from the starting point to the straight line point
  4. Get the direction from the starting point to the closest curve point
  5. Get the angle between these two directions
  6. Fiddle with this angle to work out the “centrifugal force”/tilt angle

Code!

Step 1:

var move_distance: float = current_speed * delta # current_speed is a float
var linear_pos: Vector3 = global_position + (-global_transform.basis.z * move_distance)

Step 2 - 5 :

class_name Railway extends Path3D

# When I call this:
#     global_from = global_position of the train
#     global_to = linear_pos calculated in step 1

func curve_angle(global_from: Vector3, global_to: Vector3) -> float:
	var closest_from: Vector3 = to_global(curve.get_closest_point(to_local(global_from)))
    # Step 2
	var closest_to: Vector3 = to_global(curve.get_closest_point(to_local(global_to)))
	
    # Step 3
	var a: Vector3 = closest_from.direction_to(global_to)
    # Step 4
	var b: Vector3 = closest_from.direction_to(closest_to)
	
    # Step 5
	return a.signed_angle_to(b, Vector3.UP)

Step 6:
This is how I’m currently converting the curve angle into a tilt angle. This needs to be improved for sure.

# -curve_angle as I want the tilt to be towards the outside of the curve
-curve_angle * current_speed * delta

The problem

This seems to be dependent on the bake_interval of the Curve3D. Which makes sense but the outcome seems to vary more than I’d assume.

I’m not entirely sure how to convert the curve angle to “centrifugal force”/tilt angle.

Questions

  1. Is my approach correct?
    1.1. If yes, is my code correct?
  2. Can/should I adjust my curve code to be independent of the bake_interval?
    2.1 Or is “step 6” a better place?
  3. Any advice for converting the curve angle into “centrifugal force”/tilt angle?

force = mass * velocity² / curve_radius

This of course ignores the superelevation that every railroad curve in the world has built into anyways. Here’s a “simplified” paper on the topic: https://pdhacademy.com/wp-content/uploads/2022/12/Railroad_Curves_Simplified.pdf

I’d just use the above formular with a Curve variable for easy use to determine if a train falls over before spending the rest of the afternoon with putting up more slow orders along my track :slight_smile:

2 Likes

Thanks for these links they are going to be really helpful :smiley: