Godot: Allow AnimationPlayer to apply all its animations to multiple Nodes

Created on 25 Sep 2018  路  24Comments  路  Source: godotengine/godot

Godot version:
3.0.6, 3.0.7, 3.1.alpha.calinou.c320d93

OS/device including version:
Win7Pro 64bit, Intel-internal/NVIDIA

Issue description:
I request a simple setting that allows me to select which Nodes should be affected by the AnimationPlayer. Right now this has do be done manually for each track and therefore becomes quickly unfeasible with large numbers of animations or tracks in the AnimationPlayer. On top of that, copying and pasting existing tracks seems broken right now in all versions tested (see my comment here )

For my 2D Projects I animate my characters using big sprite sheets. I have more than 80 animations for my main character just to cover the basics. If a character wears an item (imagine a hat or a glove) I need to be able to place another Sprite at the exact position on top (with only glove/hat visible) with the same proportions and framecount and have it play the same animations as the character Sprite.

Unfortunately my project is absolutely dependent on the ability to do this. Any sort of Feedback or solution would be highly appreciated.

animationtotwosprites
Composite Sprites has been done throughout the ages in 2D game development. It would be really a shame if Godot would not be able to do it.

archived feature proposal core

Most helpful comment

I managed to make a composite sprite animation with ease.
Maybe you are approaching the problem from the wrong side?

My scene structure is the following:

- root node
  - base sprite
    - animation player
  - overlaid sprite
    - animation player, using the exact same animations (inspected tscn file to make sure)

Each animation player has .. set as its target, which means it targets its parent. This is the default value anyway.
The root node of the scene just makes sure to run the two animation players in sync.

So, instead of using one animation player on multiple nodes, I just used multiple animation players, each for a seperate node.


If you still want to use just one animation player (which is way more convinient anyway), there is a way to do this without modifying the engine too! This might take a bit more time, but here is how I do it:

- root node
  - animation player
  - target (unnecessary, but added to make things clearer)
    - base sprite
    - overlaid sprite

All animations target the target node, which export-s a variable to be animated. The target node has a setter for this variable, and immediately updates all the sprites.
The root node can be reused instead of the target.

The target node has a script which looks like this:

tool # So it can be used in editor too
export var frame = 0 setget set_frame
# We make a new var and tell the engine that the set_frame function will handle it
func set_frame(new_frame):
    frame = new_frame # important, otherwise it won't be persisted on the target node
    $sprite.frame = frame # Update the sprites

Note that in the example, I reused all the animations by setting the "root node" of the animation player to ../target

Here is an example project with both ideas:
22421-example.zip

So this leaves me with two options:

c.) find a way around the problem with whatever tools are at hand.

All 24 comments

The AnimationPlayer has a target node. The default is its parent but it can be changed in the Inspector. Since tracks use relative paths, you can achieve this by using one AnimationPlayer per sprite you want to animate, and using the same animation in all of them.

Sure it's a hassle to manage all that, but it's a workaround that let you reuse the same animation for everything.

Thanks for replying I really appreciate it! I'm not sure I follow, though. There is no "target" anywhere in either 3.0.6 or 3.1 AnimationPlayer Inspector.
Do you mean "root"? in 3.0.6 this is set to ".." and in 3.1. to "Node" So I assume this is what you mean.

How would I share allthe same animations in all of them? I really really really want to avoid the save and load animation as this already has cost me an incredible amount of work in 3.0.6 Has the behavior changed? I do not remember reading anything about it in the blog updates. In 3.0.6 it does not behave like the save and load of virtually any other program I used in my life before. It's not really a save, it's much rather a way to externalize the animation data from the AnimationPlayer to a file. At least this is what I gathered through very painful trail and error. I want to avoid it at all costs to not ever make the same mistake again and destroy tons if not weeks of creative work.

Even if there would be another way to pass on the animation data, I do have to then trigger all AnimationPlayer nodes in code, whenever I want to change the characters Sprite, right? This would seem quite messy but maybe an acceptable hack until a proper solution is in place.

PS: I'm also confused about the "root" or "target" setting. If it is set to parent by default but actually affects the children (like a Sprite node) - why and to what would I change it so it affects also one of the root children (another Sprite node). This is really confusing.

Even though I despise the save/load animation feature, I gave it a go and tested it within in a testscene. It did not work.

  1. New Scene (Node2D)
  2. Add Sprite Node and load texture
  3. Add AnimationPlayer and add 3 seperate animations for Sprite position, rotation and size
  4. Click "Animation"->Save as ... (I tried .anim, .res and .tres)
  5. Add another Sprite and AnimationPlayer node
  6. Click "Animation" on the new AnimationPlayer2, ->Load previously saved file
  7. Click "Root" in the new AnimationPlayer2 Inspector and select Sprite2

Result:
Only one animation out of the 3 was copied. Assigning the "target" to the Sprite2 did not assign the track in the Animationplayer to Sprite2, so Sprite2 is still not animated.

Even if the last step worked, doing this for 10 Sprites sitting on top of my basic player Sprite seems completely unfeasible. Having more than 80 basic animations, I would have to repeat the saving and loading 800 times. And that's just for one character.

Yeah, I meant the root indeed (didn't remember what it was called). When you add a track it has a NodePath which is relative to this root setting. If you point it to the Sprite, you can have . as the node path, so it will change the properties of the defined root. Then you can use the same animation in another player that points to a different sprite.

My idea was to do this settings with code, if you need so many. Also, I don't know why you need 3 separate animations for position, rotation, and size.

And I don't know about the "saving and loading" multiple times, you only need to save it once and load it elsewhere a few times. Ideally you should have a base scene with the Sprites and AnimationPlayers set, then just the change the textures of the sprites to whatever you need either by making inherited scenes or changing the same scene at runtime. Only this base scene would have the animations to be set, and I doubt you have more than a dozen (even if you do, you should be able to set only to one player and duplicate it as many times as you need). You can also animate the players themselves with another one to coordinate all this.

I don't know why you need 3 separate animations for position, rotation, and size.

Just to have 3 visually different kind of animations for testing this thing.

When you add a track it has a NodePath which is relative to this root setting. If you point it to the Sprite, you can have . as the node path, so it will change the properties of the defined root.

I tried that. Set the Root in the inspector to the Sprite. On the track names it still said "Sprite:position, Sprite:modulate, Sprite:frame ... ect" It's the same even when creating a new track.

you only need to save it once and load it elsewhere a few times.

I need to save once for every animation I have in the AnimationPlayer. I have more than 80 animations for one character (each of those animations has more than one track). Whenever I clicked "save" or "save as" it only ever saved the one currently active animation out of all of them. So I would have to save it 80+ times then load it 80+ times for every Item sprite overlay I have in that character (up to 10, so 800 in total) and do all that again for ever other character, because they don't share the same animations.

The setting I request could be called "target node" for instance, placed in the AnimationPlayer inspector and by default be set to "track specific" - providing the same functionality as everything is right now.
When clicking and selecting a node, all Node pointers of all tracks of all animations inside the AnimationPlayer would be overruled. Example:

Animation(1)
--track Sprite:frame
--track StaticBody2D:position
Animation(2)
--track Poligon2D:size

are changed to:

Animation(1)
--track Poligon2D:frame (warning! Node "Poligon2D" does not have the property "frame")
--track Poligon2D:position
Animation(2)
--track Poligon2D:size

selecting multiple target nodes would make these changes:

Animation(1)
--track Sprite1, Sprite2, Sprite3, Sprite4:frame
--track Sprite1, Sprite2, Sprite3, Sprite4:position
Animation(2)
--track Sprite1, Sprite2, Sprite3, Sprite4:size

or:

Animation(1)
--track Sprite1, Poligon2D, StaticBody2D, Sprite4:frame (warning! Node "Poligon2D", "StaticBody2D" does not have the property "frame")
--track Sprite1, Poligon2D, StaticBody2D, Sprite4:position
Animation(2)
--track Sprite1, Poligon2D, StaticBody2D, Sprite4:size

The original "track specific" data should be remembered, so when changing back to "track specific" in the AnimationPlayer Inspector all track specific pointers are restored. Ideally, when changing back to track specific, there is a option popping up:
"restore last track specific pointers _OR_ make current pointers track specific"
So the user could go back in and make detailed changes or fix the warnings by simply manually editing the tracks, just like it is possible right now:

Animation(1)
--track Sprite1, Sprite2:frame
--track Sprite2, Poligon2D, StaticBody2D, Sprite4:position
Animation(2)
--track Sprite2, Poligon2D, StaticBody2D, Sprite4:size

I did some simple math: Lets say, hypothetically, someone in the dev team would be interested in implementing this feature: Until 3.1. we have a feature freeze. So even before anyone will start looking at it, that is if someone is interested at all, at the very least a couple weeks if not a few more months have passed. Then the implementation will take a bit of time, Pull request, commit, testing, and even if bug free, won't be in a stable built until 3.1.X
Which might be even more months from now.
Right now all my development is halted as having Composite Sprites is principal core of my Project .. and quite frankly anything else I wanted to do with this engine too. Being such a traditional technique, and Godot advertised as strong in 2D disciplines, I did not expect to find it missing. I should have done more research.
So this leaves me with two options:
a.) Try to tinker with the source code myself even though I have no idea about C or C++, or successful experience how to compile source code and potentially waste all those months by failing eventually and burning through my last financial resources without getting even a single baby step further with this project. Or,
b.) crestfallen turn my back to this new found love named Godot and look for another Engine. : (((

I managed to make a composite sprite animation with ease.
Maybe you are approaching the problem from the wrong side?

My scene structure is the following:

- root node
  - base sprite
    - animation player
  - overlaid sprite
    - animation player, using the exact same animations (inspected tscn file to make sure)

Each animation player has .. set as its target, which means it targets its parent. This is the default value anyway.
The root node of the scene just makes sure to run the two animation players in sync.

So, instead of using one animation player on multiple nodes, I just used multiple animation players, each for a seperate node.


If you still want to use just one animation player (which is way more convinient anyway), there is a way to do this without modifying the engine too! This might take a bit more time, but here is how I do it:

- root node
  - animation player
  - target (unnecessary, but added to make things clearer)
    - base sprite
    - overlaid sprite

All animations target the target node, which export-s a variable to be animated. The target node has a setter for this variable, and immediately updates all the sprites.
The root node can be reused instead of the target.

The target node has a script which looks like this:

tool # So it can be used in editor too
export var frame = 0 setget set_frame
# We make a new var and tell the engine that the set_frame function will handle it
func set_frame(new_frame):
    frame = new_frame # important, otherwise it won't be persisted on the target node
    $sprite.frame = frame # Update the sprites

Note that in the example, I reused all the animations by setting the "root node" of the animation player to ../target

Here is an example project with both ideas:
22421-example.zip

So this leaves me with two options:

c.) find a way around the problem with whatever tools are at hand.

Thank you so much for your effort to write this detailed reply and even providing an example! I will try both options and will report back.

First of all, let me tell you I don't think I can thank you enough! After already fighting this for almost two weeks, I now spend 2 days looking at the two approaches you proposed made many test projects. Thanks to you I think I have found a way to go forward! I think it is save to say I would have never gotten even remotely close to a workaround without your support and the generous example you provided!! Thank you so much!!

If possible, I would still like to keep this issue open and hope it might find it's way into a future release, as I believe my proposed request would speed up and secure the workflow massively, and make if far more intuitive and approachable for beginners. It also has more then just the Composite Sprites use case imho.


Day 1
I tried to figure this out by myself, meaning follow your proposals but without yet studying your example:
Approach 1, in Godot 3.0.7:
Worked partially, because I cannot figure out how to save all animations at once.
So the issue described above is still present: Having to save 80 times (every time typing in the name of the animation), then load 80*10 so 800 times to get all the animation for all item slots. Assumed there are only ten. This is for one character only. In my Project I have 5 Characters. Also having all those animations externally and not in the editor and spread out over multiple AnimationPlayers is just an invite for trouble.
Also I have to admit I'm having troubles to write the code for playing one specific animation in all those AnimationPlayers simultainously. I used to write onready var anim = get_node("AnimationPlayer") and then anim.play("nameofanimation"). My attemps with get_children() were unsuccessful.

Approach 1, in Godot 3.1 alpha:
This did not work at all when I previously tried in 3.1. even though I believed I tried exactly what you proposed here. Now I archived the same result as above in 3.0.7.
I experimented with the setup now for a few hours trying to understand my previous complete failing. I think three things happened: 1: I thought I needed to set the root to "..", which is impossible in 3.1. The only options are so select a node, or to delete the selection by pressing the icon next to the selected node. 2. I previously did not have the AnimationPlayer as a child of the Sprite because when I did, the animations where not playing anymore. 3: I thought I failed when I actually did not, having set the AnimationPlayer correctly, because the Animation panel shows no indication which AnimationPlayer node it currently displays other than the name of the Node it points to at the beginning of the track. This updates only when clicked directly on the AnimationPlayer in the Scene Panel, not when clicking on the parent Sprite node in the Scene panel, which is what I did to change the animation keyframes and verify if they update in both AnimationPlayer. So while this seems very difficult to figure out for a newcomer without a tutorial, it does work as you described. Still, the same issues as in 3.0.7 persist: I do not have a solution to avoid saving and loading 800 times per character, avoid to externalize all the animations to voluntary files and am yet unclear how to play the same animation in all those AnimationPlayers from code.

Day 2
Still trying to see if I can figure it out by myself. I have not looked at your example yet.
Approach 2, in Godot 3.0.7:
Whatever I tried, I failed completely. However I do feel having one AnimationPlayer is the way forward so I'm giving up my hopes to figure this out and I open up your example.
Here I managed to remove the shared animations, remove Approach1 and create a new AnimationPlayer For Approach2 with new animations all within the single AnimationPlayer and create duplications of the prison_jumpsuit Sprite I modulated to have different colors. Added more buttons to change the visibility on/off off for each colored prison_jumpsuit.

func _on_upnocloths_pressed():
$player2/target/prison_jumpsuit.visible = false
$player2/target/prison_jumpsuit_green.visible = false
$player2/target/prison_jumpsuit_red.visible = false

func _on_rightwhite_pressed():
$player2/target/prison_jumpsuit.visible = true
$player2/target/prison_jumpsuit_green.visible = false
$player2/target/prison_jumpsuit_red.visible = false

func _on_leftgreen_pressed():
$player2/target/prison_jumpsuit.visible = false
$player2/target/prison_jumpsuit_green.visible = true
$player2/target/prison_jumpsuit_red.visible = false

func _on_downred_pressed():
$player2/target/prison_jumpsuit.visible = false
$player2/target/prison_jumpsuit_green.visible = false
$player2/target/prison_jumpsuit_red.visible = true

So I can now individually switch frame animations but simultaneously for all Sprites, and turn their visibility on or off whenever needed! This was the basic functionality I was looking for so long!! Thank you so much!
I also tied to animate other properties of the Sprites simultaneously, that are not in a the target Node2D, like Offset or Texture, but while it did not seem entirely impossible, it would require more code in the target.gd script.
I cannot say how happy I am that I do not have to "save" or externalize my animations and am able too keep them all in one Animation player!! This is a beautiful day!
I'm not testing it in 3.1. because I can't wait to bring it over to my actual project.

From my point of view the solution proposed there in the comments on Github creates overhead and complicates the structure of your project. The result is much less flexible. Right now, I have mine set up very similarly, but it feels like a bad workaroud. Some features of the AnimationPlayer don't work like that anymore. (Like automatically jumping to the next key and value after you have set one key- makes the whole keyframing process a lot more tedious, exponentially more so the more keys you have.)

If you want to add Sprites with smaller or bigger atlases, you have to also duplicate the controller node. Else I get an error because I run out of frames on your spritesheet. So for every Sprite that's a bit different needs another controller. And you have to make sure the script of every controller node manages all underlying sprites properly.

It's just a lot more work, less stable and more complicated.

And all that trouble would be gone if we had the functionality to assign an animation track not just to one specific property, but to however many we like in our scene, as long as they of are the same kind.

Maybe there could be a "linked track" which references another track of the animation. It would have no keyframes of its own, and might just show ghosted previews of the main track. WDYT?

We definitely need this.

@bojidar-bg

It would have no keyframes of its own, and might just show ghosted previews of the main track.

Why creating more clutter? It could be so simple form a user experience: Just click the little plus, select the node you want to add (those you cannot add are marked red) and BAM, done.
Mockup:
addtotracksofthesamekind1

In the .tscn all it would do is add another reference:
tracks/0/path2 = NodePath("some_other_Area2D/CollisionPolygon2D:polygon")
tracks/0/path3 = NodePath("yet_another_CollisionPolygon2D:polygon")

Best thing about this: It would be pretty much self-explanatory. Try to explain a "ghost track" within the UI so people know immediate what it does just by looking at it. The less tutorials we need to explain functionality, the better.

So was there any progress on this? Or has anyone implemented this for himself by any chance? I would love this feature just like @golddotasksquestions described it.

@Mastermori As far as I know, nobody has started working on this feature.

@Mastermori I still need it but I don't have the skills unfortunately, otherwise I would have already tried to implement this.

Ok, so is this related to why I have this problem?
I made an animation with 4 rocks falling in Blender. Exported the whole section of my track, collisionshapes and animation to glTF and than imported that into Godot and saved it as a new scene. All the falling rocks are StaticBody's, since after they have fallen, they have to obstruct the racer.
When the car enters the Area, the animation starts. Problem is as soon as the the 2nd rock starts its animation, everything comes to a halt and the 1st rock is idle in the air.
I have made the animation in Blender so, that if rock 1 starts falling at frame 1, rock 2 starts falling at frame 8, rock 3 at frame 11, ... . So I need them all to start at the same time.
And why does it actually make them seperate animations? Why can't each rock be a seperate track in that animation (on import)?
And animating them in Godot is not a solution for me, I animate in Blender.

fallingRocksAni3

I am having the same issue. Have you come across any new information?

No, no new information.
I had to make 4 separate animation-players and load each action to one of them. Don't like this solution though. It should be possible to just add tracks based on the available actions.
As it is now, it seems very restrictive when you animate outside Godot.

@eyeEmotion for what you are describing you only need one animationplayer and one track for each rock. If it's the same animation for each rock you can just duplicate the track of the first, change the root property of that track to the second rock and offset all the keyframes. Then you can have rock 1 fall at frame one, rock 2 at frame three and so on.
You just can't start multiple animations on the same player, as it will switch the animation, not run them parrallel.

@eyeEmotion I could also get the same effect to work with transition times. You just need to set the transition time between the first and the second; the second and the third and so on to the length of the animation.
In my case the first animation is 1 second long, the second one is 1.5 seconds long and is offset by .5 seconds. Therefor the transition time from animation one to two is set to 1 second and I can start them at the same time, making them both fall.

Feature and improvement proposals for the Godot Engine are now being discussed and reviewed in a dedicated Godot Improvement Proposals (GIP) (godotengine/godot-proposals) issue tracker. The GIP tracker has a detailed issue template designed so that proposals include all the relevant information to start a productive discussion and help the community assess the validity of the proposal for the engine.

The main (godotengine/godot) tracker is now solely dedicated to bug reports and Pull Requests, enabling contributors to have a better focus on bug fixing work. Therefore, we are now closing all older feature proposals on the main issue tracker.

If you are interested in this feature proposal, please open a new proposal on the GIP tracker following the given issue template (after checking that it doesn't exist already). Be sure to reference this closed issue if it includes any relevant discussion (which you are also encouraged to summarize in the new proposal). Thanks in advance!

Was this page helpful?
0 / 5 - 0 ratings