You can set everything to the setup pose, as SilverStraw showed, or use an animation. An animation is more efficient since it only needs to reset the values that are changed additively. That might matter if the rest of the skeleton is complex.
[Godot] Additive Mix Blending Bug After Track Entries Completed
I've also been having a problem implementing mixblending into my code, it seems like a pretty simple task as I can get it to function properly in the preview window in the spine editor. I've tried a lot of different methods but nothing seems to work, either it isn't additive and it just ends up overwriting a previous track, or it has the same problem of scaling into infinity, or when I reset/try to mix out with empty tracks afterward nothing ends up being applied at all. For something so simply done in the editor I would think it would be pretty easy to communicate to new users how to integrate something like this in Godot.
I'm not sure if I have to have empty tracks first, to use a trackentry instance and then set mixblending there (which doesn't seem to do anything), use .SetMixBlend on the track entry, on an emptyanimation track, or the animations themselves, whether to use the timeline api or continue using animationStateData. I understand that the general runtimes API can only be so specific/detailed when it's supposed to pertain to every unique game engine but I can't deny feeling kinda frustrated spending hours trying to figure out even the most basic functions.
You need to set TrackEntry mixBlend to MixBlend.add. After that you need to ensure that properties keyed in an animation that is applied additively are reset each frame.
If you are seeing keyed properties increasing (or decreasing) each frame, then it sounds like you are using MixBlend.add
but are not resetting the keyed properties each frame. The easiest way to do that is to reset the skeleton to the setup pose with Skeleton setToSetupPose.
You could also apply an animation on a lower track that resets the properties. To create that animation, in Spine: space to deselect so all timelines are visible, ctrl+shift+L in the dopesheet for the animation applied additively to key all visible, ctrl+C to copy those keys, ctrl+Z to remove them. Create a new animation, advance the timeline to > frame 0 (say frame 10), ctrl+V to paste, move the timeline to frame 0, ctrl+shift+L to key all visible at the setup pose, then delete all the keys on frame 10. Now you have an animation that keys everything to the setup pose that the additively applied animation keys. Apply the "reset" animation on a lower track than the additively applied animation.
While the Preview is close to how things work at runtime, it's not identical. We have an issue to improve that:
EsotericSoftware/spine-editor547
Idk if I'm just dumb but what am I doing wrong here? None of these solutions seem to fix the infinite scaling and I still have no clue how I'm supposed to integrate this, should I set tracks 3 and 4 to my setupPose/reset animation (even though it's identical to the setup pose so I don't see how that's preferable to SetToSetupPose()), should I not be doing this in an if statement under PhysicsProcess (idk why that would matter since the code is only being executed in a single frame), setting the mixblend back to setup seems to do literally nothing, should I also be using emptyAnimation tracks? Should I have a blanket reset code happening in Process/PhysicsProcess outside the if statement in case the if statement is ignoring the reset to setup?
Alyria even though it's identical to the setup pose so I don't see how that's preferable to SetToSetupPose()
setToSetupPose
resets everything to the setup pose. This can be a lot more than is necessary, as it's rare (and discouraged) to key everything.
Alyria setting the mixblend back to setup seems to do literally nothing
It needs to be set to add
to get additive blending. You won't get additive blending when you set it to setup
.
setToSetupPose
doesn't use TrackEntry at all. AnimationState uses TrackEntry to keep track of which animations are playing on a track, the times, etc. Changing MixBlend around a call to setToSetupPose
will not affect setToSetupPose
.
should I also be using emptyAnimation tracks
Empty animations are used to mix "in" from the setup pose to an animation, or "out" from an animation to the setup pose. It has nothing to do with additive animations.
Should I have a blanket reset code happening in Process/PhysicsProcess outside the if statement in case the if statement is ignoring the reset to setup?
I'm not sure what "blanket reset code" would be, but setToSetupPose
is not being ignored (and in general it doesn't make sense to think that code will be ignored). I'll explain below.
idk why that would matter since the code is only being executed in a single frame
There's lots of other code getting executed every frame. When the mouse button is pressed you set some animations. Your code for that looks fine (in the first image). Don't set MixBlend to setup
and remove setToSetupPose
. Next your app continues to run. Every frame AnimationState is applied to pose your skeleton with the animations, then Skeleton updateWorldTransform
calculates world bone transforms, then the skeleton's region and mesh attachments are rendered. When the animations are applied, since you have set MixBlend add
, the animation values are added to the skeleton's pose values instead of overwriting the current values. That causes the values to increase every frame.
To fix it, you can call setToSetupPose
, but you need to do this every frame, not just when the mouse button is pressed. Also, you need to do it before animations are applied, otherwise you will lose the pose from the animations.
Alternatively, add your "reset" animation on eg track 3 (assuming you use tracks 0, 1 and 2 for other, non-additive animations), then put your additive animations on higher tracks, like tracks 4 and 5.
I get that it's being applied every frame but after the mixing from the (AddAnimation 0.5seconds) I thought I could just reset it in the same if statement instead of having a "blanket" setToSetupPose outside of the if statement (meaning it will occur every frame in code since it's in PhysicsProcess and can account for any weird additive usage elsewhere). I put setToSetupPose outside but still have issues, if I use track 4 and 5 instead, should I mix in my setup pose animation on track 3 and then mix out my two recoil animations on 4 and 5 with emptyanimation tracks? All I want to do is play two very quick recoil animations with additive blending activated (I have them split so I can use different configurations of vertical and horizontal recoil for different weapons) and then immediately stop playing them like in the spine preview window.
- संपादित
while (true) { // Each frame:
if (mouseClicked) {
animState.addAnimation(...); // Set new animations.
x = setup.value; // reset
}
x += additiveAnimation.value;
}
In this psuedo code x
represents a skeleton property and will increase every frame. Resetting x
needs to be moved outside the if
statement, so it happens every frame:
x = setup.value; // reset
x += additiveAnimation.value;
The interesting bit for additive animations is when other animations animate the values, then the additive animation is added to that:
x = setup.value; // reset
x = otherAnimation.value;
x += additiveAnimation.value;
This way your recoil can be on top of any animation. In this case you don't need to reset the value because the other animation is setting it. However, you may have many other animations and in all those you'd need to remember to key all the values used in your additive animations. When you forget, your additive animations will increase the value every frame. Resetting with setToSetupPose
or an animation ensures that won't happen.
Alyria should I mix in my setup pose animation on track 3 and then mix out my two recoil animations on 4 and 5 with emptyanimation tracks?
You don't need to mix in the reset animation. It probably sets the values to the setup pose. If your "main" animations (run, jump, etc on lower tracks) don't key the values keyed in the additive animations, they will be set to the setup pose by the reset animation without mixing in with an empty animation. If your lower track animations do key those values and you apply the reset animation on top, the values would snap to the setup pose + the recoil animations. It may be better to set your tracks like this:
0: the "reset" animation
1: main animations: idle run, jump, etc
2: recoil x (additive)
3: recoil y (additive)
This way if the main animations key the values changed additively, you get those keys + recoil. If the main animations don't key those values, you get the setup pose (from the reset animation) + recoil. You never get the values increasing every frame.
So wait you never turn off the additive you just have a setup animation that keeps resetting stuff on a previous track, and additive sounds like it's universal for all tracks not just a select few tracks like it's represented in spine? Why does this work so differently to how it functions in the editor, I feel it's setting up a completely different expectation for a lot of users. I get runtime is very different from what an editor can show you but this seems needlessly complicated for what I'm trying to do. I tried putting my setup/reset animation on track 0 (though it's a little more complicated with my setup since I have two setup poses for the front and back view of my character) with everything else on top but I still had the same scaling issue and it wasn't showing any sign of the actual recoil animations even with the additive infinity scaling. Maybe I can key more things on my setup/reset animations but even then it seems like the recoil animations aren't even going through at all. Do you mind if I send my project files over or should I just scrap having recoil animations through spine since this seems like a really unreliable process, especially if my rig/animation setup gets even more complicated later on.
Alyria you never turn off the additive
If you turned it off, you would get the pose from the animation rather than having it added to the current pose.
Alyria additive sounds like it's universal for all tracks not just a select few tracks like it's represented in spine
There's nothing universal about it. Only a TrackEntry with MixBlend add will add the animation values to the current pose.
Alyria Why does this work so differently to how it functions in the editor
The editor uses setToSetupPose
each frame.
Alyria this seems needlessly complicated for what I'm trying to do
Calling setToSetupPose
each frame is not complicated. Resetting with an animation is a bit more effort.
Alyria but I still had the same scaling issue
If you are still seeing the scale grow every frame, then you are definitely not resetting the scale each frame.
Alyria Do you mind if I send my project files over
We would look at them if it would help, but I don't think it would in this case. The problem is that you are not resetting the scale that is being added to each frame by the additive animations. Get it working with setToSetupPose
before using an animation to reset.
Alright so after a two week break from game dev I came back to test some more of my code and try to get to the bottom of this (sorry about my frustrations the other week I was rushing myself for no reason and got really impatient). After further testing I was able to stop the infinite scaling/additive from being applied every frame by putting setToSetupPose outside the IF statement(in PhysicsProcess) like you suggested. I had already done this earlier in the thread but I probably should have specified in my previous post that while it fixed the scaling issue it brought a few new problems with it as it wasn't a perfect solution.
Because this is a recoil animation, I need this animation to be over very quickly, which means no mixing when adding/setting the animation and only a very small amount of mixing when mixing out (currently I'm mixing out using two empty animation tracks. However when I have the mixduration (or delay, they both seem to have the same problem) on the Add/SetEmptyAnimation set to a very small number say below 0.5, the animation seems to rapidly transition between the two states (seems like every frame) and it seems to do this for about half second before resetting to normal. I could try using godot's timers instead of mixduration/delay since it doesn't seem to be working properly when both the add recoil/add empty track logic is in the same IF statement. Is there something else I should be doing for mixing out the recoil in this situation?
The second problem is that my aiming logic is completely invalidated by the setToSetupPose being processed every frame, which is uh, kinda a big problem for a twin stick shooter. The projectiles are only being shot from 1 angle (which is flipped if you're looking left). I tried using both a SpineSlotNode and SpineBoneNode (they both work the same for aiming projectiles since you're simply taking their rotation and position) and they both produce the same result. I would think the aiming be limited to at least 2 angles depending on if you're looking down or up (front and back view have different aiming animations). Any ideas how both of these problems might be alleviated while still having the ability to have additive recoil animations?
This is what I have for code atm
and here's a small clip of what's happening (the rapid switching is a lot less pronounced in the clip probably because of the low frame rate the vid is set to)
Alyria when I have the mixduration (or delay, they both seem to have the same problem) on the Add/SetEmptyAnimation set to a very small number say below 0.5, the animation seems to rapidly transition between the two states (seems like every frame) and it seems to do this for about half second before resetting to normal.
I'm not sure what is going on here. The video doesn't help because I'm not familiar with your project or how it is supposed to behave. It looks good to me! It seems like when you shoot twice, two recoil animations happen.
If there is a problem with the recoil animation, you'd probably need to create a simple app that ONLY shows this problem so we can take a look. Please don't send an entire app. Make an SSCCE.
Alyria The second problem is that my aiming logic is completely invalidated by the setToSetupPose being processed every frame
Calling setToSetupPose
should not wreck your aiming. Every frame, after you setToSetupPose
and apply your animations, then you adjust your bones for aiming.
I can send you my project files, like I said the rapid switching is harder to see on the vid but it also illustrates that the aiming is malfunctioning.
Nate Every frame, after you setToSetupPose and apply your animations, then you adjust your bones for aiming.
I'm not totally sure what you're even saying here, I tried putting setToSetupPose higher in PhysicsProcess() (above the SetAnimation for my aiming animation) but it doesn't seem like it's changing anything. I have 1 frame aiming animations where the weapon bones are rotated based on the position of an IK constraint bone that's being driven by my mouse position in godot.
- संपादित
If it's necessary to send us a project showing the problem, you need to make a simple project that only shows the problem and doesn't have anything else. If you send your whole project, we won't have time to look at it. That said, you can send it here: contact@esotericsoftware.com
Every frame:
setToSetupPose
.- Move your IK target bone to the mouse position.
- Apply animations (eg call AnimationState
apply
).
If calling setToSetupPose
messes up your aiming, it's because you are doing 2 and then 1.
Alyria I tried putting setToSetupPose higher in PhysicsProcess() (above the SetAnimation for my aiming animation)
This is confusing because you should never be calling SetAnimation
every frame. You set and add animations only when events occur, like the player jumps or shoots.
sent my project files over, I was able to fix the weird stuttering on the recoil by using a different method for the input singleton, but I'm still having trouble with the aiming being broken.
You sent a 226MB ZIP file. Sorry, but it is not reasonable to for us to debug your entire application.
Nate This is confusing because you should never be calling SetAnimation every frame. You set and add animations only when events occur, like the player jumps or shoots.
for my application that's too limiting as I need the player to be constantly updating their animation based on the position of their mouse cursor (front and back views are different animations). I changed how the SetAnimation functions as it checks to see if the same animation is already playing and if it is it won't be reapplied.
Nate EDIT: I see you sent a 226MB ZIP file. It is not reasonable to for us to debug your entire application.
oh wait sorry, I just realized I've been sending my export templates for no reason, I thought the mono/godot files we're taking up most of the space.
Alyria for my application that's too limiting
It is never the right solution to call set or add animation every frame. It doesn't make logical sense, so however you think it works must be flawed. After setting an animation, it is applied every frame from then on, without you needing to set it again.
Alyria I changed how the SetAnimation functions as it checks to see if the same animation is already playing and if it is it won't be reapplied.
That is a reasonable way to do it. It sounds like that solves your recoil problem and my post above solves your aiming problem.
Nate my post above solves your aiming problem.
I don't see how it does, I already said that moving the setToSetupPose method higher in Process() doesn't seem to do anything even though all my aiming logic is written after it. Admittedly I don't have code that says to move the IK bone to my mouse position in that script as I accomplished that in the editor with the SpineBoneNode and it's drive property as instructed in spine's tutorial. Should I do that through code instead then as it seems to be processed first if the problem you're describing is true?
The order of processing when using SpineBoneNode
can't be guaranteed. For your case, where you move an IK target AND also apply additive blending, you will have to implement the entire logic of moving the IK target in your process()
method.
Alright so I finally seemed to have gotten everything working but was wondering if this is the best solution. Currently I have a Node2D that's dedicated to following my mouse position that I then take the transform of and set it to my specific IK bone. Using SetX/Y or SetWorldX/Y seems to cause some problems, I'm guessing because of how godot's coordinates work where -Y is up and Y is down. The runtimes doc/api is a little confusing with all the different properties for specific bones (in godot's case set methods) so not sure if I could be doing something better.
Here's what I have for my code currently