Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Tween.tween_subtween method for nesting tweens within each other #98660

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

Meorge
Copy link
Contributor

@Meorge Meorge commented Oct 30, 2024

This is an implementation of godotengine/godot-proposals#11022, which adds the tween_subtween method to the Tween class. Using this method, Tween objects can be nested within each other and scheduled to play at specified points.

It's not fully complete yet (see the to do list below), but I wanted to get a draft posted with the core functionality so that others could give their thoughts on it. I'm especially unsure about the method used to remove a Tween from the SceneTree's tweens list once it is made into a subtween.

Example usage

extends Sprite2D

var speed := 5.0

func _ready():		
	# Holds the sequence of 5-then-6
	var subtween_5_6: Tween = create_tween()
	subtween_5_6.tween_property(self, "rotation_degrees", 80.0, 0.4 * speed) # 5
	subtween_5_6.tween_property(self, "rotation_degrees", -20.0, 0.6 * speed) # 6
	
	# Holds subsequence of 4 at the same time as 5-then-6
	var subtween_4_5_6: Tween = create_tween()
	subtween_4_5_6.tween_property(self, "position:x", 100, 1.0 * speed) # 4
	subtween_4_5_6.parallel().tween_subtween(subtween_5_6) # 5-then-6

	# Overall tween
	var tween: Tween = create_tween()
	tween.tween_property(self, "position:x", 100, 1.0 * speed) # 1
	tween.tween_property(self, "position:x", 300, 0.5 * speed) # 2
	tween.parallel().tween_property(self, "rotation_degrees", 45.0, 0.3 * speed) # 3
	tween.tween_subtween(subtween_4_5_6) # 4 and 5-then-6
Godot.Tween.Subtween.Video.1.mp4

Example project

tween-subtween.zip

Inside this project are several *_test folders; you can open them and run the scenes inside to see demos of the method in action.

To do

  • Confirm that the approach to removing Tweens from the SceneTree is okay, or improve it if necessary
  • Add functions for parity with other Tweener subclasses
    • set_trans Infeasible due to the way default transitions and tween evaluation is implemented
    • set_ease Infeasible due to the way default transitions and tween evaluation is implemented
    • set_delay
    • set_custom_interpolator May be out of scope for now, and not necessary enough (possibly best to implement on Tween itself?)
  • Confirm that the behavior of Tween features like loops, play, pause, etc doesn't break when in a parent Tween
    • Loops (note that an infinitely looping subtween will cause that step of the parent tween to never finish)
    • Play/Pause (note that a paused subtween will cause that step of the parent tween to never finish)
    • Stop
    • Kill
    • Set speed scale
  • Reset subtween when parent tween resets in loop
  • Documentation
    • Add a code example in the documentation for Tween.tween_subtween
    • Add note to Tween.play and Tween.pause that a paused subtween will result in the parent tween never moving past that step
    • Add note to Tween.set_loops that an infinitely-looping subtween will result in the parent tween never moving past that step
    • Add notes to Tween.set_pause_mode and Tween.set_process_mode that they will not have an effect when the tween is a subtween

`set_ease`, `set_trans`, and `set_delay`
Documentation only for `set_delay` so far, since I have tested it
@Meorge
Copy link
Contributor Author

Meorge commented Oct 31, 2024

I was hoping to implement set_ease, set_trans, and set_custom_interpolator methods for the SubtweenTweener, but it looks like due to the way Tweens are implemented and run under the hood, they may not be feasible. Specifically, as far as I understand the Tween::step method accepts a delta value in seconds to move forward by. If we could compute, ahead of time, the total amount of time the tween would take, then I think we could remap this value into another ease/transition type, but this might also be fragile (for example, I think the duration of a tween could be modified by changing the speed scale while it is running?)

The way that set_trans and set_ease are implemented in the Tween class right now, it appears they only set the default ease/transition for Tweeners added after those methods are called. So, in this example:

var tw := create_tween()
tw.tween_property(self, "position:x", 100, 1.0)
tw.set_trans(Tween.TRANS_SINE)  # no effect!

The object would still move with linear motion, as if the set_trans call was not there. The way the documentation describes this is a bit misleading, then, in my opinion:

Sets the default transition type for PropertyTweeners and MethodTweeners animated by this Tween.
If not specified, the default value is TRANS_LINEAR.

I might go and submit a separate PR to make this more clear. Anyways, because of this, the following doesn't work, at least in the way I would intuitively expect:

Ref<SubtweenTweener> SubtweenTweener::set_trans(Tween::TransitionType p_trans) {
	subtween->set_trans(p_trans);
	return this;
}

With this, if you start adding Tweeners after calling set_trans, then they do have the new transition type, but I don't think that's the behavior people would expect.


All this to say, while I think it would be nice to have support for methods like set_ease, set_trans, and set_custom_interpolator to control the overall behavior of a subtween, there doesn't appear to be a good way to do it; it would likely require rewriting how some of the Tween class itself works, which is beyond the scope of this PR. The set_delay method appears to be working, though, at least!

As such, for now I think I'll just go ahead with only having the set_delay method, and then in the future if the Tween class is rewritten to be more reusable/nestable/etc, we can revisit adding this functionality to SubtweenTweener.

@Meorge
Copy link
Contributor Author

Meorge commented Nov 2, 2024

I've created a small project with several testing scenes, so people can see how it behaves and what the corresponding GDScript code is like: tween-subtween.zip I'll also add this to the original post so it's easily accessible.

With this complete, I think the PR is ready to be more throughly looked at, so I'll open it for review!

  • I'm still not 100% sure if the approach for removing the Tween from the SceneTree is fine, or if there'd be a better alternative.
  • I'm also starting to wonder if a name other than tween_subtween should be used, such as tween_child or tween_nested. One of these other names would allow us to refer to "nested Tweens" or "child Tweens" as opposed to "parent Tweens" in the documentation - right now it's "subtweens" and "parent Tweens", which is a bit asymmetrical.

@Meorge Meorge marked this pull request as ready for review November 2, 2024 04:16
@Meorge Meorge requested review from a team as code owners November 2, 2024 04:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a tween_subtween method for nesting Tweens
2 participants