Godot Version
v4.2.2.stable.flathub [15073afe3]
Question
I’m trying to have some boxes that I can move around deterministically in a 2d platformer, since there may be puzzles that involve them. I basically want boxes to be movable with no rotation, and fall when nothing is supporting them. What I have working is close:
However, you can see that at certain points I’m unable to push a block when another box is above it, and you may also notice some subtle shifting of boxes up/down.
Does anyone have a fix for this, or perhaps an easier way to accomplish what I want? It seems quite simple, but I haven’t been able to figure it out. Most tutorials for pushing blocks use Godot’s rigidbody physics, but it is non-deterministic and can get quite wonky at times.
Here is my code for the block:
class_name PushBlock
extends CharacterBody2D
var push_speed = 25
var gravity = 100
func _physics_process(delta):
velocity.y += gravity * delta
move_and_slide()
reset()
func push(direction):
if is_on_floor(): velocity.x = push_speed * direction
func reset():
velocity.x = 0
and here is the code for the player character:
extends CharacterBody2D
const SPEED = 150.0
const JUMP_VELOCITY = -250.0
var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var push_obj = null
var direction = 0
@onready var on_floor_label = $OnFloorLabel
@onready var push_right_collider = $PushRightCollider
@onready var push_left_collider = $PushLeftCollider
func _physics_process(delta):
# Add the gravity.
if not is_on_floor():
velocity.y += gravity * delta
# Handle jump.
if Input.is_action_just_pressed("jump") and is_on_floor():
velocity.y = JUMP_VELOCITY
# Get the input direction and handle the movement/deceleration.
# As good practice, you should replace UI actions with custom gameplay actions.
direction = Input.get_axis("left", "right")
if direction:
velocity.x = direction * SPEED
else:
velocity.x = move_toward(velocity.x, 0, SPEED)
# logic to push block
var pushblock = is_pushing_block()
if pushblock and is_on_floor():
pushblock.push(direction)
move_and_slide()
# returns the pushblock body if player is pushing a block
func is_pushing_block():
var bodies = null
if direction == 0:
return null
elif direction == 1: bodies = push_right_collider.get_overlapping_bodies()
else: bodies = push_left_collider.get_overlapping_bodies()
for i in range(bodies.size()):
if bodies[i].is_in_group('pushblock'):
return bodies[i]
return null