Hi there,

the documentation at http://docs.godotengine.org/en/3.0/classes/class_tween.html says:

bool follow_property ( Object object, NodePath property, Variant initial_val, Object target, NodePath target_property, float duration, int trans_type, int ease_type, float delay=0 )

Follows property of object and applies it on target_property of target, beginning from initial_val for duration seconds, delay seconds later.

What I would expect from this description is that the Tween would continuously monitor object's property and -- also continuously -- apply its current value to target's target_property after modulating it according to the remaining parameters.

I already stumbled over the documentation issue described in https://godotengine.org/qa/12337/troubles-with-tween-follow_method, so interchanging source and target object/property did have some effect. But what I really don't get is the "for duration seconds" part. Does this mean

a) that the Tween will monitor object's property for duration seconds only b) that, whenever object's property changes, the Tween will take duration seconds to adjust target's target_property accordingly c) that I got it completely wrong and something completely different happens?

Thanks for clarification!

Cheers --

Torsten

Hi,

answer b) is what you are trying to get, isn't it? You have to put any signal for the state "objects property changed". The signal has the purpose to change tween settings (start property, end property, duration) and start that tween. Duration in seconds means: How long it takes to change the property from start to end. The shorter that duration, the faster that animation.

Kryzmak,

@kryzmak said: answer b) is what you are trying to get, isn't it? You have to put any signal for the state "objects property changed". The signal has the purpose to change tween settings (start property, end property, duration) and start that tween.

you're right, the behavior described under b) is what I'm after. However, I don't quite understand what you mean by 'put any signal for the state "objects property changed'. According to my research, Godot doesn't (yet) feature a built-in mechanism for automatically notifying other objects about a property's change -- or does it? Instead, I would have to

  • explicitly declare a property_changed signal on the source object
  • emit that signal on changing the property (possibly from within a setter method)
  • let the Tween subscribe to that signal
  • (re-)start an animation whenever the Tween receives the signal

But all of this could be done without making use of a Tween.follow_property method. So I guess I am still missing the point -- what is follow_property actually good for and how do I leverage its assumed power?

Cheers --

Torsten

It is very theoretical without any code but you could for example check in _process() loop if a property has changed compared to the previous frame. Or maybe there is some "active event" which changes the source property for some reason. In that case you could as well let that custom signal emit. You are right, that there is no "built in" signal for property changed. You have to "create" a custom one.

So to your specific question about "What is follow_property exactly for?" Unfortunately I cannot answer this because I never used that specific method, sorry.

Kryzmak,

@kryzmak said: It is very theoretical without any code but you could for example check in _process() loop if a property has changed compared to the previous frame. Or maybe there is some "active event" which changes the source property for some reason. In that case you could as well let that custom signal emit. You are right, that there is no "built in" signal for property changed. You have to "create" a custom one.

yeah, I guess it should be straightforward to implement something like this. However...

So to your specific question about "What is follow_property exactly for?" Unfortunately I cannot answer this because I never used that specific method, sorry.

...I was hoping follow_property would prevent me from having to implement all that boilerplate code required for, well, following a property.

Thanks anyway for your valuable input! I hope you don't mind that I'll leave my question in "unanswered" state since though you gave a reasonable answer on this thread's subject, I still hope to get enlightened by someone regarding the usage of Tween.follow_property.

Cheers --

Torsten

2 years later

I came across this thread while looking for info on how to use Tween.follow_property(). Like what is discussed above, I also thought that follow_property() would automatically detect changes in the target property. However, I'm convinced that unfortunately it's just a minor variation of interpolate_property().

The two functions signatures are very similar, the only difference is that in interpolate_property you give a specific end value, while in follow_property you instead specify an object and its property.

So, the following two code samples would be interchangeable:

	var start_value = node.rotation_degrees
	var end_value = target.rotation_degrees
	tween.interpolate_property(node, "rotation_degrees", start_value, end_value, 1.0, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()

	var start_value = node.rotation_degrees
	tween.follow_property(node, "rotation_degrees", start_value, target, "rotation_degrees", 1.0, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()

As already discussed, the follow function isn't as smart as its name suggests, and you have to set up a mechanism for detecting the changes of its target. However, this might not be so much boilerplate required as one would thought. Just identify where the changes are actually happening, that is, where and what in the code is indirecting causing the changes, and put signals on that node/object.

For example, in my 3D game I want the rotation of an arm of a character to follow the rotation of the character's neck. So when a character is looking in a certain direction, the raised arm should rotate towards that direction.

The solution became simpler than I would have thought. I put scripts on the Arm and the Neck. The main part of the code is coded in the Arm script. The arm can be raised and lowered. When raised, I connect to the Neck's signal, when lowered I disconnect.

The script on the arm:

extends Spatial

const READY_SPEED = 0.75 # (seconds)
const FOLLOW_SPEED = 0.15 # (seconds)

onready var tween = $Tween
onready var neck = get_node("../Neck")

var ready = [false, false] # left, right raised and ready 

func follow_neck_rotation(node):
	var start_degrees = node.rotation_degrees
	var end_degrees = neck.rotation_degrees + Vector3(90, 0, 0)
	tween.interpolate_property(node, "rotation_degrees", start_degrees, end_degrees, FOLLOW_SPEED, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()
	pass
	
func raise_arm(node):
	# initial ready sequence
	var start_degrees = node.rotation_degrees
	var end_degrees = neck.rotation_degrees + Vector3(90, 0, 0)
	tween.interpolate_property(node, "rotation_degrees", start_degrees, end_degrees, READY_SPEED, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()
	
	# connect to neck rotation
	neck.connect("looked_left", self, "_on_Neck_looked_left")
	neck.connect("looked_up", self, "_on_Neck_looked_up")
	pass
	
func lower_arm(node):
	# disconnect neck rotation
	neck.disconnect("looked_left", self, "_on_Neck_looked_left")
	neck.disconnect("looked_up", self, "_on_Neck_looked_up")
	
	# final unready sequence
	var start_degrees = node.rotation_degrees
	var end_degrees = Vector3(0, 0, 0)
	tween.interpolate_property(node, "rotation_degrees", start_degrees, end_degrees, READY_SPEED, Tween.TRANS_LINEAR, Tween.EASE_IN_OUT)
	tween.start()
	pass
	 
func raise_left_arm():
	if not ready[0]:
		raise_arm($Left)
		ready[0] = true
	else:
		lower_arm($Left)
		ready[0] = false
	pass
	
func raise_right_arm():
	if not ready[1]:
		raise_arm($Right)
		ready[1] = true
	else:
		lower_arm($Right)
		ready[1] = false
	pass

func _on_Neck_looked_up():
	var node = $Left if ready[0] else $Right
	follow_neck_rotation(node)
	pass 

func _on_Neck_looked_left():
	var node = $Left if ready[0] else $Right
	follow_neck_rotation(node)
	pass 

I actually can't use follow_property() because I need to add an additional 90 degrees to the arm. So I ended up using only interpolate_property().

The script on the neck:

extends Spatial

signal looked_up
signal looked_left

const LOOKAT_MAX_ANGLE_X = 60 # rotation up/down
const LOOKAT_MAX_ANGLE_Y = 75 # rotation left/right

func look_up(angle):
	# rotate neck up/down
	rotation_degrees.x -= angle
	if rotation_degrees.x > LOOKAT_MAX_ANGLE_X:
		rotation_degrees.x = LOOKAT_MAX_ANGLE_X
	if rotation_degrees.x < 0 - LOOKAT_MAX_ANGLE_X:
		rotation_degrees.x = 0 - LOOKAT_MAX_ANGLE_X
	emit_signal("looked_up")
	pass
	
func look_left(angle):
	# rotate neck left/right
	rotation_degrees.y -= angle
	if rotation_degrees.y > LOOKAT_MAX_ANGLE_Y:
		rotation_degrees.y = LOOKAT_MAX_ANGLE_Y
	if rotation_degrees.y < 0 - LOOKAT_MAX_ANGLE_Y:
		rotation_degrees.y = 0 - LOOKAT_MAX_ANGLE_Y
	emit_signal("looked_left")
	pass

So the signals are put in the neck's functions where the rotation is indirectly changed. It's not needed to try to somehow catch the changes in rotation_degrees directly.

The neck is then rotated in the player input code.

In Player.gd:

func _input(event):
	# look at
	if Input.get_mouse_mode() == Input.MOUSE_MODE_CAPTURED:
		if player_camera.current:
			if event is InputEventMouseMotion:
				# rotate neck up/down
				if event.relative.y:
					character.neck.look_up(event.relative.y * LOOKAT_SPEED)
				if event.relative.x:
					character.neck.look_left(event.relative.x * LOOKAT_SPEED)

For NPC characters I will implement neck rotations somewhere in their AI code, but I havn't come to that yet.

Here's a short YouTube video showing how it finally looked like. My video recording skills are not the very best, but I hope it shows how the raised arm is synced with the head movements.

https://youtu.be/4I6IE1gDajI

This thread is two years old so I'm not sure how much my experience with this matter helped. But I thought it could be worthwhile to post this comment still, in case someone else runs into issues when trying to get properties follow each other.

2 years later