Raycast not properly detecting collision

Godot Version

Godot v4.4

Question

So, basically… I’m making a mario-kart like kart racer game and I need the car to be able to detect if it is touching the ground or not. I am using a raycast to align the car with the ground that it is touching and need that same raycast to be able to detect when the car is touching the ground. It should be simple but for whatever reason, when it is on the ground, it isn’t properly detecting it all of the time…

Example of what’s happening (watch the output)

Here’s the code for the kart:

extends Node3D

@onready var ball: RigidBody3D = $ball
@onready var car_mesh: Node3D = $suv
@onready var ground_ray: RayCast3D = $suv/RayCast3D
@onready var left_wheel: MeshInstance3D = $"suv/wheel-back-left"
@onready var right_wheel: MeshInstance3D = $"suv/wheel-back-right"
@onready var body_mesh: MeshInstance3D = $suv/body
@onready var anim: AnimationPlayer = $suv/AnimationPlayer
@onready var reset_ray: RayCast3D = $ResetRay
@onready var collision_detector: Area3D = $suv/collisionDetector

var sphere_offset = Vector3(0, -1.0, 0)
var acceleration = 40
var steering = 15
var turn_speed = 10
var turn_stop_limit = 0.75
var body_tilt = 70
var sideways_friction = 10
var drift_amount = 0
var turn_lessen = 0
var left = false
var right = false

var speed_input = 0
var rotate_input = 0
var total_input = 0

var ground = true

var drifting = false
var max_speed = 30
var reversing = false
var speed = 0

func _ready():
	ground_ray.add_exception(ball)
	reset_ray.add_exception(ball)
	
func snap_position(t: Transform3D) -> void:
	ball.global_transform = t
	car_mesh.global_transform = t
	ball.linear_velocity = Vector3.ZERO
	ball.angular_velocity = Vector3.ZERO

func _process(_delta):
	
	var linear_velocity: Vector3 = ball.get_linear_velocity()
	var local_linear_velocity: Vector3 = car_mesh.global_transform.basis.inverse() * linear_velocity
	
	if linear_velocity.length() > 0:
		reversing = false
	elif linear_velocity.length() < 0:
		reversing = true
		
	speed = local_linear_velocity.z
	
	# Keep the car mesh aligned with the sphere
	car_mesh.transform.origin = ball.transform.origin + sphere_offset
	# Accelerate based on car's forward direction
	if linear_velocity.length() < max_speed:
		ball.apply_central_force(-car_mesh.global_transform.basis.z * speed_input)
	
	var sideways_velocity: float = local_linear_velocity.x
	var sideways_force: Vector3 = sideways_velocity * sideways_friction * -car_mesh.transform.basis.x
	#Left is positive, Right is negative
	
	if drifting == false:
		ball.apply_central_force(sideways_force)
	
	if drifting == true && right == false:
		#Drift Left
		if rotate_input > .1:
			anim.play("driftLeft")
			drift_amount = .2
			steering = 5
			turn_speed = 7
			turn_lessen = -0.07
			left = true
			right = false
		#Drift Right
		elif rotate_input < -.1 && left == false:
			anim.play("driftRight")
			drift_amount = -.2
			steering = 5
			turn_speed = 7
			turn_lessen = 0.07
			left = false
			right = true
	elif drifting == false:
		steering = 7
		turn_speed = 10
		turn_lessen = 0
		drift_amount = 0
		left = false
		right = false

func _physics_process(delta):
	
	
	if ground_ray.is_colliding():
		print("ground")
		#ground_ray = $suv/RayCast3D
	else:
		print("air")
		#ground_ray = $ResetRay
	
	speed_input = 0
	speed_input += Input.get_action_strength("w")
	speed_input -= Input.get_action_strength("s")
	speed_input *= acceleration
	# Get steering input
	rotate_input = 0
	rotate_input += Input.get_action_strength("a")
	rotate_input -= Input.get_action_strength("d")
	rotate_input *= deg_to_rad(steering)
	
	total_input = rotate_input + drift_amount + turn_lessen
	
	right_wheel.rotation.y = rotate_input
	left_wheel.rotation.y = rotate_input
	
	# rotate car mesh
	if ball.linear_velocity.length() > turn_stop_limit:
		var new_basis = car_mesh.global_transform.basis.rotated(car_mesh.global_transform.basis.y, total_input)
		car_mesh.global_transform.basis = car_mesh.global_transform.basis.slerp(new_basis, turn_speed * delta)
		car_mesh.global_transform = car_mesh.global_transform.orthonormalized()
		# tilt body for effect
		var t = -rotate_input * ball.linear_velocity.length() / body_tilt
		body_mesh.rotation.z = lerp(body_mesh.rotation.z, t, 10 * delta)
		
	var n = ground_ray.get_collision_normal()
	var xform = align_with_y(car_mesh.global_transform, n.normalized())
	car_mesh.global_transform = car_mesh.global_transform.interpolate_with(xform, 10 * delta)
	
	if Input.is_action_pressed("drift") && speed <= -10:
		drifting = true
	elif Input.is_action_just_released("drift"):
		drifting = false
		anim.play("straight")

func align_with_y(xform, new_y):
	xform.basis.y = new_y
	xform.basis.x = -xform.basis.z.cross(new_y)
	xform.basis = xform.basis.orthonormalized()
	return xform

If anyone knows why this may be happening or knows a solution it would be greatly appreciated!

Maybe the ray origin is just too low so much so that the ray is below the ground?

Omg I can’t believe this, that was the issue, I’ve spent multiple hours struggling to fix this and the solution was just moving the ray up a bit :rofl:, thank you for your help

1 Like