3.5.3
Hello, I’m trying to make a 2D platformer with Sonic-like slope physics. I’m trying to make it so the player slowly eases into an inclination, but it only seems to snap into it over a few frames instead of the whole way through. Here’s some relevant code that returns the orientation the player should assume while on a slope; Raycasts are 3, 2 and 1 from left to right. This section of code only applies when raycasts 1 and 2 collide with the ground, and not 3.
#Rotate normal to align with surface
var surfaceNormal1 = rayCollisionNormal1.rotated(-PI / 2)
#Rotate normal to align with player rotation
surfaceNormal1 = surfaceNormal1.rotated(-rotation)
#Get slope from normal
if (surfaceNormal1.x == 0):
return(rayCollisionNormal2)
# Convert normal from vector to a single value that applies only to Y
var m = surfaceNormal1.y / surfaceNormal1.x
#Find change in Y if continue down the slope to raycast2's position
var a = m * distBetweenRays
#If y change is BELOW raycast2's collision point, the normals must connect somewhere to form a slope
if to_local(raycast1.get_collision_point()).y + a >= to_local(raycast2.get_collision_point()).y:
#Slope works!
#Essentially, find how far up the slope could possibly go if it continued to the right from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
#We can then multiply this ray's normal by the scale to determine the influence it should have
var b = rayHalfLength - a
var c = b / rayHalfLength
return(rayCollisionNormal2 + (rayCollisionNormal1 * c))
else:
#Slope does not work :/ Ignore rayCollisionNormal1 and just use 2
return(rayCollisionNormal2)
For the scenario where all three raycasts collide with the ground, the code is MUCH more complicated:
#All rays collide
if (rayCollisionDistance1 == rayCollisionDistance2 and rayCollisionDistance2 == rayCollisionDistance3):
return(rayCollisionNormalAvg)
elif (rayCollisionDistance1 == rayCollisionDistance2):
## Only left ray has different distance
#Rotate normal to align with surface
var surfaceNormal3 = rayCollisionNormal3.rotated(PI / 2)
#Rotate normal to align with player rotation
surfaceNormal3 = surfaceNormal3.rotated(-rotation)
#Get slope from normal
if surfaceNormal3.x == 0:
return(rayCollisionNormal2)
var m = surfaceNormal3.y / surfaceNormal3.x
#Find change in Y if continue down the slope to raycast2's position
var a = m * distBetweenRays
#If y change is BELOW raycast2's collision point, the normals must connect somewhere to form a slope
if to_local(raycast3.get_collision_point()).y - a >= to_local(raycast2.get_collision_point()).y:
#Slope works!
#Essentially, find how far up the slope could possibly go if it continued left from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
var b = rayHalfLength + a
var c = b / rayHalfLength
return((rayCollisionNormal2 + (rayCollisionNormal3 * c)).normalized())
else:
#Slope does not work :/ Ignore rayCollisionNormal3 and just use 2
return(rayCollisionNormal2)
elif (rayCollisionDistance2 == rayCollisionDistance3):
## Only right ray has different distance
#Rotate normal to align with surface
var surfaceNormal1 = rayCollisionNormal1.rotated(-PI / 2)
#Rotate normal to align with player rotation
surfaceNormal1 = surfaceNormal1.rotated(-rotation)
#Get slope from normal
if surfaceNormal1.x == 0:
return(rayCollisionNormal2)
var m = surfaceNormal1.y / surfaceNormal1.x
#Find change in Y if continue down the slope to raycast2's position
var a = m * distBetweenRays
#If y change is BELOW raycast2's collision point, the normals must connect somewhere to form a slope
if to_local(raycast1.get_collision_point()).y + a >= to_local(raycast2.get_collision_point()).y:
#Slope works!
#Essentially, find how far up the slope could possibly go if it continued left from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
var b = rayHalfLength - a
var c = b / rayHalfLength
return((rayCollisionNormal2 + (rayCollisionNormal1 * c)).normalized())
else:
#Slope does not work :/ Ignore rayCollisionNormal3 and just use 2
return(rayCollisionNormal2)
else:
## No adjacent rays are the same distance from their origins
#Rotate normals to align with surface
var surfaceNormal1 = rayCollisionNormal1.rotated(-PI / 2)
var surfaceNormal3 = rayCollisionNormal3.rotated(PI / 2)
#Rotate normals to align with player rotation
surfaceNormal1 = surfaceNormal1.rotated(-rotation)
surfaceNormal3 = surfaceNormal3.rotated(-rotation)
#Get slope from normals
if surfaceNormal1.x == 0:
#OOPS. Skip normal1, just calculate 2 and 3
if surfaceNormal3.x == 0:
#UGGGH just do normal2
return(rayCollisionNormal2)
var m = surfaceNormal3.y / surfaceNormal3.x
#Find change in Y if continue down the slope to raycast2's position
var a = m * distBetweenRays
#If y change is BELOW raycast2's collision point, the normals must connect somewhere to form a slope
if to_local(raycast3.get_collision_point()).y - a >= to_local(raycast2.get_collision_point()).y:
#Slope works!
#Essentially, find how far up the slope could possibly go if it continued left from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
var b = rayHalfLength + a
var c = b / rayHalfLength
return((rayCollisionNormal2 + (rayCollisionNormal3 * c)).normalized())
else:
#Slope does not work :/ Ignore rayCollisionNormal3 and just use 2
return(rayCollisionNormal2)
if surfaceNormal3.x == 0:
#OOPS. Skip normal1, just calculate 2 and 3
if surfaceNormal1.x == 0:
#UGGGH just do normal2
return(rayCollisionNormal2)
var m = surfaceNormal1.y / surfaceNormal1.x
#Find change in Y if continue down the slope to raycast2's position
var a = m * distBetweenRays
#If y change is BELOW raycast2's collision point, the normals must connect somewhere to form a slope
if to_local(raycast1.get_collision_point()).y + a >= to_local(raycast2.get_collision_point()).y:
#Slope works!
#Essentially, find how far up the slope could possibly go if it continued right from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
var b = rayHalfLength - a
var c = b / rayHalfLength
return((rayCollisionNormal2 + (rayCollisionNormal1 * c)).normalized())
else:
#Slope does not work :/ Ignore rayCollisionNormal3 and just use 2
return(rayCollisionNormal2)
## Both normals 1 and 3 aren't 0; we can proceed
var m1 = surfaceNormal1.y / surfaceNormal1.x
var m2 = surfaceNormal3.y / surfaceNormal3.x
#Find change in Y if continue down the slope to raycast2's position
var a = m1 * distBetweenRays
var d = m2 * distBetweenRays
#If y change is BELOW raycast2's collision point, the normals must connect somewhere to form a slope
if to_local(raycast1.get_collision_point()).y + a >= to_local(raycast2.get_collision_point()).y:
#Slope works!
#Essentially, find how far up the slope could possibly go if it continued to the relevant ray's position from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
var b = rayHalfLength - a
var c = b / rayHalfLength
if to_local(raycast3.get_collision_point()).y - d >= to_local(raycast2.get_collision_point()).y:
#All slopes work!
#Essentially, find how far up the slope could possibly go if it continued to the relevant ray's position from raycast2's position
#Then compare that with how far up it actually went, and create a scale from 0 to 1
var e = rayHalfLength - d
var f = e / rayHalfLength
return(((rayCollisionNormal3 * f) + rayCollisionNormal2 + (rayCollisionNormal1 * c)).normalized())
else:
#Only right slope works...
return((rayCollisionNormal2 + (rayCollisionNormal1 * c)).normalized())
elif to_local(raycast3.get_collision_point()).y - d >= to_local(raycast2.get_collision_point()).y:
#Only left slope works...
var e = rayHalfLength - d
var f = e / rayHalfLength
return(((rayCollisionNormal3 * f) + rayCollisionNormal2).normalized())
else:
#Slope does not work :/ Ignore rayCollisionNormal1 and 3 and just use 2
return(rayCollisionNormal2)
As a programmer who is very new to godot and currently may be overshooting in scope, some help would be appreciated