Character getting snagged on downwards slope

Godot Version

4.1.3

Question

So, I’m trying to make a game that is somewhat of a cross between the classic Sonic games and ULTRAKILL, and I’ve been working for a long time on trying to get the running on walls part down. I’ve scrapped and remade the character movement multiple times because of various issues. Currently, I’m stuck with the issue of the character getting snagged on downward slopes. It’s bizarre, the character will just lightly bounce off of a slope while going downwards on it. I have absolutely no idea what could be wrong.

This is the code for the character (incase there’s just something horribly wrong with my coding):
###########################################################

extends CharacterBody2D

####################################################################################################

####Instantiations########################################################################################################################################################

###Signals################################################################################################################################################################

signal Grounded()

###Variables##############################################################################################################################################################

##Physics

#Velocity
var runVelocityChange: Vector2 #How the character's velocity changes per-tick (With regard to running)

var jumpVelocityChange: Vector2 #How the character's velocity changes per-tick (With regard to jumping)

var velocityChange: Vector2 #How the character's velocity changes per-tick (misc)

#Forces
var currGrabity: float #The strength of the current gravity field
var normalGrabity = 20 #The strength of gravity normally

##Character

#Jumps
var jumpStrength = 600 #Force of a jump
var flaps = 4 #Doublejumps remaining
var flapsMax = 4 #Maximum doublejumps

#Horizontal Movement
var currAcceleration: float
var currAccelMod: float
var currBaseAccel: float

var accelNorm = 18 #Normal running acceleration

var accelBrakeMod = 5 #Acceleration modifier when character is running against momentum
var accelAirMod = 0.3 #Acceleration modifier when character is airborne

var maxSpeed = 1000 #Maximum speed normal running will accelerate the character to

var currDrag: float #groundDrag or airDrag
var groundDrag = 24 #Amount of drag whilst grounded
var airDrag = 3 #Amount of drag whilst airborne

var direction = 1 #Left or Right, -1 or 1
var axisInput = 0

##Misc

#Environment Conditions
var isAgainstWall = false #Whether or not the character is touching the wall
var wallSide = 0 #The side of the character a wall it touching; -1, 0 ,1 ==> left, center, right
var nullDrag = false #Whether or not drag is currently irrelevant

##########################################################################################################################################################################

####Functions#############################################################################################################################################################

###Signals################################################################################################################################################################

##Angle Detection#########################################################################################################################################################

func GroundAngleFound(Angle):
	self.rotation_degrees = (-convertVector2ToDegree(Angle) + 90)

##Wall Detection##########################################################################################################################################################

func WallDetected(): #What to do if the Wall Detector is triggered
	isAgainstWall = true

func NoWallDetected(): #What to do if the Wall Detector is not triggered
	isAgainstWall = false

###Calculations###########################################################################################################################################################

##General Maf

func averageVector2(vector: Vector2) -> float: #Averages the X and Y values of a Vector2
	var average
	
	average = ((vector.x + vector.y) / 2)
	
	return average

##Conversions

func convertDegreeToVector2(degree: float) -> Vector2:
	var radian = 0.000
	var vector = Vector2()
	
	radian = (degree * (PI / 180))
	vector.x = cos(radian)
	vector.y = sin(radian)
	
	return vector

func convertRadianToVector2(radian: float) -> Vector2:
	var vector = Vector2()
	
	vector.x = cos(radian)
	vector.y = sin(radian)
	
	return vector

func convertVector2ToRadian(vector: Vector2) -> float:
	var radian
	
	radian = acos(vector.x)
	
	return radian

func convertVector2ToDegree(vector: Vector2) -> float:
	var radian
	var degree
	
	radian = acos(vector.x)
	degree = (radian / (PI / 180))
	
	return degree

func convertRadianToDegree(radian: float) -> float:
	var degree
	
	degree = (radian / (PI / 180))
	
	return degree

func convertDegreeToRadian(degree: float) -> float:
	var radian
	
	radian = (degree * (PI / 180))
	
	return radian

###Misc

##_ready & _process

func _ready(): ####_ready
	self.rotation_degrees = 0

func _physics_process(_delta): ####_physics_process#######################################################################################################################
	
	###Getting Ready
	
	#Getting Direction/Axis Input
	if (Input.get_axis("ui_left", "ui_right") != 0): #If the left right axis is not 0
		direction = Input.get_axis("ui_left", "ui_right") #Changes direction to the left right axis
	
	axisInput = Input.get_axis("ui_left", "ui_right")
	
	#Resetting Velocity Change
	runVelocityChange = Vector2(0, 0) #Gets rid of velocity change from the last tick
	jumpVelocityChange = Vector2(0, 0) #Gets rid of velocity change from the last tick
	velocityChange = Vector2(0, 0) #Gets rid of velocity change from the last tick
	
	#Resetting Jumps Left
	if (self.is_on_floor() == true):
		flaps = flapsMax
	
	#Deciding Drag
	if (self.is_on_floor() == true):
		currDrag = groundDrag
	else:
		currDrag = airDrag
	
	#Deciding Gravity
	currGrabity = normalGrabity
	
	#Getting Angle
	if (self.is_on_floor() == true):
		Grounded.emit()
	else:
		self.rotation_degrees = 0
	
	###Physics and Movement###############################################################################################################################################
	
	##Forces##############################################################################################################################################################
	
	#Grabity##############################################################################################################################################################
	
	if (self.is_on_floor() == false): #Checks if character is on the floor
		velocityChange.y = (velocityChange.y + currGrabity) #Adds downward force to velocityChange if the character is not on the floor
	elif (sign(velocity.y) == 1): #If the character is on the floor
		velocityChange.y = (velocityChange.y - velocity.y) #Removes downward force from velocity if the character is on the floor
	
	##Input###############################################################################################################################################################
	
	#Handling Jumps#######################################################################################################################################################
	
	if ((Input.is_action_just_pressed("ui_accept") == true) && (flaps > 0)): #Checks if "Jump" was just pressed AND if the Character has any jumps left
		
		#Checks if the character is on the floor
		if (self.is_on_floor() == false): #If character is airborne
			flaps = (flaps - 1) #Removes a jump if the character is airborne
			
			#Checks if momentum is upward or downward while airborne
			if (sign(velocity.y) == 1): #If momentum is downward
				jumpVelocityChange.y = ((-jumpStrength * (0.5 + (0.5 * (flaps / flapsMax)))) - velocity.y) #Negates all downward force as well as applying an upward force that weakens with each consecutive jump
			else: #If momentum is upward
				jumpVelocityChange.y = (-jumpStrength * (0.5 + (0.5 * (flaps / flapsMax)))) #Adds more upward force, weakening with each consecutive jump
		else: #If the character is grounded
			jumpVelocityChange.y = -jumpStrength #Applies upward force equal to jumpStrength
	
	jumpVelocityChange = jumpVelocityChange.rotated(self.rotation)
	
	#Handling Horizontal Inputs###########################################################################################################################################
	
	#(Deciding the current acceleration modifier)
	if (self.is_on_floor() == false): #If the character is not on the ground
		currAccelMod = accelAirMod #Acceleration is cut to 30% if in the air
	elif ((axisInput != sign(velocity.rotated(-self.rotation).x)) && (axisInput != 0)): #If instead the character is moving against momentum while on the ground
		currAccelMod = accelBrakeMod #Acceleration is multiplied by 500%
	else: #Otherwise
		currAccelMod = 1 #There is no acceleration modifier
	
	#(Setting the current base acceleration)
	currBaseAccel = accelNorm
	
	#(Setting the current acceleration)
	currAcceleration = (currBaseAccel * currAccelMod)
	
	#(Applying acceleration or drag)
	if ((axisInput != 0) && (abs(velocity.rotated(-self.rotation).x) <= maxSpeed)): #If a left or right input is being made
		runVelocityChange.x = (runVelocityChange.x + (currAcceleration * axisInput)) #Adds gradual horizontal force dependant upon the left or right input
	elif (axisInput == 0): #If no horizontal input is being made
		if (abs(velocity.x) > currDrag):
			runVelocityChange.x = (runVelocityChange.x - (currDrag * sign(velocity.x))) #Gradually negates horizontal velocity when no horizontal input is being made
		else:
			runVelocityChange.x = (runVelocityChange.x - velocity.x)
	
	runVelocityChange = runVelocityChange.rotated(self.rotation)
	
	
	##Angle Handling######################################################################################################################################################
	
	##Finalizing##########################################################################################################################################################
	
	velocity = ((velocity + velocityChange) + (jumpVelocityChange + runVelocityChange))
	
	print("----------")
	print("is on floor? ", self.is_on_floor())
	print("velocity ", velocity)
	print("rotation ", self.rotation_degrees)
	print("position ", self.transform)
	print("currAccel ", currAcceleration)
	
	move_and_slide()

###########################################################

Also, the character has circular collision and there’s a raycast downwards to get the collision normal of the floor

Any help would be appreciated, thanks!

I still have the issue, but I cleaned up the code just a bit to make it simpler and look nicer

extends CharacterBody2D

var direction = 1 #What direction the character is facing, Left or Right, -1 or 1
var axisInput = 0 #What direction the Player is inputting

var miscVelocityChange: Vector2 #How the character's velocity changes per-tick (misc)

####Functions#######################################################################################

###Signals##########################################################################################

###Calculations#####################################################################################

##General Math

func isInRange(min: float, max: float, number: float) -> bool:
	if ((min <= number) && (number <= max)):
		return true
	else: 
		return false

##Conversions

func convertDegreeToVector2(degree: float) -> Vector2:
	var radian = 0.000
	var vector = Vector2()
	
	radian = (degree * (PI / 180))
	vector.x = cos(radian)
	vector.y = sin(radian)
	
	return vector

func convertRadianToVector2(radian: float) -> Vector2:
	var vector = Vector2()
	
	vector.x = cos(radian)
	vector.y = sin(radian)
	
	return vector

func convertVector2ToRadian(vector: Vector2) -> float:
	var radian
	
	radian = acos(vector.x)
	
	return radian

func convertVector2ToDegree(vector: Vector2) -> float:
	var radian
	var degree
	
	radian = acos(vector.x)
	degree = (radian / (PI / 180))
	
	return degree

func convertRadianToDegree(radian: float) -> float:
	var degree
	
	degree = (radian / (PI / 180))
	
	return degree

func convertDegreeToRadian(degree: float) -> float:
	var radian
	
	radian = (degree * (PI / 180))
	
	return radian

###Character Movement

##Environment Checks

var isAgainstWall = false #Whether or not the character is touching the wall
var wallSide = 0 #The side of the character a wall it touching; -1, 0 ,1 ==> left, null, right

func WallCheck():
	if ($"Wall Detector".has_overlapping_bodies() == true):
		isAgainstWall = true
		if ($"Wall Detector".get_node("Right Wall Detector").is_colliding() == true):
			wallSide = 1
		else:
			wallSide = -1
	else: 
		isAgainstWall = false
		wallSide = 0

var grounded = false
var groundedPrevious = false
var groundIndexNumber = 0

func GroundCheck():
	if ($"Ground Detector".is_colliding() == true):
		for n in $"Ground Detector".get_collision_count():
			if (isInRange(30, 150, convertRadianToDegree(self.global_position.direction_to($"Ground Detector".get_collision_point(n)).rotated(-self.global_rotation).angle())) == true):
				if ((groundedPrevious == false) && (grounded == true)):
					groundedPrevious = true
				grounded = true
				groundIndexNumber = n
				break
			else:
				if ((groundedPrevious == true) && (grounded == false)):
					groundedPrevious = false
				grounded = false
	else: 
		grounded = false
		if ((groundedPrevious == true) && (grounded == false)):
			groundedPrevious = false

##Horizontal Movement###############################################################################

var currAccelBase = 0.00
var currAccelMod = 0.00
var currAccel = 0.00
var accelBaseNorm = 15.00
var accelModAir = 0.65
var accelModReverse = 5.00

func GetAcceleration():
	if (grounded == false): #If not on the ground
		currAccelMod = accelModAir 
	elif (axisInput != sign(velocity.rotated(-self.global_rotation).x)): #If input is against current velocity
		currAccelMod = accelModReverse
	else: #With no special conditions
		currAccelMod = 1.00
	
	currAccelBase = accelBaseNorm
	
	currAccel = (currAccelBase * currAccelMod)

var currDrag = 0.00
var groundDrag = 12.00
var airDrag = 6.00

func GetDrag():
	if (grounded == false): #If not on the ground
		currDrag = airDrag
	else: #Otherwise
		currDrag = groundDrag

var runVelocityChange: Vector2 #How the character's velocity changes per-tick (With regard to running)

func MoveHorizontal():
	if (axisInput != 0):
		GetAcceleration()
		runVelocityChange.x = (currAccel * axisInput)
	else:
		GetDrag()
		if (abs(velocity.rotated(-self.global_rotation).x) < currDrag):
			runVelocityChange.x = -velocity.rotated(-self.global_rotation).x
		else:
			runVelocityChange.x = (currDrag * sign(-velocity.rotated(-self.global_rotation).x))
	
	if ((wallSide != 0) && (axisInput == wallSide)):
		runVelocityChange = Vector2(-velocity.rotated(-self.global_rotation).x, 0.00)
	
	runVelocityChange = runVelocityChange.rotated(self.global_rotation)

##Vertical Movement#################################################################################

var jumpVelocityChange: Vector2 #How the character's velocity changes per-tick (With regard to jumping)
var jumpStrength = Vector2(0.00, 600.00) #Force of a jump
var jumpInclination = 0.00

func Jump(strength: float):
	if (axisInput != 0):
		if (grounded == false):
			jumpInclination = (30.00 * axisInput) 
		else:
			jumpInclination = (15.00 * axisInput) 
	else: 
		jumpInclination = 0.00
	
	jumpVelocityChange = -jumpStrength.rotated(convertDegreeToRadian(jumpInclination))
	
	if ((grounded == false) && (velocity.y > 0)):
		jumpVelocityChange.y = (jumpVelocityChange.y - velocity.y)
	
	jumpVelocityChange = jumpVelocityChange.rotated(self.rotation)

var flaps = 4.00 #Doublejumps remaining
var flapsMax = 4.00 #Maximum doublejumps

func JumpCheck():
	if ((grounded == true) && (flaps < flapsMax)):
		flaps = flapsMax
	if ((Input.is_action_just_pressed("Jump") == true) && (flaps > 0)):
		Jump(0.25 + (0.75 * (flaps / flapsMax)))
		flaps = (flaps - 1.00)

##Rotation##########################################################################################

var previousAngle = 0.00

func CorrectRotation():
	if (grounded == true):
		previousAngle = self.global_rotation
		self.global_rotation = $"Ground Detector".get_node("Ground Angle Detector").get_collision_normal().rotated(convertDegreeToRadian(90)).angle()
		if (previousAngle != self.global_rotation):
			velocity = ((velocity.rotated(self.global_rotation) * 0.50) + (velocity * 0.50))
	else:
		self.global_rotation = 0

##Gravity###########################################################################################

var currGrabity: float #The strength of the current gravity field
var normalGrabity = 20.00 #The strength of gravity normally

func GetGrabity():
	currGrabity = normalGrabity

var gravityVelocityChange: Vector2

func Grabity():
	GetGrabity()
	if (grounded == false):
		gravityVelocityChange = Vector2(0.00, currGrabity)
	else:
		gravityVelocityChange = Vector2(0.00, -velocity.y)

###Misc

##_ready & _process#################################################################################

func _physics_process(_delta): ####_physics_process#################################################
	
	#Getting Direction/Axis Input
	if (Input.is_action_just_pressed("Fire Weapon") || Input.is_action_just_pressed("Alt Fire Weapon") || Input.is_action_just_pressed("Melee")):
		direction = sign((get_global_mouse_position() - self.global_position).x)
	elif ((Input.get_axis("Run Left", "Run Right") != 0) && (($"Projectile Creator".arsenalMeleeCooldown - 15) <= 0)):
		direction = Input.get_axis("Run Left", "Run Right")
		
	axisInput = Input.get_axis("Run Left", "Run Right")
	
	#Resetting Velocity Change
	runVelocityChange = Vector2(0.00, 0.00) #Gets rid of velocity change from the last tick
	jumpVelocityChange = Vector2(0.00, 0.00) #Gets rid of velocity change from the last tick
	gravityVelocityChange = Vector2(0.00, 0.00)
	miscVelocityChange = Vector2(0.00, 0.00) #Gets rid of velocity change from the last tick
	
	GroundCheck()
	WallCheck()
	CorrectRotation()
	Grabity()
	MoveHorizontal()
	JumpCheck()
	
	##Finalizing####################################################################################
	
	velocity = (velocity + miscVelocityChange + jumpVelocityChange + runVelocityChange + gravityVelocityChange)

	move_and_slide()

again, thanks to anyone willing to help!

You can adjust “snap length” in the inspector, or edit it in script by adding floor_snap_length after calling move_and_slide()