Raycast collision being inconsistent

Godot Version

4.5

Question

I am using raycasts to detect obstructions and prevent the player from moving. However, I am clearly doing something wrong as the player collides with no vertical walls but is unable to move when under/above a horizontal wall (not even left or right). It seems that the problem lies in the first for loop of _physics_process, specifically with the variable valid_move, but I have no idea why it's behaving like this. Please have a look at the code and see if you can spot the error:

extends CharacterBody2D

var direction := Vector2.ZERO
var target := Vector2.ZERO
var target_reached := true
const SPEED = 150.0
@onready var casts := $RayCasts.get_children() # +x, +y, -x, -y
var valid_move := true

func _ready() -> void:
	position = Vector2(8,8)

func _physics_process(delta: float) -> void:
	# Check for collisions with walls or enemies in the direction that the player wants to move:
	for axis_i in range(2):
		if (direction[axis_i] > 0 and casts[axis_i].get_collider()) or (direction[axis_i] < 0 and casts[axis_i+2].get_collider()):
			print("this move is illegal")
			valid_move = false
		else:
			print("this move is fine")
			valid_move = true
	
	# Handle the acceleration and deceleration:
	if direction and valid_move:
		if (target-position).length() > 0.00390625:
			target_reached = false
			position = position.move_toward(target, SPEED * delta)
		else:
			target_reached = true
			position = target
			direction = Vector2.ZERO
		"""
		print("pos:", position)
		print("tar:", target)
		print("dist:", target-position)
		print("pythag dist:", (target-position).length())
		print("reached?:", target_reached)
		print("dir:", direction)
		print("valid?:", valid_move)"""

func _input(event: InputEvent) -> void:
	# If the user tries to move, update the direction vector:
	if target_reached and (event.is_action_pressed("character_move_left") or event.is_action_pressed("character_move_down") or event.is_action_pressed("character_move_right") or event.is_action_pressed("character_move_up")):
		direction = Vector2(Input.get_axis("character_move_left", "character_move_right"),Input.get_axis("character_move_up", "character_move_down"))
		target = position + direction * 16
		#print(target)

I have changed the code to the following because I realised that a for loop would potentially set the vaild_move variable back to true in certain cases and thought this would fix it. It did not:

extends CharacterBody2D

var direction := Vector2.ZERO
var target := Vector2.ZERO
var target_reached := true
const SPEED = 150.0
@onready var casts := $RayCasts.get_children() # +x, +y, -x, -y
var valid_move := true

func _ready() -> void:
	position = Vector2(8,8)

func _physics_process(delta: float) -> void:
	# Check for collisions with walls or enemies in the direction that the player wants to move:
	
	if (direction[0] > 0 and casts[0].get_collider()) or (direction[1] < 0 and casts[2].get_collider()) or (direction[0] > 0 and casts[1].get_collider()) or (direction[1] < 0 and casts[3].get_collider()):
		print("this move is illegal", direction)
		valid_move = false
	else:
		print("this move is fine", direction)
		valid_move = true
	
	# Handle the acceleration and deceleration:
	if direction and valid_move:
		if (target-position).length() > 0.00390625:
			target_reached = false
			position = position.move_toward(target, SPEED * delta)
		else:
			target_reached = true
			position = target
			direction = Vector2.ZERO
		"""
		print("pos:", position)
		print("tar:", target)
		print("dist:", target-position)
		print("pythag dist:", (target-position).length())
		print("reached?:", target_reached)
		print("dir:", direction)
		print("valid?:", valid_move)"""

func _input(event: InputEvent) -> void:
	# If the user tries to move, update the direction vector:
	if target_reached and (event.is_action_pressed("character_move_left") or event.is_action_pressed("character_move_down") or event.is_action_pressed("character_move_right") or event.is_action_pressed("character_move_up")):
		direction = Vector2(Input.get_axis("character_move_left", "character_move_right"),Input.get_axis("character_move_up", "character_move_down"))
		target = position + direction * 16
		#print(target)
	

Okay at least it’s consistent now :sob:

the player gets stuck at EVERY wall now

extends CharacterBody2D

var direction := Vector2.ZERO
var target := Vector2.ZERO
var target_reached := true
const SPEED = 150.0
@onready var casts := $RayCasts.get_children() # +x, +y, -x, -y
var valid_move := true

func _ready() -> void:
	position = Vector2(8,8)

func _physics_process(delta: float) -> void:
	# Check for collisions with walls or enemies in the direction that the player wants to move:
	
	if (direction[0] > 0 and casts[0].get_collider()) or (direction[1] > 0 and casts[1].get_collider()) or (direction[0] < 0 and casts[2].get_collider()) or (direction[1] < 0 and casts[3].get_collider()):
		print("this move is illegal", direction)
		valid_move = false
	else:
		print("this move is fine", direction)
		valid_move = true
	
	# Handle the acceleration and deceleration:
	if direction and valid_move:
		if (target-position).length() > 0.00390625:
			target_reached = false
			position = position.move_toward(target, SPEED * delta)
		else:
			target_reached = true
			position = target
			direction = Vector2.ZERO
		"""
		print("pos:", position)
		print("tar:", target)
		print("dist:", target-position)
		print("pythag dist:", (target-position).length())
		print("reached?:", target_reached)
		print("dir:", direction)
		print("valid?:", valid_move)"""

func _input(event: InputEvent) -> void:
	# If the user tries to move, update the direction vector:
	if target_reached and (event.is_action_pressed("character_move_left") or event.is_action_pressed("character_move_down") or event.is_action_pressed("character_move_right") or event.is_action_pressed("character_move_up")):
		direction = Vector2(Input.get_axis("character_move_left", "character_move_right"),Input.get_axis("character_move_up", "character_move_down"))
		target = position + direction * 16
		#print(target)
	

I solved it!! kind of

now the player gets to the edge but they can still move for some reason? idk anyway they move and they get a tiny bit stuck in the wall, I think _physics_process might be moving them a bit before it realises that it’s an invalid move. anyway heres the code:

extends CharacterBody2D

var direction := Vector2.ZERO
var target := Vector2.ZERO
var target_reached := true
const SPEED = 150.0
@onready var casts := $RayCasts.get_children() # +x, +y, -x, -y
var valid_move := true

func _ready() -> void:
	position = Vector2(8,8)

func _physics_process(delta: float) -> void:
	# Handle the acceleration and deceleration:
	if direction and valid_move:
		if (target-position).length() > 0.00390625:
			target_reached = false
			position = position.move_toward(target, SPEED * delta)
		else:
			target_reached = true
			position = target
			direction = Vector2.ZERO
	
	# Check for collisions with walls or enemies in the direction that the player wants to move:
	if (direction[0] > 0 and casts[0].get_collider()) or (direction[1] > 0 and casts[1].get_collider()) or (direction[0] < 0 and casts[2].get_collider()) or (direction[1] < 0 and casts[3].get_collider()):
		print("this move is illegal", direction)
		print(target_reached)
		valid_move = false
	else:
		print("this move is fine", direction)
		valid_move = true
		"""
		print("pos:", position)
		print("tar:", target)
		print("dist:", target-position)
		print("pythag dist:", (target-position).length())
		print("reached?:", target_reached)
		print("dir:", direction)
		print("valid?:", valid_move)"""

func _input(event: InputEvent) -> void:
	# If the user tries to move, update the direction vector:
	if target_reached and (event.is_action_pressed("character_move_left") or event.is_action_pressed("character_move_down") or event.is_action_pressed("character_move_right") or event.is_action_pressed("character_move_up")):
		direction = Vector2(Input.get_axis("character_move_left", "character_move_right"),Input.get_axis("character_move_up", "character_move_down"))
		target = position + direction * 16
		#print(target)
	

its inconsistent again. i love my life.

the player can sometimes get 2.5 pixels (the amount that they move per call of physics_process(), at least on my machine) into a wall, but only if their direction has changed from what it was before for some reason.

extends CharacterBody2D

var direction := Vector2.ZERO
var target := Vector2.ZERO
var target_reached := true
const SPEED = 150.0
@onready var casts := $RayCasts.get_children() # +x, +y, -x, -y
var valid_move := true

func _ready() -> void:
	position = Vector2(40,24)

func _physics_process(delta: float) -> void:
	# Handle the acceleration and deceleration:
	if direction and valid_move:
		if (target-position).length() > 0.00390625:
			target_reached = false
			position = position.move_toward(target, SPEED * delta)
		else:
			target_reached = true
			position = target
	
	# Check for collisions with walls or enemies in the direction that the player wants to move:
	if (direction[0] > 0 and casts[0].get_collider()) or (direction[1] > 0 and casts[1].get_collider()) or (direction[0] < 0 and casts[2].get_collider()) or (direction[1] < 0 and casts[3].get_collider()):
		#print("this move is illegal", direction)
		#print("posisionn", position)
		#print(target_reached)
		valid_move = false
	else:
		#aprint("this move is fine", direction)
		valid_move = true
		"""
		print("pos:", position)
		print("tar:", target)
		print("dist:", target-position)
		print("pythag dist:", (target-position).length())
		print("reached?:", target_reached)
		print("dir:", direction)
		print("valid?:", valid_move)"""

func _input(event: InputEvent) -> void:
	# If the user tries to move, update the direction vector:
	if target_reached and (event.is_action_pressed("character_move_left") or event.is_action_pressed("character_move_down") or event.is_action_pressed("character_move_right") or event.is_action_pressed("character_move_up")):
		direction = Vector2(Input.get_axis("character_move_left", "character_move_right"),Input.get_axis("character_move_up", "character_move_down"))
		target = position + direction * 16
		#print(target)
	

It’s fixed!!! I changed a couple things, like making it so that the player only actually moves at the end of _physics_process(), removing valid_move entirely and getting rid of invalid moves by resetting the target to position. Here’s the final code if anyone wants it:

extends CharacterBody2D

var direction := Vector2.ZERO
var target := Vector2.ZERO
var target_reached := true
const SPEED = 150.0
@onready var casts := $RayCasts.get_children() # +x, +y, -x, -y

func _ready() -> void:
	# Align to grid:
	position = Vector2(8,8)

func _physics_process(delta: float) -> void:
	# Handle the acceleration and deceleration:
	if direction:
		if (target-position).length() > 0.00390625:
			target_reached = false
		else:
			# Close enough, put them exactly on the tile:
			target_reached = true
			position = target
	
	# Check for collisions with walls or enemies in the direction that the player wants to move:
	if (direction[0] > 0 and casts[0].get_collider()) or (direction[1] > 0 and casts[1].get_collider()) or (direction[0] < 0 and casts[2].get_collider()) or (direction[1] < 0 and casts[3].get_collider()):
		# Make sure the player doesn't try to move:
		target = position
	
	# Move the player a little bit:
	if not target_reached:
		position = position.move_toward(target, SPEED * delta)

func _input(event: InputEvent) -> void:
	# If the user tries to move, update the direction vector:
	if target_reached and (event.is_action_pressed("character_move_left") or event.is_action_pressed("character_move_down") or event.is_action_pressed("character_move_right") or event.is_action_pressed("character_move_up")):
		direction = Vector2(Input.get_axis("character_move_left", "character_move_right"),Input.get_axis("character_move_up", "character_move_down"))
		target = position + direction * 16

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.