kolya-j

I found couple issues, technical and logical. I'm using C runtime v4.0.31(plus compile fix)

1. I follow steps on your description page: spine-c Runtime Documentation: Track Entries spAnimationState_setAnimationByName and after this spAnimationState_addAnimationByName
Set mixTime - and it doesn't work. I can see jump with my eye when animation switches.

I checked in debugger. mixFrom and mixTo are nil, so I assume it might be connected to the issue.
If I manually set mixingFrom and mixDuration - looks like it works. But this is not what documentation says.

2. I checked another topic from here - Chaining animation resets elements to their set-up pose.
Basically, you saying that I can't add one animation after other, because if I move skeleton in run animation and after that add idle, idle will happen on default skeleton position. This is not what I expected at all.
Also you saying that I should not use holdPrevious - but this actually fixed situation and skeleton do not jump in default position. And now I'm worrying, that later this might stop working, taking into account provided link from Unity sub forum.

Proposal with 2 tracks saying that I should add idle at exact t = end of run animation -mixTime, but to track with index 1. And If I want more effects, it will become more complecated. And here is problem - I know only when completion on animation arriave, not completion - t. Alternatively, I can do schedule with timer.... but this is always bad idea to mix rendering time with other timers....
And clear track also sounds strange - it means, I have to manually listen for completion of run animation so right after this I can clearTrack and set idle. This is for sure less comfy solution. And how I can apply mix between animations in this scenario?

I decided to add couple more points here rather than create separate thread on forum, let me know if I should do oposite.
3. Currently c-runtime description says nothing about clipping.Only mesh and region.... but our animator used clipping strahgt away, so I have to check your cpp runtime, objc-cocos and 3rd party c++ to implement rendering for it.
I think it would be nice to include it in documentation. And I'm sure cocos-objc has bug, where clipping finishes on the same slot and has no efect, because in my solution, I should not use spSkeletonClipping_clipEnd on the same slot, which triggered spSkeletonClipping_clipStart(same I can see in cpp runtime). To my best knowledge here: https://github.com/EsotericSoftware/spine-runtimes/blob/7b0d71568b79bc486fcb486c0e725a81d46c4868/spine-cocos2d-objc/src/spine/SkeletonRenderer.m#L231 we will continue, skip if (texture) { and will call end, which will remove clipping effect.

4. This is annoying part. 4.0.31 actually not compilng... you applyed fix for compile next day after cutting 4.0.31. This is really worrying situation, since we gonna release app in production, which depends deeply on Spine and not compiling in release tag is scary bit.
kolya-j
  • Postovi: 6

Nate

1) Ah, the docs should say mixDuration (the total duration of the mix), not mixTime (the value increasing over time from 0 to mixDuration). Sorry for the confusion! I've updated the documentation pages.

2) The thread you linked is pretty involved. You summarized the thread, sort of, but it's hard to tell if your summary is correct. Can you please explain what you are trying to do?

Asking multiple things in one thread is fine!

3) Thanks, we'll take a look.

4) We can do a new release tag. The 4.0 branch should be quite stable (4.1 beta is almost complete).
Avatar
Nate

Nate
  • Postovi: 11875

Mario

I can speak to 3 and 4:

3. I've created an issue for the spine-cocos2d-objc bug you found regarding clipping: https://github.com/EsotericSoftware/spine-runtimes/issues/2073

The integration example in the spine-c documentation is already quite involved, so we opted to not add descriptions for clipping there. You do however not have to dig into any third party code, or even spine-cocos2d-objc. The canonical runtime for C is spine-sfml, you can find its renderer there: https://github.com/EsotericSoftware/spine-runtimes/blob/4.0/spine-sfml/c/src/spine/spine-sfml.cpp#L293

I've updated the spine-c documentation with a link to that implementation.

4. Sorry this happened. When we do a Spine Editor release, we also tag the current release Spine Runtime branch. Generally, the release branch always compiles. In this case, we didn't catch the compilation error and the branch was still tagged.

For what it's worth, there is no need to use a specific x.y.zz tag of the Spine Runtimes repository. If you are using Spine Editor x.y.zz, you can use the latest commit from the corresponding Spine Runtimes repository branch x.y. E.g. You are using Spine Editor 4.0.31, so you can use the latest commit to the Spine Runtimes repository branch 4.0. The release tags on the Spine Runtimes repository are for us to be able to go back and see what the Spine Editor version was using as the runtimes. They aren't really that meaningful for users of the runtimes. Always use the latest commit from the x.y branch is the rule of thumb.
Avatar
Mario

Mario
  • Postovi: 3043

Nate

We've added a 4.0.64 tag to match the latest 4.0 editor:
https://github.com/EsotericSoftware/spine-runtimes/releases/tag/4.0.64
Avatar
Nate

Nate
  • Postovi: 11875

kolya-j

Thank you for such prompt response and for your help.

About point 2.
I have "run" animation, which move spine across screen. From left edge to right. If I just add idle after that, skeletom will be shifted to left edge, because idle only changes some elements, not root(or any top level bone) position.
I would Assume that while I'm adding animation, it will be applyed on top of current transformations if those elements not changed in "idle". So basically transformations from last frame of run animation stay the same, if no other animation applied to them. This is how I expected it to work.

After reading thread, which I provided, it looks like this is not how it works and I can't see easy way to chain "run" with "idle". Because I'm not sure how I can schedule animation on track 1 with delay. setAnimation doesn't have delay. Even with delay, I'm not sure - will make nice mix? Because now, if run finished with skeleton arms up, added idle animation after it, will lower its hands during mixDuration (in "idle" skeleton hold hands down)
Right now I'm using holdPrevious property, and it works for me. But question is: will it work if I add 2 animation, and not one. Will it keep transformation from both prev animations?
kolya-j
  • Postovi: 6

Nate

I would Assume that while I'm adding animation, it will be applyed on top of current transformations if those elements not changed in "idle".
We started out this way, but it caused a lot problems. In most cases it's a lot easier for the animations combined to define the skeleton's pose, without remnants from previously applied animations.

I understand your run/idle situation now, thanks. Often at runtime you need to know where the skeleton is for things like hit detection, so it's often impractical to have the animation moving the skeleton around the screen. If your animations position your game objects, your game code is unlikely to know where the objects. Also it can make it difficult to serialize your game world's state, if you need that. There are a few ways to go about it:

The easiest thing to do is play your idle animation on the right side of the screen. This may be sufficient for simple scenarios, where you need only a few animations like idleLeft, run, idleRight.

The next easiest thing is to know where your skeleton has been moved to after you play an animation. For example, you play idle, then run, when run is complete you know you need to move the skeleton across the screen, then when you play idle it's on the right side. If you want to smoothly transition from run to idle it may take some planning. It's easiest for the run animation to end in the idle pose, so the transition from run to idle is in the animation.

That approach is useful for complex movements, like climbing a ladder, when the exact position of the skeleton isn't important during the animation (maybe they can't be hit while on the ladder).

Another option for you can be to use additive animation. This is where you set TrackEntry mixBlend to MixBlend add. This causes an animation to be added to the pose from lower tracks. For example, it can be used to do facial expressions during other animations. For example, to use it for that you first need to key the face on a lower track. If you don't then every frame the additive animation will be applied on top of the pose from the previous frame, quickly getting out of hand. Next, you play your animations, run, jump, idle, etc normally. Then on a higher track you can play one or more face animations that are applied using MixBlend add. This changes the face no matter what else the character is doing. You can use the TrackEntry alpha to partially apply an animation, for example to blend between facial emotions using a weighted system.

Or, you could move your skeleton in your code, at a speed that matches the animation. See the run or walk animations in the spineboy example project. To do this you need to animate without moving the skeleton across the screen. It can be helpful to move the skeleton while animation, then remove those keys when finished. Ghosting offset can be used to determine the speed the skeleton needs to move at runtime to match the animation.

That approach can work for linear or simple movement. It's also necessary when the player has a lot of control of the skeleton's movement, like a platformer game. In that situation you may want to adjust the animation speed based on the skeleton's movement speed, so they match. For example, when moving in a platform, often movement starts slow and then gets faster.

For more complex movement, like a shambling zombie, it would be difficult to write code that moves the skeleton in a way that matches the animation. For this it's better for the animation to define the movement, where the animator has full control. At runtime you can use a technique called "root motion". This is where you take the motion that would be applied to the root bone and you instead apply it to the entire skeleton. This allows an animation to move your skeleton in your game world in complex ways. With root motion, after you play your run animation the skeleton would be moved across the screen, then when you play idle, it would play on the right side of the screen.

Implementing root motion is relatively advanced. We provide code to do it in Unity. The docs are here, the code is here and here. It is split because Unity has two types of scene graph nodes that are position differently. Also it is relatively complex because it has some advanced features you may not need, like rotation.

"Hold previous" is probably not what you want. It applies the previous animation fully in a mix from one animation to the next, ie A -> B. If you have multiple animations, eg A -> B -> C then yes, it will apply A and B and then C and with hold previous it won't mix out the previous animation. However, AnimationState tracks intend mixing to be used from one animation to the next. To apply multiple animations, it's intended to use different AnimationState tracks.

If you really want to change how animations are applied, you can modify the runtimes, as mentioned in the thread you linked. A drawback to that is you'll find you often need to key things in an animation to reset things that were keyed in an animation played previously. This leads to keying almost everything at the start of most animations. That is tedious, bloats the animation data, and it makes applying an animation at runtime slightly more expensive to have many extra timelines. As you add things to your skeleton, you'll need to revisit existing animations and key things to reset them. It's also error prone, as you won't notice any problems until you happen to play an animation that keys something that some other animation forgot to key to override. Lastly, setting the skeleton back to the setup pose will snap, rather than transition smoothly.

Sorry for the long post!
Avatar
Nate

Nate
  • Postovi: 11875

kolya-j

Thank you for long explanation. I will try to sum up proposed approaches. And may be explain why they do not fit me. This is just to make sure I totally understood proposed approached.

1. Approach one: implement idle animation where run ends. We implement story, so we will have multiple actions, after which we need idle. I call it run for simplicity. But across story, skeleton will run a lot and stops in different places. So end of each action animation might ends in different places, and idle is a way to keep skeleton in place while we playing sound till end. (we can't guarantee timing of sounds, localizations are difficult)

2. Approach 2: move skeleton after animation This approach would be the easiest... if only our character didn't have more elements in skeleton. So items, which in skeleton hands, he sometime drops. And if I move root, it will move other elements which are on the ground now(breaks animations....). This is how our animator did it now. Because he need move items with skeleton and some of them need to be between hands of skeleton
I see possible adoption here. we have in skeleton parent bone for character itself. So all additions are not part of it. I assume I can create programatically animation in runtime, add to this characterParent bone on other track and play in parallel to idle. So idle will play together with those animation, which will move character where we need it to be. Pleasse let me know, if this make sense, I will try to implement it now.
Other possible adoption here is move everything extra from skeleton and use IK to connect 2 skeletons. Fiew issues here. I don't know - can we connect 2 skeletons with IK? Other issue is how put elements from other skeleton between characters hands in draw order

3. Option 3: mixBlend: I might got the idea, but some terms make me think that I again have some mistunderstanding.
My assumption of how 2 track system works - if you have 2 animations in parallel, they both apply effect on skeleton at the same time. So, for example, with facial animation. We have "idle" animation and I can make. it "sad-idle" by adding "idle" on track 0 and "sad" to track 1. And if idle and sad do not touch same bone, things will work as intended.
Here I assume mixBlend is intended to help solve how we handle when 2 tracks effect same bone at the same time.
I honestly don't think it help me in my case. As if "run" and "idle" don't finish/start with same pose, we will have strange pose of idle applyed on final frame of "run". Like example with rised hands on last frame of "run". Which is bug in animation now, which helped me catch that animation didn't switch smoothly, later end of "run" and start of "idle" will have same pose or almost same(not position in screen). Thats why I like mixDuration, it helps fix situation with "almost" same poses.

Alternatively here I might confuse lower tracks, aka track 0, 1, 2 and lower track animations, aka animation in the same track but played before.

4. Option 4: Ghosting offset This is amazing option for game, for sure. As we do story telling, for us simpler if animator responsible for everything or almost everything. So I don't need to think on movement in every scene. We even consider options on how implement camera on his end, so I will just follow it during rendering.

5. Option 5: root motion. Not exactly match what I want, as I can't use root or whole skeleton as I explained, but this is what I will try today, create animation on one bone, which is bone to which whole movement applied. I will try to read docs, because I need find way to identify translation of bone in end of "run" and add same translation in new created animation and play it parallel to "idle".

6. Options 6: Hold previous To be honest I do not undestand answer. It totally works in my case, where I use it when add idle. And it keep changes in position not only during animation mix, but also during whole idle in loop. And I don't understand why this is not what I want. Will it stop working if I add third animation? Will it drop changes from first after adding third? You also was against usage of this bool in linked post, but looks like there it was also the thing, which will fix their issue.
This is what documentation says:
Snapping will occur if holdPrevious is true and this animation does not key all the same properties as the previous animation.
But in my case "idle" do not override(not keyed) translation for characterParent bone and yet, charater stays on place. This is what worry me, as it might looks like a bug in current runtime and soon will be fixed, which breaks my code. Or may be, you need change documentation, saying that not keyed changes from first animation will stay in place until keyed by second or any next animations. Does it make sense?

---

So I did try to implement extra animation on other track, which will move skeleton to needed position to keep it there for idle. And looks like it works.
What I did:
1. Find bone with needed name in skeleton. I assume that bone index won't be changed during lifetime.
2. spSkeletonData_findAnimation + spAnimation_apply to find bone position in the end of animation.
3. make a diff between final position in animation and spBone.data.x, to know actual translation
4. Crearte animation in runtime, here is code, will do my best with C, even tho my code is in swift
spTranslateTimeline *pTranslateTimeline = spTranslateTimeline_create(2, 0, moveBoneIndex)
spTranslateTimeline_setFrame(pTranslateTimeline, 0, 0, position.x, position.y)
spTranslateTimeline_setFrame(pTranslateTimeline, 1, duration, position.x, position.y)

spTimelineArray *pTimelineArray = spTimelineArray_create(1)
spTimelineArray_add(pTimelineArray, pTranslateTimeline)
spAnimation_create("tmp_animation", pTimelineArray, duration)
spAnimationState_addAnimation(pAnimationState, 1, pAnimation, 1, delay)
With this code It looks like it works for me. All I need is just clear track at index 1 before playing next scene.

Also, I checked unity code, which you linked. And as I understood you looking for shift between time A and B. This kinda clear. But I didn't understand what you do with this shift after that? I always can add it to shader and move every vertex... but I would prefer spine runtime to do all of this for me. Thats why for now animation looks like a working solution.

Please, let me know, if this make sense and may be I can improve or I missed smth.
kolya-j
  • Postovi: 6

Nate

can we connect 2 skeletons with IK?
You can, at runtime, by having code move an IK target bone to the world position of a bone in a different skeleton.
kolya-j je napisao/la:here I might confuse lower tracks, aka track 0, 1, 2 and lower track animations, aka animation in the same track but played before.
Yep. Tracks are animations applied on top of each other. We call transitioning from one animation to another on a single track "mixing".

Hold previous applies to mixing to another animation on the same track. The snapping from hold previous happens in this case: one animation raises an arm and a running animation is set for the same track using hold previous. Running doesn't key the arm. When the arm raise animation is no longer applied (the mix duration is reached), the arm will snap back to the setup pose. AnimationState doesn't keep the pose from an animation that will no longer be applied, it resets the keyed properties for that animation back to the setup pose. You would need to modify AnimationState if you want to avoid this resetting to the setup pose.

Hold previous is working for you because you keep applying your run and idle animations. You are mixing from run -> idle I guess with a very high mix duration and hold previous, so you get both run and idle on the same track. That is how AnimationState works, it is not accidental behavior, but it isn't how we usually see AnimationState used. What happens if you need to play more animations? Say you need to run up the screen and then idle again.
kolya-j je napisao/la:I assume that bone index won't be changed during lifetime.
Correct.
kolya-j je napisao/la:So I did try to implement extra animation on other track, which will move skeleton to needed position to keep it there for idle. And looks like it works.
I should have suggested something like this. It makes sense to use an animation to position things, then play your idle and other animations on other tracks so they happen wherever the "position" animation has placed the bones.
spTranslateTimeline_setFrame(pTranslateTimeline, 0, 0, position.x, position.y)
spTranslateTimeline_setFrame(pTranslateTimeline, 1, duration, position.x, position.y)
Note if you have two keys with the same position, you could just add the key at frame 0. Maybe your two keys have different positions in your real code though.

The way you did it programmatically by creating an animation to move the bone to the end position will work, depending how you use it. Do you play run and then when it is done, play run position on a different track to keep the position from run, even if run is no longer applied?

Let's step back and think about how it could be done in the editor, eg by animating run, then cutting all the translate keys for the root bone of your character and pasting them into a new animation. Then you have two kinds of animations:

1) Animations that change the position, like run. For those, you could play run on track 0 and run position on track 1. This gives you the run animation and the position animation.

2) Animations that don't change position, like idle. For those, you just play them on track 0. The run-position animation continues to play on track 1, keeping the character where the run animation wanted it, even though you aren't playing run anymore.

If you later play another animation that affects movement, like walk up, then you play walk up on track 0 and walk up position on track 1.

You would probably find it annoying to separate the translate keys from the rest of the animation in the editor, so you could do it at runtime by removing the translate, translate X, and translate Y timelines from the run animation and creating a new run position animation with those timelines.
I checked unity code, which you linked. And as I understood you looking for shift between time A and B. This kinda clear. But I didn't understand what you do with this shift after that?
It removes the movement from the root bone and applies it to the GameObject, which is Unity's scene graph node. This makes the movement of the GameObject come from the animation. Unity can do hit detection, physics, etc that is based on the position of the GameObject. Also other code can move the GameObject around to run, jump, get hit, etc.
Avatar
Nate

Nate
  • Postovi: 11875

kolya-j

The way you did it programmatically by creating an animation to move the bone to the end position will work, depending how you use it. Do you play run and then when it is done, play run position on a different track to keep the position from run, even if run is no longer applied.
So I have scenes, I need play idle only in the end of scene. I'm playing "run position" on track 1 after scene end, with same delay as I plan to play idle on track 0. After scene over, I clear tracks. So for now nothing gonna play after idle.

Split animations option: split in spine
Let's step back and think about how it could be done in the editor, eg by animating run, then cutting all the translate keys for the root bone of your character and pasting them into a new animation
This is interesting option. But in this case animator will create seaprate animations and I don't see option in spine to play multliple animations from multiple tracks. So basically how animator can make sure that both animation played together play nice? For example to make sure that there is no problem with leg sliding, like you showed here. I see Preview option, but in this preview each track can play only 1 animation at a time. So No way to test how it will looks like to play "run + idle" on track 0 and "run position" on track 1. Featrue request for future - player mode where we can throw multiple animations on mutiple tracks. Or if exists, send me some link to educate myself, please.

Split animations option: split in runtime
This is also very interesting option, this way less math for me, just take all transtate from one bone and split it. Thank you!
kolya-j
  • Postovi: 6

Nate

kolya-j je napisao/la:I don't see option in spine to play multliple animations from multiple tracks.
This can be done in Preview:
Preview view - Spine User Guide
kolya-j je napisao/la:in this preview each track can play only 1 animation at a time. So No way to test how it will looks like to play "run + idle" on track 0 and "run position" on track 1.
Preview does (almost) everything AnimationState does (it uses AnimationState). Play run on track 0, set the mix duration, change the track 0 animation to idle, click track 1, set the run position animation. If needed you can temporarily set the speed to 0, so your run and run position animations start at the same time. We have plans to improve the preview.
kolya-j je napisao/la:just take all transtate from one bone and split it
Yep, I think this is the way to go! I wanted to explain doing it in the editor mostly as a way of understanding what is going on, then find a way to cut out the work by doing it programmatically.
Avatar
Nate

Nate
  • Postovi: 11875


Natrag na Runtimes