Multiple tweens problem

Godot Version

4.3

Question

I’m experiencing issues with a basic back-and-forth tween animation for my character. The goal is to animate the rotation while the player is moving and reset it smoothly when the player stops.

Here’s the relevant code:

extends CharacterBody2D

@export var speed: float = 40.0

var is_moving: bool = false
var bopping: Tween

func _ready() -> void:
    bopping = get_tree().create_tween().set_loops()
    bopping.stop()
    bopping.tween_property(self, "rotation", 0.1 * TAU, 0.3)
    bopping.tween_property(self, "rotation", -0.1 * TAU, 0.3)

func _process(delta: float) -> void:
    var input = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = input * speed
    
    is_moving = input != Vector2.ZERO

    if is_moving:
        bopping.play()
    else:
        bopping.stop()
        create_tween().tween_property(self, "rotation", 0.0, 0.3)
    
    move_and_slide()

I’ve created a GIF demonstrating the issue: GIF.

The problem is that when the player starts moving, the tweening doesn’t begin immediately and then jumps to the maximum rotation angle. I suspect this might be due to having two tweens animating the same property simultaneously.

Could anyone suggest how to work around this?

The “bopping” is being stopped properly, but the resting tween is not saved, so it cannot be stopped and must complete 0.3. Try storing the resting tween too.

var resting: Tween

func _process(delta: float) -> void:
    var input = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
    velocity = input * speed
    
    is_moving = input != Vector2.ZERO

    if is_moving:
        bopping.play()
        if resting and resting.is_valid():
           resting.kill()
    else:
        bopping.stop()
        if not (resting and resting.is_valid()):
            resting = create_tween().tween_property(self, "rotation", 0.0, 0.3)
1 Like

Thanks, that worked! I’m still trying to understand how to work with multiple tweens, I have troubles understanding when to use kill and is_valid methods.

Anyway, here’s the full code solution:

extends CharacterBody2D

@export var speed: float = 150.0

var is_moving: bool = false
var bopping: Tween
var resting: Tween

func _ready() -> void:
	bopping = get_tree().create_tween().set_loops()
	bopping.stop()
	bopping.tween_property(self, "rotation", 0.1 * TAU, 0.3)
	bopping.tween_property(self, "rotation", -0.1 * TAU, 0.3)

func _process(delta: float) -> void:
	var input = Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
	velocity = input * speed
	
	is_moving = input != Vector2.ZERO
	
	if is_moving:
		bopping.play()
		if resting and resting.is_valid():
			resting.kill()
	else:
		bopping.stop()
		if not (resting and resting.is_valid()):
			resting = create_tween()
			resting.tween_property(self, "rotation", 0.0, 0.3)
	move_and_slide()

In this case it may be better to start looking at AnimationTree, as you have a pretty simple animation between two states, walking “bopping” and idle “resting”.

1 Like

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.