GDevelop-extensions icon indicating copy to clipboard operation
GDevelop-extensions copied to clipboard

New extension: TweenGroup

Open github-actions[bot] opened this issue 1 year ago โ€ข 18 comments

Description

Allows you to create a group of objects with tween behavior, performing tween on all of them.

How to use the extension

You need to create an object with the TweenGroup behavior. Then, you need to register some objects in it with the Register object action. These objects MUST HAVE the tween behavior. Then, you just add the wanted tween on the object with TweenGroup behavior and it will also propagate the same tween to the registered objects.

Checklist

  • [X] I've followed all of the best practices.
  • [X] I confirm that this extension can be integrated to this GitHub repository, distributed and MIT licensed.
  • [X] I am aware that the extension may be updated by anyone, and do not need my explicit consent to do so.

What tier of review do you aim for your extension?

Community (Unreviewed)

Example file

Tween Group Example.zip

Extension file

TweenGroup.zip

github-actions[bot] avatar Sep 26 '23 14:09 github-actions[bot]

Thank you for submitting an extension.

What is the difference with using a tween action on objects picked by conditions?

D8H avatar Sep 26 '23 18:09 D8H

This is to do the same tween in a group of elements. The benefits would be:

1 - encapsulation of the behavior in a single component responsible for the group 2 - cleaner list of events 3 - with it, you can chain tweens with a custom delay. So, for example, you create a group and want that all elements in it do a specific tween with 1s between each element 4 - you can do tween to relative positions, like, instead of every element goes to X = 40, make all elements add 40 to their current X position

You can check the demo for an example of how this would look.

george-gca avatar Sep 26 '23 19:09 george-gca

How do you see this extension be used? Can you explain some real-life use-cases?

I noticed some issues:

  • all object instances "register" no matter the conditions image
  • I didn't find a way to choose instances order

D8H avatar Sep 27 '23 22:09 D8H

A use case could be an inventory like menu from Zelda a link to the past. When you press a specified button, the menu would slide from one direction with an animation to the top of the screen, with the inverted animation to exit the screen. Another one would be something more cartoonish, where the player runs in one direction and some of its clothes take a little while to follow it, going piece by piece.

I don't think I understand what you mean with the first issue.

About the order of the objects, any suggestions? I could only think about the user adding them one by one in the specific order.

george-gca avatar Sep 28 '23 13:09 george-gca

A use case could be an inventory like menu from Zelda a link to the past. When you press a specified button, the menu would slide from one direction with an animation to the top of the screen, with the inverted animation to exit the screen.

I've never played this game, but I guess that to move a panel and its content together, I would use the Sticker behavior to stick the content to the panel or move a layer camera. https://wiki.gdevelop.io/gdevelop5/extensions/sticker/

Another one would be something more cartoonish, where the player runs in one direction and some of its clothes take a little while to follow it, going piece by piece.

This case is probably too specific to require an extension. I don't understand how it would be done because the character is moving so it won't stay at the tween target.

I don't think I understand what you mean with the first issue.

Let's say there are 2 rows and you want to create one group for each.

About the order of the objects, any suggestions? I could only think about the user adding them one by one in the specific order.

I guess users would have to register one instance after the other if they want a specific order. The Stack Object behavior does something like this. https://wiki.gdevelop.io/gdevelop5/extensions/object-stack/

Please feel free to challenge what I said and give other use-cases.

D8H avatar Sep 28 '23 21:09 D8H

I've never played this game, but I guess that to move a panel and its content together, I would use the Sticker behavior to stick the content to the panel or move a layer camera. https://wiki.gdevelop.io/gdevelop5/extensions/sticker/

Yes, this could be one solution, but using this extension also could be one. Of course, for this simple case, Sticker would be better.

This case is probably too specific to require an extension. I don't understand how it would be done because the character is moving so it won't stay at the tween target.

I stated this example because it was the first one that came in my mind. But think NPC for example, or a door, composed of multiple parts. When "opened", all parts would slide to the side, with a delay between them, but adding to their y position so they could keep their position one above the other. Of course it could also be done for the same position, so all the pieces collapse to the same space. The example I put in the demo was dominoes pieces falling and "pushing" the next one.

Since I created all the individual functions with some variations, basically all tweens that you could be done separately could be encapsulated into a single behavior. One thing that I did was add some variations, for example, AddObjectPositionXTween, AddObjectRelativePositionXTween, and AddObjectMultiplierPositionXTween, as can be seen below.

image

If a group contains 2 objects, one with x1 = 5 and the other x2 = 10, they would behave like this (note that there are more params to these functions, like easing function, duration, delay, etc, but I will focus on the x here):

Function Params Final Values
AddObjectPositionXTween X: 20 x1 = 20, x2 = 20
AddObjectRelativePositionXTween X: 20 x1 = 25, x2 = 30
AddObjectMultiplierPositionXTween MultiplyX: 20 x1 = 100, x2 = 200

Let's say there are 2 rows and you want to create one group for each.

For this case, there should be a group for each row.

I guess users would have to register one instance after the other if they want a specific order. The Stack Object behavior does something like this. https://wiki.gdevelop.io/gdevelop5/extensions/object-stack/

Currently the RegisterObject appends the new object to the end of the list. I could also create a function for the user to set the index if you think will be useful.

Please feel free to challenge what I said and give other use-cases.

Of course. Open development is not enforcing ideas, is discussing them to create better solutions ๐ŸŽ‰

george-gca avatar Sep 30 '23 16:09 george-gca

But think NPC for example, or a door, composed of multiple parts. When "opened", all parts would slide to the side, with a delay between them, but adding to their y position so they could keep their position one above the other. Of course it could also be done for the same position, so all the pieces collapse to the same space. The example I put in the demo was dominoes pieces falling and "pushing" the next one.

Without an extension, it could be done like this:

  • Put an hidden Door instance Behind the Bar instances
  • Set the delay (or an index) in a variable of Bar
  • Pick the Bar in collision with the Door (or use links)
  • Iterate on each instance and use the "wait" action according to the variable
  • Use Bar.X() to choose the targeted X relatively

Do you see a way the extension could make this easier?

Since I created all the individual functions with some variations, basically all tweens that you could be done separately could be encapsulated into a single behavior. One thing that I did was add some variations, for example, AddObjectPositionXTween, AddObjectRelativePositionXTween, and AddObjectMultiplierPositionXTween, as can be seen below.

This is a huge work. What is the purpose of AddObjectMultiplierPositionXTween? I mean, when does a position need to be multiplied?

D8H avatar Sep 30 '23 22:09 D8H

Without an extension, it could be done like this:

  • Put an hidden Door instance Behind the Bar instances
  • Set the delay (or an index) in a variable of Bar
  • Pick the Bar in collision with the Door (or use links)
  • Iterate on each instance and use the "wait" action according to the variable
  • Use Bar.X() to choose the targeted X relatively

Do you see a way the extension could make this easier?

Supposing that the door is composed of 3 parts (different objects), you could add a TweenGroupBehavior to the first part, then register the 2 other parts as part of that group in the beginning of the scene. When the door is opening, simply apply a AddObjectRelativePositionXTween action to the first element, setting the value that would be added to the x of all the individual parts and the delay. When wanting to move back to the previous position, do the same action giving negative x value. At least it seems simpler to me. I won't need to add actions to iterate on each instance, use wait, and other specifics, since these are all encapsulated.

This is a huge work. What is the purpose of AddObjectMultiplierPositionXTween? I mean, when does a position need to be multiplied?

I can't think of a really good example now, but think of a billiards table, but all the balls have different sizes and masses. If I hit the white ball that is huge in another ball that is medium sized, and that hits another one that is smaller, since they all have different masses and sizes, they would move a different value. The white ball would move a maximum of x, the medium one 2x, and the smallest one 3x, for example. Of course in this example all of this could be calculated with a physics engine, but it could also be made by adjusting the params of a AddObjectMultiplierPositionXTween.

george-gca avatar Sep 30 '23 22:09 george-gca

Without an extension, it could be done like this:

  • Put an hidden Door instance Behind the Bar instances
  • Set the delay (or an index) in a variable of Bar
  • Pick the Bar in collision with the Door (or use links)
  • Iterate on each instance and use the "wait" action according to the variable
  • Use Bar.X() to choose the targeted X relatively

Do you see a way the extension could make this easier?

Supposing that the door is composed of 3 parts (different objects), you could add a TweenGroupBehavior to the first part, then register the 2 other parts as part of that group in the beginning of the scene. When the door is opening, simply apply a AddObjectRelativePositionXTween action to the first element, setting the value that would be added to the x of all the individual parts and the delay. When wanting to move back to the previous position, do the same action giving negative x value. At least it seems simpler to me. I won't need to add actions to iterate on each instance, use wait, and other specifics, since these are all encapsulated.

Considering that the extension allows to register instances, the loop would still be needed to register instances. I guess it only encapsulates a mapping from an index and a delay and relative position calculus which are a multiplication and a division. To me, the extension looks overly-complicated for this. Maybe I missed something.

The event solution is probably more flexible as it allows to:

  • do some actions when each bar starts to move, play a sound for instance
  • use delays that are not linear

It also has the advantage to use concepts already known by users. I guess beginners won't find this solution easily but making an animation with pixels is probably a better solution most of the time anyway.

Do you see any other use-case?

D8H avatar Oct 01 '23 11:10 D8H

Yes, the event solution is more flexible, but sometimes you don't need all that flexibility, and just want a simpler way to do things. Also I believe this solution is easier for beginners than doing a loop every time. For instance, I still don't know how I would do a loop through various different objects. I believe I would have to create a group with them, so I could get every instance of them, right?

You are thinking isolated animations, that happens once, but think about animations that happens more than once, and different animations. You would need to do a loop for each animation, and do all those steps again and again, creating a lot of events in the event sheet. By using the behavior you only need the loop once, while registering the objects in the TweenGroup. After that, you only need to apply the tweens, and the loop per elements and other peculiarities will be hidden.

george-gca avatar Oct 01 '23 19:10 george-gca

Yes, the event solution is more flexible, but sometimes you don't need all that flexibility, and just want a simpler way to do things. Also I believe this solution is easier for beginners than doing a loop every time. For instance, I still don't know how I would do a loop through various different objects. I believe I would have to create a group with them, so I could get every instance of them, right?

Without the extension: image

With the extension: image

Both look as complicated.

You are thinking isolated animations, that happens once, but think about animations that happens more than once, and different animations. You would need to do a loop for each animation, and do all those steps again and again, creating a lot of events in the event sheet. By using the behavior you only need the loop once, while registering the objects in the TweenGroup. After that, you only need to apply the tweens, and the loop per elements and other peculiarities will be hidden.

Not really, events can be factorized using an extension dedicated to a game. For the case of the door, the events can be moved into an action "Play the Door animation with Bar".

D8H avatar Oct 01 '23 22:10 D8H

Bar in this case is an Object, or a Object Group?

george-gca avatar Oct 02 '23 00:10 george-gca

Bar in this case is an Object, or a Object Group?

An Object, but it can be a group if there is several kind of doors.

Actually for the "with extension" events, it would need to loop on an index to register instances in order (which makes it more complicated)

D8H avatar Oct 02 '23 12:10 D8H

How it would look like if you were to do that with different objects, but each with a single instance? You would need to create a group so that you could know which specific objects to use right?

george-gca avatar Oct 02 '23 14:10 george-gca

How it would look like if you were to do that with different objects, but each with a single instance? You would need to create a group so that you could know which specific objects to use right?

I suggest to never do this. If there are 20 doors in the level, it would need 20 * 4 objects and as much events. This is not scalable. Instances must be used.

D8H avatar Oct 02 '23 16:10 D8H

In the example of the door I gave, the door was composed by 3 different parts, meaning 3 different objects with different assets. That's why I was talking about different objects.

george-gca avatar Oct 02 '23 21:10 george-gca

In the example of the door I gave, the door was composed by 3 different parts, meaning 3 different objects with different assets. That's why I was talking about different objects.

Ho, ok, a Door, a Bar1, a Bar2 and a Bar3 for each door. Yes, that would work, but the 2 codes with or without the extension would still be as much complicated.

D8H avatar Oct 02 '23 22:10 D8H

With the extension would not. It could be done like I said, register at the beginning of the scene, then just run actions.

george-gca avatar Oct 04 '23 05:10 george-gca