Hey folks! I just finished a gamejam using Godot where I made a little top down puzzle game. However, there was one bug that I wasn't able to fix - for whatever reason it seems to me that my player is not processing collisions with more than one node at a time. I am confident that the issue is with my _handle_collisions() function in my player script, the full version of which I will set out below.

Here is a video of my issue, and a detailed explanation of what I think is happening.

You will see in the player script below that at the end of every physics frame I loop through everything collided with since the last call of move_and_slide(), and if any of those things is a "Moveable" object (i.e., a crate), we apply a force to it to push it. Now, the problem that arises is when I am pushing a crate while also touching a water tile. In the video you will see that in this case the player pushing on the crate has no effect.

When I print out the value of get_slide_collision_count() it is always either 0 or 1, and in the situation shown in the video, when I print out the collision value, it is just the collider of the water tile, and the crate collider never appears in the logs. So it's as if the collider only ever registers one collision at any given time. This leads me to believe that something wrong in either (i) the way I am reading the collisions; or (ii) the way my movement code is set up, but I have not yet figured out what exactly is the problem.

If this information is helpful, the crate is a RigidBody2D and the water tile is a StaticBody2D, although I am not sure if this makes a difference.

Any help would be much appreciated, and you can find the full version of my player script below!

extends CharacterBody2D
class_name Player

@export var speed: float = 100.0
@export var push_force: float = 100.0
@export var disable_movement: bool = false
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var collider: CollisionShape2D = $Collider


func _ready() -> void:
	animated_sprite.play("up")


func _physics_process(delta: float):
	if disable_movement:
		return
	_handle_input(delta)
	move_and_slide()
	_handle_collisions()
	
	
func _handle_input(delta: float) -> void:
	var x_axis: float = Input.get_axis("left", "right")
	var y_axis: float = Input.get_axis("up", "down")
	
	if x_axis == 0 and y_axis == 0:
		_set_idle_animations()
		return
		
	var input: Vector2 = Vector2(x_axis, y_axis).normalized()
	position += input * speed * delta
	
	_set_movement_animations(input)
		
		
func _set_movement_animations(input: Vector2) -> void:
	if input.x != 0:
		animated_sprite.play("side")
		if input.x < 0:
			animated_sprite.flip_h = false
		elif input.x > 0:
			animated_sprite.flip_h = true
	elif input.y > 0:
		animated_sprite.play("down")
	elif input.y < 0:
		animated_sprite.play("up")


func _set_idle_animations() -> void:
	animated_sprite.stop()
	if animated_sprite.animation == "down":
		animated_sprite.play("down_idle")
	elif animated_sprite.animation == "side":
		animated_sprite.play("side_idle")
		
		
func _handle_collisions() -> void:
	for index in get_slide_collision_count():
		var collision: KinematicCollision2D = get_slide_collision(index)
		var rigid_body: RigidBody2D = collision.get_collider() as RigidBody2D
		if rigid_body != null and rigid_body.is_in_group("Moveable"):
			rigid_body.apply_central_impulse(-collision.get_normal() * push_force)
    8 months later

    Hey man, I see you're setting the position in your input method and calling move_and_slide. You should be setting velocity in input method.

    The move_and_slide method works by taking the current velocity and applying it to the position while taking care of depenetration and reporting collision(s). It will also update velocity.

    toadkarter I think you are overestimating what CharacterBody2D does on its own. Yes, it provides collision detection but an Area2D provides detection AND influence. You need to also use an Area2D (some would also say a Raycast2D would work). Then you can use that area as a "Push Area" and whatever enters it gets moved. Then you can apply the physics there to whatever Moveable body enters it.