Help needed with smoothness of a character move

Question

I wanted to make a smooth move for a character but i dont really know how to make it out of the script that i got:

if Input.is_action_just_pressed("Stick"):
			for dir in raycasts.keys():
				if dir == "left":
					is_sticking = false
					reattach_timer = reattach_delay
					
					velocity.x = left_force * speed

Can someone please tell me how to deal with this?

Can you explain what you expect to happen versus what really happens?

1 Like

Now its “teleporting” instantly to a postition, but i want to make it move smootly and not to a desired position. Oh and with a limit to it. I hope you will understand now.

I tried 4 methods to do that but they didnt work for me.
@gertkeno

I don’t think this code is relevant to teleporting then, it only changes velocity, not position. Could you paste more of your script (any part with position) or if you have Animations can you post how you set up those animations (especially if the modify position)

Here you have the entire code:

extends CharacterBody2D




@export var speed: float = 200.0
@export var jump_force: float = -300.0
@export var x_force: float = -300.0
@export var max_force : float = -1000.0
@export var min_force: float = -20.0
@export var stick_time: float = 1
@export var reattach_delay: float = 0.3  

var is_sticking: bool = false
var stick_timer: float = 0.0
var reattach_timer: float = 0.0
var stick_count = 0
var canStick = true
var onAir = true
var canDir = true

const gravity: float = 500.0
const fallGravity : float = 1500.0

@onready var player: CharacterBody2D = $"."
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var walk_particles: GPUParticles2D = $GPUParticles2D

@onready var raycasts: Dictionary = {
	"up": $RayCastUp,
	"down": $RayCastDown,
	"left": $RayCastLeft,
	"right": $RayCastRight
}


func _get_gravity(velocity : Vector2 ):
	if velocity.y < 0:
		return gravity
	return fallGravity
func _physics_process(delta):
	
	if not is_on_floor() and stick_count >= 3 and onAir:
		canStick = false
	else:
		canStick = true
		
	if not is_on_floor():
		walk_particles.emitting = false
		onAir = true
	
	if onAir == true and is_on_floor():
		onAir = false
		stick_count = 0
		
	if velocity.x == 0:
		walk_particles.emitting = false
	else:
		walk_particles.emitting = true
	var direction = Vector2(Input.get_axis("Left", "Right"), 0)
	if Input.is_action_pressed("Left") and canDir:
		sprite.flip_h = true
	
	if Input.is_action_pressed("Right") and canDir:
		sprite.flip_h = false
	if not is_sticking:
		
		velocity.x = direction.x * speed
		
		velocity.y += _get_gravity(velocity) * delta
		
		if is_on_floor() and Input.is_action_just_pressed("Jump"):
			velocity.y = jump_force
		if Input.is_action_just_released("Jump") and velocity.y < 0:
			velocity.y = jump_force / 6
		if not is_on_floor() and Input.is_action_just_pressed("Crouch"):
			velocity.y = -jump_force
		reattach_timer -= delta
	else:
		velocity = Vector2.ZERO
		stick_timer -= delta
		
		if stick_timer <= 0:
			canDir = true
			is_sticking = false
			reattach_timer = reattach_delay
			
		if Input.is_action_just_pressed("Stick"):
			for dir in raycasts.keys():
				var raycast = raycasts[dir]
				if dir == "left" and raycast.is_colliding():
					canDir = true
					is_sticking = false
					sprite.flip_h = false
					reattach_timer = reattach_delay
					
					velocity.x = -x_force * speed
					
					
					
					
	
	move_and_slide()
	
	if not is_sticking and reattach_timer <= 0 and canStick and Input.is_action_just_pressed("Stick"):
		for dir in raycasts.keys():
			var raycast = raycasts[dir]
			raycast.force_raycast_update()
			if raycast.is_colliding() and not dir == "down":
				if dir == "up":
					stick_count += 1
					canDir = false
					is_sticking = true
					stick_timer = stick_time
					player.position = player.position + Vector2(0, -3)
					print("Sticking to:", dir)
				else:
					print(player.position)
					stick_count += 1
					canDir = false
					is_sticking = true
					stick_timer = stick_time
					print("Sticking to:", dir)

The only part using position is near the bottom, and it’s not a direct set.

As a nitpick you can remove the player declaration, for one this script is already the player so modifying position on it’s own modifies the player’s position. And if you want self you should use self instead of $".".

So a equivalent shortened line is as follows.

position += Vector2(0, -3)

However this shouldn’t telepor the player either! Is there anything else that modifies the player’s position? Can you share a video or two screenshots of what is happening? Can you describe what actions cause the teleport to happen? Can you describe the other 4 methods you tried?

I meant in this part of the script(this is the part that i need help with):

if Input.is_action_just_pressed("Stick"):
			for dir in raycasts.keys():
				var raycast = raycasts[dir]
				if dir == "left" and raycast.is_colliding():
					canDir = true
					is_sticking = false
					sprite.flip_h = false
					reattach_timer = reattach_delay
					
					velocity.x = -x_force * speed
					

@gertkeno

Oh and i meant to transition the position smoothly to a desired location, but if you stop the key then it just slow down rapidly.

Why x force multiplied by speed? This evaluates to 6,000 pixels per second, seems rather high considering the player normally moves at 200 pixels per second.

It was just for testing.

But, is there a way to do that what i wanted(i know it is but i dont know it)?
If there is can you help me with it?

I am still not sure what it is you want to do.

Why do you think that part of the script is your culprit?

  • The part of the script is not culprit, but i want to modify it.
  • I want to modify it to move the player smoothly from the attached (sticked) position to a new position smoothly. Then when it start to move, the Player can stop the move with the release of the “Stick” action.

I hope you will understand what I mean now.

Still having trouble understanding. Maybe it would help to explain the bigger picture, is this a platformer? Is there anything like this in another game you could example?

Do you have the two “sticked” and “new position” figured out? Is one of them supposed to be the raycast variable?

Sounds like you want to be modifying velocity while the button is held, first try using Input.is_action_pressed as the function with just_pressed only applies to one frame when the button is initially pressed.

Yea my game will be a platformer.

Sorry but i dont know really if there are any like it. So lemme explain it. I want the mechanics of the game to stick the player to walls when they hit it. Then add some enemies graphics and other stuff that platformers have. The mechanics will base as I said around the walls sticking, and lauching from them to beat the map, fight or get somewhere.

“Sticked” position is the “freezed” postion when one of the raycast is detecting and when the “Stick” action is pressed.
“New postion” will be the players postion + some pixels to the left or a Marker2D if that will be not a problem.
@gertkeno

Are you here @gertkeno ?

At least I’m sure you want to move the player from one position to another, you don’t have “Sticked”, “freezed”, or “New position” defined in your script so I can’t give a direct example.

Take your target position in global space, if it’s from the raycast’s raycast.get_collision_point() then it’s in global space. Then you can multiply the direction to that point by the speed you want the player to move.

var target: Vector2 = raycast.get_collision_point()
var direction: Vector2 = global_position.direction_to(target)
# apply the direction and speed
velocity = direction * speed

I’m betting you want to remove this velocity reset too, since this function is run every frame velocity = Vector2.ZERO will stop your stick movement instantly on the next frame.

Ghhh… maybe this helps…

extends CharacterBody2D

@export var speed: float = 200.0
@export var jump_force: float = -300.0
@export var detach_force: float = 400.0  # Force applied when detaching
@export var detach_deceleration: float = 800.0  # How quickly to slow down after releasing stick
@export var stick_time: float = 1.0
@export var reattach_delay: float = 0.3

var is_sticking: bool = false
var is_detaching: bool = false
var stick_timer: float = 0.0
var reattach_timer: float = 0.0
var stick_count: int = 0
var can_stick: bool = true
var on_air: bool = true
var can_dir: bool = true
var detach_direction: Vector2 = Vector2.ZERO

const GRAVITY: float = 500.0
const FALL_GRAVITY: float = 1500.0

@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
@onready var walk_particles: GPUParticles2D = $GPUParticles2D
@onready var raycasts: Dictionary = {
    "up": $RayCastUp,
    "down": $RayCastDown,
    "left": $RayCastLeft,
    "right": $RayCastRight
}

func _get_gravity(velocity: Vector2) -> float:
    return FALL_GRAVITY if velocity.y >= 0 else GRAVITY

func _physics_process(delta: float) -> void:
    _update_stick_state()
    _handle_movement(delta)
    _handle_particles()
    move_and_slide()

func _update_stick_state() -> void:
    if not is_on_floor() and stick_count >= 3 and on_air:
        can_stick = false
    else:
        can_stick = true
        
    if not is_on_floor():
        on_air = true
    
    if on_air and is_on_floor():
        on_air = false
        stick_count = 0

func _handle_movement(delta: float) -> void:
    var direction = Input.get_axis("Left", "Right")
    
    if Input.is_action_pressed("Left") and can_dir:
        sprite.flip_h = true
    if Input.is_action_pressed("Right") and can_dir:
        sprite.flip_h = false

    if is_sticking:
        _handle_stick_state(delta)
    elif is_detaching:
        _handle_detach_state(delta)
    else:
        _handle_normal_state(delta, direction)

func _handle_stick_state(delta: float) -> void:
    velocity = Vector2.ZERO
    stick_timer -= delta
    
    if stick_timer <= 0:
        _end_stick_state()
        
    if Input.is_action_just_pressed("Stick"):
        for dir in raycasts.keys():
            var raycast = raycasts[dir]
            if dir == "left" and raycast.is_colliding():
                _initiate_detach("right")
            elif dir == "right" and raycast.is_colliding():
                _initiate_detach("left")

func _handle_detach_state(delta: float) -> void:
    # If stick button is released, start decelerating
    if Input.is_action_just_released("Stick"):
        velocity = velocity.move_toward(Vector2.ZERO, detach_deceleration * delta)
        if velocity.length() < 10:  # Small threshold to stop completely
            is_detaching = false
    
    # Apply gravity while detaching
    velocity.y += _get_gravity(velocity) * delta

func _handle_normal_state(delta: float, direction: float) -> void:
    velocity.x = direction * speed
    velocity.y += _get_gravity(velocity) * delta
    
    if is_on_floor() and Input.is_action_just_pressed("Jump"):
        velocity.y = jump_force
    if Input.is_action_just_released("Jump") and velocity.y < 0:
        velocity.y = jump_force / 6
    if not is_on_floor() and Input.is_action_just_pressed("Crouch"):
        velocity.y = -jump_force
        
    reattach_timer -= delta
    
    if reattach_timer <= 0 and can_stick and Input.is_action_just_pressed("Stick"):
        _try_stick()

func _initiate_detach(direction: String) -> void:
    can_dir = true
    is_sticking = false
    is_detaching = true
    reattach_timer = reattach_delay
    
    # Set sprite direction
    sprite.flip_h = direction == "left"
    
    # Apply smooth detach force
    var force = detach_force * (-1 if direction == "left" else 1)
    velocity.x = force

func _end_stick_state() -> void:
    can_dir = true
    is_sticking = false
    reattach_timer = reattach_delay

func _try_stick() -> void:
    for dir in raycasts.keys():
        var raycast = raycasts[dir]
        raycast.force_raycast_update()
        
        if raycast.is_colliding() and dir != "down":
            stick_count += 1
            can_dir = false
            is_sticking = true
            stick_timer = stick_time
            
            if dir == "up":
                position += Vector2(0, -3)
            
            print("Sticking to:", dir)

func _handle_particles() -> void:
    walk_particles.emitting = velocity.x != 0 and is_on_floor()

Thank you for it but i have a problem. When the pull stops the player still moves to that direction that the force is applied to. Can you fix it in the script?
@redflare
And after that you cant move.

The problem is in the _handle_detach_state function where the deceleration isn’t properly stopping the player and restoring normal movement control.