Can't break away from a moving wall when using move_and_slide()

Godot Version

4.6.2

Question

When a moving wall pushes the player, using move_and_slide() prevents the player from moving away from the wall — they just slide along its surface.

Example:
The wall moves right at 100 px/s. Once it contacts the player, the player inputs right at 200 px/s. I expect the player to actually move right at 200 px/s and break away from the wall, but instead they just keep sliding along the wall.

Increasing the player’s safe_margin works for low wall speeds — the player can break away. But at higher wall speeds, the collision still prevents separation (the wall’s velocity overcomes the safe margin). Also, a large safe margin causes noticeable jitter when the player collides with a static wall.

Is there a better way to solve this? Do I need to rewrite it with move_and_collide()?

# Scene
func _physics_process(delta: float) -> void:
	$StaticBody2D.global_position.x += 100.0 * delta

# Player
var speed: float = 800.0
func _physics_process(delta):
	var input_dir = Input.get_vector("move_left", "move_right", "move_up", "move_down")
	velocity = input_dir * speed
	move_and_slide()

I cannot envision what you are describing. Is the player climbing up the wall?

It’s a top-down 2D situation, player moves on a flat horizontal plane, a moving wall pushes the player, but when the player tries to move faster in the same direction, they get stuck instead of breaking away.

You might have better luck if you use a CharacterBody2D for your moving platform instead of a StaticBody2D. StaticBodies don’t actually move when you program them to do so, they calculate where they should be then teleport to the new location with no physics interaction. This could be what’s causing the issues with your physics.

Also, if the block and the player are moving at the same speed, there’s nothing in your code to add the velocity of the block to the velocity of the player. The player’s movement code is just velocity = input_dir * speed and this will always be 800 no matter what affects the player.

To reiterate, what you need to do is use a CharacterBody2D for your moving wall, and add its velocity to the player’s in code.

What type of node is the wall? And what is the CharacterBody2D’s platform_on_leave value set to?

1 Like

@boe_taito You can actually use an AnimateableBody2D and then there’s a lot less code involved.

the wall is StaticBody2D now, platform_on_leave is add velocity

if the wall is characterBody2D, it will stop when it contacts the player

Change its type to an AnimateableBody2D. Right not that platform_on_leave value is not being used.

Also, I recommend canhging this:

# Scene
func _physics_process(delta: float) -> void:
	$StaticBody2D.global_position.x += 100.0 * delta

To this:

@onready var wall: AnimateableBody2D = $AnimateableBody2D

func _physics_process(delta: float) -> void:
	wall.global_position.x += 100.0 * delta

Because then when you change the name of the node, you only have to change it in one place.

Also, I recommend that for objects like your wall, you put the script on the wall itself. And you just modify position instead of global_position.

func _physics_process(delta: float) -> void:
	position.x += 100.0 * delta

I changed the code to this, the problem still exists. When the wall contacts the player, I input Key D to move right, player node can’t move right at 800px/s. Player can only slide along the wall

extends Node2D

var player_speed: float = 800.0
var wall_speed: float = 200.0
@onready var player: CharacterBody2D = $Player
@onready var wall: AnimatableBody2D = $Wall

func _ready() → void:
    $Player/Camera2D.make_current()

func _physics_process(delta: float) → void:
    var input_dir: Vector2 = Input.get_vector(“move_left”, “move_right”, “move_up”, “move_down”)
    player.velocity = input_dir * player_speed
    player.move_and_slide()

    wall.global_position.x += wall_speed * delta

What node is that script attached to? Can you add a screenshot of your Scene Tree?

Also, good job with the variables.

It’s attached to the Root
Root(Node2D)
-BackGround(Sprite2D)
-Wall(AnimatableBody2D)
—CollisionShape2D
—Sprite2D
-Player(CharacterBody2D)
—CollisionShape2D
—Sprite2D
—Camera2D

That’s not a screen shot and does not provide all the information I am looking for. It doesn’t show me:

  • What icon is attached to each node, regardless of name.
  • What errors or warnings might exist.
  • What other nodes have scripts attached.

Please either take a screen shot, or provide that information.

Great, thank you.

The reason I asked is I was wondering whether you might be calling move_and_slide() on the player more than once, and if you had any other scripts in the scene.

Also, I wanted to recreate your scene in Godot and test some things out. Give me a few.

Ok, so I tested this out, and I made two scripts:

extends AnimatableBody2D

@export var speed: float = 200.0


func _physics_process(delta: float) -> void:
	position.x += speed * delta
extends CharacterBody2D

@export var speed : float = 300.0


func _physics_process(delta: float) -> void:
	var direction := Input.get_vector("move_left", "move_right", "move_up", "move_down")
	if direction:
		velocity = direction * speed
	else:
		velocity.x = move_toward(velocity.x, 0, speed)
		velocity.y = move_toward(velocity.y, 0, speed)

	move_and_slide()

I attached them and it works fine. My alien could run away from the wall with no problem.

Then I recreated your code, and it worked fine as well.

extends Node2D

var player_speed: float = 300.0
var wall_speed: float = 100.0

@onready var wall: AnimatableBody2D = $AnimatableBody2D
@onready var player: CharacterBody2D = $CharacterBody2D

func _physics_process(delta: float) -> void:
	var input_dir: Vector2 = Input.get_vector("move_left", "move_right", "move_up", "move_down")
	player.velocity = input_dir * player_speed
	player.move_and_slide()

	wall.global_position.x += wall_speed * delta

When I copied your code into Godot however, all the actions ("move_left", "move_right", "move_up", "move_down") were using smart quotes instead of normal quotes, and I got an error from Godot. Are you getting this error?

Either way, copying and pasting my code should solve your problem.

Sorry, I missed one detail: my player’s collision shape is a CircleShape2D. I just tested it with a RectangleShape2D, and indeed there was no problem. It might be caused by the collision shape being a CircleShape2D.

Also, when the edges of the RectangleShape2D are not parallel to the edges of the wall, this issue also occurs.

When I copied the code back into Godot, I also encountered issues with quotation marks. It might have been a mistake during copying to the web page. I’ve already corrected all the quotation marks in Godot. It won’t affect the result.

Yep, I see that.

It appears that if the CollisionShape2D is only a pixel in size on the colliding side, it cannot break away. That should probably be logged as a bug here: Godot Issues

One solution is to make a CapsuleShape2D and make it round, then add a half pixel to the height.