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)