Trouble with characterbody2D block pushing

On Godot 4.2.1

I have a raycast the projects the players current direction onto the block when they collide and it functions as intended until the player moves away from the block, where the block then applies the players new direction even though the raycast is not colliding and therefore shouldnt update and remain stationary. Please forgive my code, i am a novice.

-Player code

extends CharacterBody2D

const speed = 300.0
const gravity = 1500
const jump_velocity = -450.0

@onready var ray = $RayCast2D

func _physics_process(delta):
	#movement
	if not is_on_floor():
		ray.enabled = false
		velocity.y += gravity * delta
	else:
		ray.enabled = true
	
	if Input.is_action_pressed("left"):
		ray.set_target_position(Vector2(-50,0))
	if Input.is_action_pressed("right"):
		ray.set_target_position(Vector2(50,0))
	
	var direction = Input.get_axis("left", "right")
	if direction != 0:
		velocity.x = direction * speed
	else:
		velocity.x = move_toward(velocity.x, 0, speed)
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = jump_velocity
	
	#block pushing
	var collider = ray.get_collider()
	if ray.is_colliding() and collider.is_in_group("block"):
		ray.force_raycast_update()
		collider._move(direction)
	move_and_slide()

-Block code

extends CharacterBody2D

const speed = 150
const gravity = 1000

func _ready():
	add_to_group("block")

func _physics_process(delta):
	if not is_on_floor():
		velocity.y += gravity * delta
	move_and_slide()

func _move(direction):
	if direction != 0:
		velocity.x = direction * speed
	else:
		velocity.x = move_toward(velocity.x, 0, speed)
	print(direction)

Try this:

extends CharacterBody2D

const speed = 300.0
const gravity = 1500
const jump_velocity = -450.0

@onready var ray = $RayCast2D

func _physics_process(delta):
	#movement
	if not is_on_floor():
		ray.enabled = false
		velocity.y += gravity * delta
	else:
		ray.enabled = true
	
	var direction = Input.get_axis("left", "right")
	if ray.enabled:
		ray.set_target_position(Vector2(50  * sign(direction.x),0))
		ray.force_raycast_update()
	
	if direction != 0:
		velocity.x = direction * speed
	else:
		velocity.x = move_toward(velocity.x, 0, speed)
	if Input.is_action_just_pressed("jump") and is_on_floor():
		velocity.y = jump_velocity
	
	#block pushing
	var collider = ray.get_collider()
	if ray.enabled and ray.is_colliding() and collider.is_in_group("block"):
		collider._move(direction)
	move_and_slide()
1 Like

Sorry for the delayed response, but i get the error “Invalid get index ‘x’ (on base: ‘float’)” on:

ray.set_target_position(Vector2(50  * sign(direction.x),0))

I tinkered around with your solution a bit and eventually found a clunky but functional workaround, but I encountered another issue where when I quickly change direction or jump and push the block, the blocks direction isn’t updated and it continues to slide along the axis. Even stranger is that if I hop over the block onto the other side of it while it’s sliding the block stops yet the blocks actual direction variable doesn’t change back to 0.

I spent a long time figuring out the best way to push a block using both rigid body and character body so I have a bit of experience here. I eventually settled on using a characterbody with a couple of states idle, fall, pushandpull etc. This way, I was able to manage the blocks physics interaction better.

Based on the first code you posted, one issue I noticed in the blocks code is this:

This code can’t be reached since you only call collider.move() when the raycast collides in the player script. That means, the blocks x velocity will never be set bac to 0 if you instantly turn in the other direction while pushing the block, the block will just keep moving.

To fix this, you need an else statement in the player script, that calls the blocks move() function to set the x velocity to 0 when the players raycast is not colliding. i.e:

	#block pushing
	var collider = ray.get_collider()
	if ray.is_colliding() and collider.is_in_group("block"):
		ray.force_raycast_update()
		collider._move(direction)
        else:
                collider._move(0)
	move_and_slide()

This should fix this issue too:

The other issue you will face is that once the raycast is not hitting a block, there will be no collider to reference to call the _move() method on so the x velocity will never be set to 0 and the block will just keep moving. To fix this, you have to save a reference to the last block as a variable so you can call _move(0) on the block even if the raycast is not colliding.

1 Like

Thank you so much for your reply.

I’m still very new coding and am curious how I might save a reference to the block even after it is no longer being collided with?

oops, I meant direction not direction.x

1 Like