Tween.property (scale) await finished not working

Godot Version

4.4

Question

I have a tween utility method that changes scales and reverts it back to original below (in global autoload class)

func wobble_effect(nodes:Array[Variant], scale_factor:float = 1.08, time:float = 0.11) -> void:
	for node in nodes:
		var current_scale:Vector2 = node.scale
		var tween = node.get_tree().create_tween()
		tween.tween_property(node, "scale", node.scale * scale_factor, time).set_ease(Tween.EASE_IN_OUT)
		tween.chain().tween_property(node, "scale", current_scale, time).set_ease(Tween.EASE_IN_OUT)
		await tween.finished

The problem is, if i call this method under 0.11 seconds again, object keeps getting bigger (think it like a fast press) which is not i want. I want to ignore tween if already a tween is applied to same node.
My solution below does not work as i wanted

var wobble_tween_going_on:bool = false

func wobble_effect(nodes:Array[Variant], scale_factor:float = 1.08, time:float = 0.11) -> void:
	if wobble_tween_going_on:
		return
	
	wobble_tween_going_on = true
	for node in nodes:
		var current_scale:Vector2 = node.scale
		var tween = node.get_tree().create_tween()
		tween.tween_property(node, "scale", node.scale * scale_factor, time).set_ease(Tween.EASE_IN_OUT)
		tween.chain().tween_property(node, "scale", current_scale, time).set_ease(Tween.EASE_IN_OUT)
		await tween.finished
	wobble_tween_going_on = false #tried getting this into loop but no

How to handle these situations? I need to make sure that tween is not applied to same object that has already tween going on. I dont want to go to route of having dictionary and keeping track etc.

There is not enough context to give a specific solution. What is supposed to happen when you call wobble_effect on nodes where it is currently active?

In general, if you want to know if there is currently a tween running for a node there is no way around keeping track of the ‘node ↔ tween’ relationship. The node does not know that it is ‘tweened’. From its perspective only the scale property is updated.

Btw., you don’t need chain. Tweens are chained by default. It is only needed with parallel tweens. In general I also suggest to use Node.create_tween instead of SceneTree.create_tween. This way it’s bound to the ‘lifecycle’ of the calling node (e.g., it’s freed when the calling node is freed).

Sorry but context is enough. As i told above, i dont want a second tween happening if already a tween on that node is going on. As you see in the tween code “scale” is modified, so if second tween comes then object gets bigger from its supposed to. As seen in the code above, wobble method returns to original scale.

Anyway my solution below currently is working, but i wish there was a way to get existing/binded tweens on a node built-in

var existing_tweens:Array[String]

func wobble_effect(nodes:Array[BaseShape], scale_factor:float = 1.08, time:float = 0.11) -> void:
	for node in nodes:
		if existing_tweens.has(node.id):
			return
		
		existing_tweens.append(node.id)
		var current_scale:Vector2 = node.scale
		var tween = node.create_tween()
		tween.tween_property(node, "scale", node.scale * scale_factor, time).set_ease(Tween.EASE_IN_OUT)
		tween.tween_property(node, "scale", current_scale, time).set_ease(Tween.EASE_IN_OUT)
		tween.connect("finished", on_tween_finished.bind(node.id))


func on_tween_finished(id:String) -> void:
	existing_tweens.erase(id)

I don’t think there was. It is not clear on which nodes you call it. Is it always the same, can there be overlaps, is it a specific type of nodes, …? There might be solutions that apply to your case that might not work in a more general case.

E.g., I see you changed your code, so that it only applies to BaseShape and you do not await tween.finished anymore before starting the next.
What you could do now, is to implement the tween in BaseShape and then call it in wobble_effect:

# ---- BaseShape ----

var wobbling := false

func wobble(scale_factor: float, time: float) -> void:
  if wobbling:
    return
  wobbling = true

  var tween = create_tween()
  // ... your tweeners here
  await tween.finished
  wobbling = false 

# ---- Your autoload ----

func wobble_effect(nodes: Array[BaseShape], scale_factor: float = 1.08, time: float = 0.11):
  for node in nodes:
    node.wobble(scale_factor, time)

But yeah, for the general case you need to store the relation like you do right now.

Edit: I missed that you return early as soon as you encounter a ‘wobbling’ node. Is this what you intended to do? In this case you could check wobbling from BaseShape or return a boolean from wobble.