3
\$\begingroup\$

I am trying to replicate Mario Maker's On/Off switches in Godot 4.1.

In my current version, if the player got stuck inside the block after it was toggled on, the player just teleports out it. I know this is expected, but I wanted a smoother transition.

After looking it up, I found a video replicating the same feature in Godot, but the player, when stuck inside the block, slid out it, much more smoothly (see 0:33 in the video).

How can I replicate this smoother overlap resolution?

\$\endgroup\$
1
  • 1
    \$\begingroup\$ I can't give a real godot specific answer for you but it seems straightforward for any game engine. If you know where you would teleport to you can always just lerp to those coordinates instead. You might need some kind of state machine to turn off physics or player input while lerping the position of the character. \$\endgroup\$
    – Romen
    Oct 27, 2023 at 17:01

1 Answer 1

2
\$\begingroup\$

The key is to use add_collision_exception_with to prevent the physics system from resolving the collision. Then you're free to implement whatever movement you want!

I assume your block is a StaticBody2D. I would add an Area2D that matches the size of the StaticBody2D so you can detect objects overlapping the block. When you enable the switch to turn on collision:

  1. Area2D.get_overlapping_bodies to find what's overlapping and store them.
  2. For each of them, add_collision_exception_with to prevent collision response so they won't collide with the static body.
  3. Push them smoothly out of the collision (extrapolate a point that's outside and lerp them there over time or move them away at a fixed speed).
  4. When the Area2D gets body_exited, remove_collision_exception_with that body to collide with the block again.
  5. When all bodies have been forced out, turn monitoring off on the Area2D since you no longer need to respond to bodies.
  6. If the switch is hit again, turn monitoring back on the Area2D and repeat the process.

That might look like this (untested):

var push_speed := 3.0

var _pending_bodies

func _ready():
    $Area.body_exited.connect(_on_body_exited)

func enable_collision():
    _pending_bodies = $Area.get_overlapping_bodies()
    $StaticBody.disabled = false

    var needs_to_push := _pending_bodies.size() > 0
    $Area.monitoring = needs_to_push
    if needs_to_push:
        set_physics_process(true)
        for body in _pending_bodies:
            $StaticBody.add_collision_exception_with(body)

func disable_collision():
    $Area.monitoring = true
    $StaticBody.disabled = true

func _on_body_exited(body):
    _pending_bodies.erase(body)
    $StaticBody.remove_collision_exception_with(body)
    if _pending_bodies.size() == 0:
        set_physics_process(false)
        $Area.monitoring = false

func _physics_process(dt):
    for body in _pending_bodies:
        # May need to raycast to find an empty space to push them to. Or try a
        # few different ways to push them away.
        var away = (body.global_position - $StaticBody.global_position).normalized()
        body.move_and_collide(away * push_speed * dt)
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .