• 3D
  • Having trouble not sliding/snapping to slopes in 3D

I was working on a project of my own and decided to redo some of the player code, this time by use of two separate Vectors: spd_vec (Player Speed) and grv_vec (Player Gravity). Because I found out that you can add the two vectors together in move_and_slide_with_snap(), but however the problem I ran into is that the Y-Axis isn't colliding/snapping with the slopes (Shown in link/video)

The two vectors work by use of global_transform.basis[2] (Z-Axis / Speed) and global_transform.basis[1] (Y-Axis / Gravity) so then I can make the player fall in a different direction for gravity modification. This is the code I used for the player:

extends KinematicBody

var movedir = Vector3(0,0,0)
var velocity = Vector3()
var spd_vel = Vector3()
var grv_vel = Vector3()
var gravity = -35
var camera
var character

const SPEED = 12
const ACCELERATION = 5
const DECELERATION = 10
const JUMP_HEIGHT = 10

var is_moving = false

var can_jump = false

var rot_speed = 0
var mov_speed = 0
var camrot_h = 0
var camrot_v = 0
var floored = false
var ang_grav = false
var ang_gate = false

func _ready():
	character = get_node(".")
	Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
	$CamRoot.set_as_toplevel(true)

func _input(event):
	if event is InputEventMouseMotion:
		camrot_h += -event.relative.x
		camrot_v += event.relative.y
		pass
	pass

func _physics_process(delta):

	# Rotating our camera by mouse controls (press Alt+ f4 to quit, just in case)
	camera = get_node("CamRoot/H/V/Camera").get_global_transform()

	$CamRoot.global_transform.origin = self.global_transform.origin
	$CamRoot/H.rotation_degrees = Vector3(0,(camrot_h*(delta*10)),0)
	$CamRoot/H/V.rotation_degrees = Vector3((camrot_v*(delta*10)),0,0)



	# Speed Movement and Jump

	if Input.is_action_pressed("left"):
		rot_speed = lerp(rot_speed, 5, .1)
		pass
	elif Input.is_action_pressed("right"):
		rot_speed = lerp(rot_speed, -5, .1)
	else:
		rot_speed = lerp(rot_speed, 0, .1)
	
	#rotation += (global_transform.basis[1] * delta) * rot_speed

	if Input.is_action_pressed("up"):
		mov_speed = lerp(mov_speed, 400, .1)
		is_moving = true
	elif Input.is_action_pressed("down"):
		mov_speed = lerp(mov_speed, -400, .1)
		is_moving = true
	else:
		mov_speed = lerp(mov_speed, 0, .1)
		
		is_moving = false
	
	var ground = get_node("FloorCast").get_collision_normal()
	spd_vel = (global_transform.basis[2].normalized() * delta) * mov_speed
	if Input.is_action_just_pressed("jump"):
		grv_vel = (global_transform.basis[1].normalized() * delta) * 1000
	else:
		grv_vel += (global_transform.basis[1].normalized() * delta) * gravity



	if is_on_floor() or is_on_wall():
		if ang_gate == false:
			gravity = 0
		else:
			gravity = -35
	else:
		gravity = -35



	# Moving our character (Also used to determine ground collision)
	var slides = move_and_slide_with_snap(spd_vel + grv_vel, ground, Vector3(0,1,0), true)
	
	# Player origin base (Feet)
	var down = -global_transform.basis.y


	# Freezes player rotation while moving
	if is_moving && !Input.is_key_pressed(KEY_SHIFT):
		set_rotation($CamRoot/H.global_transform.basis.get_euler())


	# Alternate character collison detection
	var tilt = move_and_collide(global_transform.origin,true,true,true)



	### OLD Ignore this ###
#	if ang_grav == true:
#		if is_moving == false and (is_on_floor() or is_on_wall()):
#			spd_vel = Vector3.ZERO
#			grv_vel = Vector3.ZERO
#	elif ang_grav == false:
#		if is_moving == false:
#			spd_vel = Vector3.ZERO
#		if (is_on_floor() or is_on_wall()):
#			grv_vel = Vector3.ZERO



	# Modify gravity based on slopes and wheter on floor or not
	if ang_grav == true:
		if is_moving == false and (is_on_floor() or is_on_wall()):
			spd_vel = Vector3.ZERO
			if ang_gate == true:
				pass
			else:
				grv_vel = Vector3.ZERO
	elif ang_grav == false:
		if is_moving == false:
			spd_vel = Vector3.ZERO
		if (is_on_floor()):
			grv_vel = Vector3.ZERO
			pass



	# Used to determine if on a slope or not (Slopes / Part 2)
	if test_move(global_transform,down,true):
		#print("Ground!")
		var n = get_node("FloorCast").get_collision_normal()
		
		#var floor_angle = rad2deg(acos(n.dot(Vector3(0,1,0))))
		var floor_angle = rad2deg(acos(n.dot(global_transform.basis.y)))
		#print(floor_angle)
		if floor_angle > 0:
			ang_grav = true
			if floor_angle > 60:
				$CollisionShape3.shape.slips_on_slope = true
				ang_gate = true
			else:
				$CollisionShape3.shape.slips_on_slope = false
				ang_gate = false
			pass
		else:
			ang_grav = false

	else:
		
		pass
	#print(-global_transform.basis.y)



	# if tilt is colliding
	if tilt:
		
		pass



	# if move_and_slide detects a collision
	if get_slide_count() > 0:
		for i in get_slide_count():
			print(get_slide_collision(i).normal)
		pass
	else:
		if is_moving == true:
			
			pass
		else:
			
			pass
		pass
	
	global_transform = global_transform.orthonormalized()

I organized the lines of code based on which part does for ease of taking a look at the code. I honestly have no Idea how to get the collision to stay/snapped to the slope. Any help is appreciated!

If you need the demo project for a more better Idea I'm running into, let me know and I'll upload it.

Try with "move_and_slide()". Don't ever set "gravity" to zero, but set "grv_vel" to zero when "is_on_floor()" is true.

Well cybereality, I tried "move_and_slide()" But it didn't work right. The "move_and_slide_with_snap() allows to utilize the Snap part to let the player object stay on the ground. Say I use a raycast underneath the player and when the player makes contact, it uses the raycast's get_collision_normal() to determine the ground level; henceforth, the var ground = get_node("FloorCast").get_collision_normal()

Sorry, it's a lot of code and I don't exactly understand what you are try to accomplish. Can you explain what it is you are trying to do? I use "move_and_slide" in my project and I can walk on slopes no problem (gravity keeps the player on the ground). I just set the velocity.y to zero so the player doesn't slide down. Otherwise it works fine.

Thanks for the tips so far! I'm getting close based on all your suggestions. There might be one other mystery to solve. I currently set the code up to let the player fall by its own "Local" axis velocity; Or in technical terms, using the global_transform.basis.y (rotated Y-Axis) and multiply it by the gravity:

grv_vel += (global_transform.basis[1].normalized() * delta) * gravity

This way I could let the player walk on the wall if I choose to do so. Problem is that I need to find a way to capture the velocity/speed that falls on it's rotated "Y-Axis"

Say that I have it rotated 45 degrees and it now falls in a different direction. I want to be able to get the Positive or Negative values at what speed the object is falling to use in this:

		if is_on_floor() or is_on_wall():
			if ang_gate == false:
				if -grv_vel.length() < -0.1:
					grv_vel = (global_transform.basis[1].normalized() * delta) * -500
				else:
					grv_vel = Vector3.ZERO
			else:
				grv_vel += (global_transform.basis[1].normalized() * delta) * gravity
		else:
			grv_vel += (global_transform.basis[1].normalized() * delta) * gravity

the grv_vel.length() only captures how fast its going up and down, not showing any negative or positive value. That is what I believe may help get the code to work. Cause if it's negative (below 0.0 or -0.1) it uses the additional gravity to stay on the slope while going down and whereas positive (above 0.0 or -0.1) uses the Vector3.ZERO to move up without any additional dragging.

OK. GREAT NEWS. I finally managed to get the code to work! Its a bit of a mess but I'll take some time to cleanup/organize lines of code so then it can be (somewhat) understandable/readable. Then I'll share the code so then everyone can take a look and analyze.