Tween Sliding Player Downwards

Godot Version

v4.2.2.stable.official [15073afe3]

Question

I’m trying to make a pretty simple player movement system with stairs, but when I tried using tweens to move the player to the top of the stairs they just slide down to the right then teleport up to where they should be. How could I fix this?

extends CharacterBody2D

const SPEED = 130.0
const JUMP_VELOCITY = -200.0
const COUNTDOWN_RESET = 10
const SLIDE_STOP_SPEED = 12.0

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var countdown = COUNTDOWN_RESET

@onready var raycast = $Raycast
@onready var sprite = $Sprite2D

func stair_entered():
	velocity.y = 5
	print("entered 2")

func _physics_process(delta):
	if not is_on_floor():
		velocity.y += gravity * delta
		countdown -= 1
	else:
		countdown = COUNTDOWN_RESET

	var touching_floor = is_on_floor() or countdown > 0

	if Input.is_action_pressed("ui_up") and touching_floor:
		velocity.y = JUMP_VELOCITY
		countdown = 0

	var direction = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
	velocity.x = direction * SPEED if direction != 0 else move_toward(velocity.x, 0, SLIDE_STOP_SPEED)

	if is_on_wall() and is_on_floor() and direction != 0:
		var stair_pos = raycast.get_collision_point()
		stair_pos.y -= 2

		# Disable collisions and set velocity to zero for smooth movement
		set_physics_process(false)
		velocity = Vector2.ZERO
		
		var tween = create_tween()
		tween.tween_property(self, "global_position", stair_pos, 1.0)
		await tween.finished

		# Re-enable collisions and restore velocity
		set_physics_process(true)

	sprite.flip_h = direction < 0

	move_and_slide()

Does disabling the physics on the node also stop the tween processing if the tween is in physics process mode?
image

It doesn’t appear so. When I keep physics enabled it doesn’t change.

Yea, I found more docs to support that. it should be processed by the scene tree independent of the node process state.

Where is the ray collision point? That is my only other concern. I know you also modify it in the y axis.

If the ray collision is right you may need to disable the collision objects because those run on the physics server, not sure if set_physics_process(false) also stops the physics engine on that node.

1 Like

I should have included this in the original post. Sorry for that.
image
Disabling the collision object doesn’t seem to help. I could just be doing it wrong though?

1 Like

I’m not by a computer with Godot if that arrow is the raycast object it’s pointing down potentially at the stairs. Okay…

… I don’t see anything wrong logically, I can image what it should do. I guess if the character goes into the stair then the raycast didn’t hit the stair properly.

I guess from the video those stairs are thin it could be missing the collision. Also it seems like it isn’t colliding when going to the left and is still remembering the last collision?

You can turn on debug collision shapes in the game this should also show the ray cast.

I turned on the Debug and figured out that the raycast doesn’t turn with the player, which was causing the left problem. So I’ve modifed the raycast to fix this problem and updated the code. I’m still not sure why the player is floating off into space, though this time they fly upward in the opposite direction then they’re facing rather than down into the floor.

image

extends CharacterBody2D

const SPEED = 130.0
const JUMP_VELOCITY = -200.0
const COUNTDOWN_RESET = 10
const SLIDE_STOP_SPEED = 12.0

var gravity = ProjectSettings.get_setting("physics/2d/default_gravity")
var countdown = COUNTDOWN_RESET
var stair_pos = 0

@onready var raycastr = $RaycastR
@onready var raycastl = $RaycastL
@onready var sprite = $Sprite2D

func stair_entered():
	velocity.y = 5
	print("entered 2")

func _physics_process(delta):
	if not is_on_floor():
		velocity.y += gravity * delta
		countdown -= 1
	else:
		countdown = COUNTDOWN_RESET

	var touching_floor = is_on_floor() or countdown > 0

	if Input.is_action_pressed("ui_up") and touching_floor:
		velocity.y = JUMP_VELOCITY
		countdown = 0

	var direction = Input.get_action_strength("ui_right") - Input.get_action_strength("ui_left")
	velocity.x = direction * SPEED if direction != 0 else move_toward(velocity.x, 0, SLIDE_STOP_SPEED)

	if is_on_wall() and is_on_floor() and direction != 0:
		if raycastr.is_colliding():
			stair_pos = raycastr.get_collision_point()
		else:
			stair_pos = raycastl.get_collision_point()
		stair_pos.y -= 2

		# Disable collisions and set velocity to zero for smooth movement
		set_physics_process(false)
		$CollisionShape2D.disabled = true
		velocity = Vector2.ZERO
		
		var tween = create_tween()
		tween.tween_property(self, "global_position", stair_pos, 1.0)
		await tween.finished

		# Re-enable collisions and restore velocity
		set_physics_process(true)
		$CollisionShape2D.disabled = false

	if direction < 0:
		sprite.flip_h = true
	elif direction > 0:
		sprite.flip_h = false

	move_and_slide()

So I ran your code locally and I think I found the issue. so your global_position of your character is at the center of the the character. This will be higher than the stair. so the tween thinks it needs to get the chest of your character to the collision point of the raycast. which is down.

To fix this you need to compute the height of the character and divide it in half to know where the feet of the character is. then you should add this to your stair_pos.

stair_pos.y -= $CollisionShape2D.shape.size.y / 2.0

also had to move the raycast objects out a little because they would not hit the stair if they were right along the player edge.

here is an image showing where the tween wants to go if you do not add the height of the character

1 Like

This worked! Tysm!