Godot 4.5.1 stable
Introduction:
Hi, I’m working on a roller coaster designing game, but I’ve encountered a bug related to the tilt values for the points on a Path3D curve.
Context:
The basic system behind the game–as far as this bug is concerned–works like this: a Path3D curve called the “coastercurve” is drawn by the player with it’s y-values adjusted for verticality. This curve (displayed by the black dots) represents the path that the coaster track follows.
After it is set by the player, the game creates a “bankcurve,” another Path3D, and sets every point’s position to the highest position (highest y value) possible on the orbit around it’s corresponding coastercurve point. The player then alters every bankcurve point by rotating it around it’s corresponding coastercurve point. The angle from the coastercurve to the bankcurve is supposed to represent how the track is banked, or tilted, through it’s layout.
Afterwards, the coastercurve and bankcurve points disappear and CSGPolygon-based track mesh appears as the final track visual (it is actually attached to a third Path3D–“trackcurve”–but it’s position is just set to be on the opposite side of bankcurve around coastercurve). It’s important to note that the banking actually takes effect by setting each point on coastercurve and trackcurve’s tilt value to that point’s angle towards it’s corresponding bankpoint, which is the same for coaster and trackcurve.
Problem:
Phew, that was a lot of exposition. The problem is that the tilt value doesn’t actually seem to be what it says to be.
For instance, if I test the game without altering the bankpoints’ orbits, the tilt value for every coastercurve and trackcurve point should be 0.0–or straight up–because the bankpoints are directly above their respective coastercurve points by default. But it actually seems to be fairly unpredictable, usually generally facing upwards but sometimes tilting to the side without me altering the bankpoint orbits.
And if I test it but do alter the bankpoint orbits, the game does notice the changes and change the tilt values accordingly, but it seems to compound upon or be counteracted by the seemingly random tilt changes.
This seems to depend on the elevation of the track, as flat tracks never seem to have this issue while tracks with a bunch of hills tend to experience it more heavily.
What is not the problem:
I think I should clarify a few things that are not causing this issue:
-The bankpoints always appear perfectly above their respective coastercurve points in their orbits, as intended.
-This is very important: the tilt values are actually always correct. The tilt is always 0.0 for every point when I don’t alter the bankpoint orbits, for instance, but it seems to tilt anyway for some reason at seemingly random points.
-It does seem to correctly calculate the angle between a coastercurve point and bankcurve point.
Possible solutions:
#1: If I can find the tilt value of the angle that actually goes straight up for every coastercurve point, I should be able to just add rollangle (the intented tilt amount if tilt actually was always 0.0 for straight up) to that and call it a day.
#2: If I can’t maybe I can brute-force it by trying to add another Path3D, copy all points, and set it above. Then somehow get the angles to all of those points and add rollangle?
Visual [Black points are coastercurve, red are bankcurve]:
The main function involved in this issue (let me know if you need the whole script):
func GenerateTrackPath():
trackcurve.clear_points()
var count = coastercurve.get_point_count()
var prevrollangle = 0.0
for i in range(count): #Loop through every coaster/bank/track curve point
var pivot = coastercurve.get_point_position(i)
var bank = bankcurve.get_point_position(i)
var dir = GetTrackDirection(i)
var up = Vector3.UP
if abs(dir.dot(up)) > 0.99:
up = Vector3.FORWARD
var orbitplanenormal = dir
var orbitx = up.cross(orbitplanenormal).normalized()
var orbity = orbitplanenormal.cross(orbitx).normalized()
var offset = bank - pivot
var x = offset.dot(orbitx)
var y = offset.dot(orbity)
var angle = atan2(y, x)
var oppangle = angle + PI
var radius = sqrt(x * x + y * y)
var finaloffset = orbitx * cos(oppangle) * radius + orbity * sin(oppangle)
var trackpos = pivot + finaloffset
trackcurve.add_point(trackpos)
var tiltaxis = dir.normalized()
var rollup = offset - tiltaxis * offset.dot(tiltaxis)
var projectedup = up - tiltaxis * up.dot(tiltaxis)
projectedup = projectedup.normalized()
rollup = rollup.normalized()
var rollangle = projectedup.angle_to(rollup) #Rollangle is the intended tilt amount assuming 0.0 is straight up.
if projectedup.cross(rollup).dot(tiltaxis) < 0.0:
rollangle = -rollangle
if i > 0:
var delta = rollangle - prevrollangle
delta = wrapf(delta, -PI, PI)
rollangle = prevrollangle + delta
prevrollangle = rollangle
print(trackcurve.get_point_tilt(i)) #Debug
print(coastercurve.get_point_tilt(i)) #Debug
trackcurve.set_point_tilt(i, rollangle) #Find the actual tilt that goes straight up, then add rollangle?
coastercurve.set_point_tilt(i, rollangle) #Find the actual tilt that goes straight up, then add rollangle?
markers.queue_free()
bankmarkers.queue_free()
coastereditor.DestroyTempPath()
toolbar.queue_free()
Thank you very much for your consideration, and sorry if I did an awful job explaining that. Basically I want to make up go up.

