• Editor
  • Run two animations - Cocos2d

I dove in head first so I am still wrapping my head around all this. I think I got it down largely, but I can't seem to figure out how to run two animations at once (the bones in use are exclusive to each animation and should not affect each other).

I call it as such:
AnimationState_setAnimationByName(skeletonNode->state, "torso", true);

and if I use:
AnimationState_setAnimationByName(skeletonNode->state, "feet", true);

then only the feet animation runs. I assume the latter is overwriting the former. I assume there is a different way to run them in conjunction but I can't see how?

Thanks for this awesome program 🙂

Related Discussions
...

If you want to mix two animations, I think you want to use setMixByName the second time.

Anyone want to provide source on how to do this with a looping animation? or this another part of the "complete" api we have to write our selves.. :/

fail to see how this method :
void AnimationStateData_setMixByName (AnimationStateData* self, const char* fromName, const char* toName, float duration)

will mix a looping animation..

I'm not going to be happy if I have to track the state and constantly call this in my update method (though I'd hardly be surprised if that's the answer I get).

In fact I've tried god knows how many things from this c api and on cocos2d I can't get it to mix 2 looping animations. Looks easy as hell to do in the code workshop on Java..

This is the code I've got..

int currentLoopCount= (int)self.skeleton->state->time /4;
if (currentLoopCount != self.lastLoopCount){
    NSLog(@"mixing animation");
    AnimationStateData_setMixByName(self.skeleton->state->data, "flying1", "wings", 2);
    self.lastLoopCount = currentLoopCount;
}

It doesn't mix - the wings animation doesn't effect anything except the wings on my skelenton.

Each animation plays individually fine - I just can't get mixing to work.

I really need an answer to this urgently - i hope someone out there knows the trick to do this on cocos2d.

@[削除済み]

I am using AnimationStateData_setMixByName between two animations which as I understand it "transitions" between those two. For instance, I pass it my idle and walk animation, and when I start walking from idle, it has a nice transition into it (which is adjusted by the float value). I thought that is what this is for and I'm not sure that would allow me to run two separate animations at once? I could be completely wrong but that's what it looks like it does to me.

@[削除済み]

Again, for the same reason as above, I don't think it does what you're expecting it does. I'm pretty sure it ties animations together (the end of one to the start of the next), and therefore would have no effect on a looping animation. Or am I crazy and just seeing what I want to see? 🙂

I'll start a better named thread.

Sorry, I don't use Cocos2d. I was just guessing by how I use it in C++. Wish I could offer more help.

tsaintthomas wrote

I thought that is what this is for and I'm not sure that would allow me to run two separate animations at once?

That's actually what I use it for too, so I'm not sure. I suppose I shouldn't offer guesses without running a few tests.

No problem, I appreciate the input.

@tsaintthomas, as you said, since both your animations don't key the same bone properties, you can apply them both and they won't overwrite each other. But first, a little description of CCSkeleton and AnimationState.

AnimationState tracks which animation is active and how long it has been active. Also, when the active animation is changed, it does mixing from an old animation to a new animation. CCSkeleton has an AnimationState for convenience, because it is very common to want to apply just one animation at a time with mixing when the animation changes. There are an unlimited number of ways someone might want to apply and mix animations or manipulate bones manually, so for any other behavior you'll need to write some code that describes what you want to do. To do this, extend CCSkeleton and override the update method.

Eg, maybe you want to keep track of two animations and their times and apply them both:

// Class fields.
Animation* animation1;
float time1;
Animation* animation2;
float time2;
...
- (void) update:(ccTime)deltaTime {
	Skeleton_update(skeleton, deltaTime);
	time1 += deltaTime;
	Animation_apply(animation1, skeleton, time1, true);
	time2 += deltaTime;
	Animation_apply(animation2, skeleton, time1, true);
	Skeleton_updateWorldTransform(skeleton);
}

Note both apply calls in this example always pass true for looping. You can get an Animation* from the SkeletonData:

Animation* animation = SkeletonData_findAnimation(skeleton->data, "jump");

The example above keeps track of the times and animations. If you wanted to do mixing when the animations change, you'd need to manage all that state. Doing this is a little tricky and, as described, AnimationState already does this, so you may want to use 2 AnimationStates:

// Class fields.
AnimationState* state1;
AnimationState* state2;
...
// Initialization.
AnimationStateData* stateData = AnimationStateData_create(skeletonData);
AnimationStateData_setMixByName(stateData, "walk", "jump", 0.4f);
AnimationStateData_setMixByName(stateData, "jump", "walk", 0.4f);
state1 = AnimationState_create(stateData);
state2 = AnimationState_create(stateData);
...
- (void) update:(ccTime)deltaTime {
	Skeleton_update(skeleton, deltaTime);
	AnimationState_update(state1, deltaTime);
	AnimationState_apply(state1, skeleton);
	AnimationState_update(state2, deltaTime);
	AnimationState_apply(state2, skeleton);
	Skeleton_updateWorldTransform(skeleton);
}
...
// Clean up.
- (void) dealloc {
	AnimationStateData_dispose(state1->data); // Only need to dispose the state data once.
	AnimationState_dispose(state1);
	AnimationState_dispose(state2);
	[super dealloc];
}

I don't know your exact use case, but this is probably how I'd do it. 🙂 Note the AnimationStateData could be stored outside your CCSkeleton instance and could be reused if you are creating many instances that use the same AnimationStateData.

I should also explain the functions used:

Skeleton_update increments the skeleton's time. Slots use this to keep track of how long an attachment has been in the slot. This can be used by an attachment in various ways, eg to show frame by frame animation. Note that currently no attachments actually make use of this, so not calling Skeleton_update would be fine.

Animation_apply, sets the skeleton's bones and slots based on the keys for the animation at the specified time.

Animation_mix, not shown above, but this sets the skeleton's bones and slots to somewhere between the current skeleton pose and the pose for the animation at the specified time. The interpolation between these poses is controlled by the alpha parameter. If use AnimationState mixing is done for you, otherwise ask if you have specific questions.

AnimationState_update, this increments the time for the AnimationState.

AnimationState_apply, this applies the current animation. It may also do mixing when transitioning from an old animation.

Skeleton_updateWorldTransform, this is very important. The skeleton is made up of bones. The bones are arranged in a hierarchy and the coordinate system for bones is defined by the parent's transform. When the skeleton is posed, it means the bones' local x, y, rotation, scaleX, and scaleY are set. These make up the local transform, meaning they are relative to the parent bone. To draw, we need to get world transform. Skeleton_updateWorldTransform goes through the bone hierarchy and calculates the world x, y, rotation, scaleX, and scaleY for each bone based on the parent transforms. Any time the local transforms are changed for a bone, Skeleton_updateWorldTransform should be called so drawing will reflect the changes.

Hope that makes sense! Be sure to ask for clarification if it doesn't. I will be writing some actual documentation soon.

Wow that's extremely helpful. I'm away from my mac so I just have one question at this point. When you say "you can apply them both and they won't overwrite each other" do you mean I should be using one of the _apply functions as opposed to setAnimationByName? I appear to be experiencing overwriting so I assume I am calling the wrong function. Thus far that (setAnimationByName) was the only one I knew of so now I have more stuff to play with (I didn't know how to make an Animation as the previous implementations were using a readAnimation using json which appears to be gone now? but SkeletonData_findAnimation gets me back on track!) Thanks again. Awesome stuff.

@spineUser, hello again George Cook. You seem to be your regular optimistic self. 🙂 I think the above info will help. If not, just let me know what isn't clear.

AnimationStateData_setMixByName is for defining the overlap duration when changing from one animation to another. During this overlap duration the animations will be mixed so the transition from one animation to the other is smooth. You typically call AnimationStateData_setMixByName beforehand to set up the mix times, then don't call it again. Use Animation_apply, Animation_mix, or AnimationState to pose a skeleton with an animation.

Thanks for the update - that helps a lot.. I'll see if it works then ping back.

I still think it's a shame that the CCSkelenton doesn't have an "add other animation" feature, as you've as good as written it above and now we'll end up with variations of it in our code.. Just thinking you might end up getting more questions by not doing it, than actually implementing it.

I also think that you need to either wrap this stuff or document it sooner than later as naming is not consistent across platforms as @tsaintthomas points out above.

ps I wrote this before your reply.. as I said on the cocos forum, I'm done with bitching and moaning. I'm not happy - but I have to work with what I've got... I'm utterly dissapointed about your alpha fbo response, so please don't get me started lest I get another ban and another thread in lockdown. 🙂

ps

can you refactor your CCSkelenton class?

We're all going to need to subclass it to get the functionality we want and it's currently not extension friendly lots of construction code is in the static constructor, meaning I have to duplicate that code and diff it when you do runtime updates... not nice.

thanks.

@tsaintthomas, the _setAnimationByName function you mentioned is AnimationState_setAnimationByName. It sets the current animation for an AnimationState. As explained above, an AnimationState only tracks a single animation and time. You'll need an AnimationState for each animation you want to apply.

In the API, "apply" means to set the skeleton pose based on an animation. This ignores the current pose and overwrites it with the pose from the animation. "mix" means to set the skeleton pose somewhere between the current pose and the animation pose, based on the alpha parameter.

For your use case you want to "apply" both animations. As long as your animations don't key the same bones, when you apply the second one it won't affect bones posed by the first one.

The problem you were having is you were trying to use a single AnimationState to apply two animations, which is not possible. You'll have to override the CCSkeleton update method and use code like was described above. Use two AnimationStates and you'll be good to go.

I'm fine with that but can you please seriously consider my request to make the class more extension friendly.

I'm having to copy and paste code from the static methods,
change visibility of members in the base class..

these are all things that will make updates painful.

thanks.

I think I get it now. Thanks again! Looking forward to the docs though 😉

Is this a mistake?

AnimationStateData* stateData = AnimationStateData_create(skeletonData);
AnimationStateData_setMixByName(stateData, "walk", "jump", 0.4f);
AnimationStateData_setMixByName(stateData, "jump", "walk", 0.4f);
state1 = AnimationState_create(stateData);
state2 = AnimationState_create(stateData);

I don't get why we have state1 and state2 from the same stateData in the same state.. what's the function of that? I'm presuming it's a typo.. or is the state1 assigment menat to be above the previous line?

spineUser wrote

I still think it's a shame that the CCSkelenton doesn't have an "add other animation" feature, as you've as good as written it above and now we'll end up with variations of it in our code.. Just thinking you might end up getting more questions by not doing it, than actually implementing it.

I also think that you need to either wrap this stuff or document it sooner than later as naming is not consistent across platforms as @tsaintthomas points out above.

I do think there could be some utility in a CCSkeleton class that applies multiple animations. I wonder if it should be a subclass though? If someone subclasses CCSkeleton to do their own custom animation manipulation, there would be some clutter from the methods that handle multiple animations. There is some clutter even now, just by having an AnimationState that might not be used. How about:

  • CCSkeleton: has a Skeleton and knows how to render it. update method does nothing.

  • CCSkeletonAnimation: extends CCSkeleton, has a list of AnimationStates.

ps I wrote this before your reply.. as I said on the cocos forum, I'm done with bitching and moaning. I'm not happy - but I have to work with what I've got... I'm utterly dissapointed about your alpha fbo response, so please don't get me started lest I get another ban and another thread in lockdown. 🙂

I've unbanned your infrid username if you want to go back to using that. I hope we can keep things friendly. Keep in mind everything is still new, just a couple weeks old and hasn't seen much usage. Things are evolving and I'm not shutting any doors on how they might turn out, even for using ObjC. The foundation is pretty carefully thought out. I want the evolution of it to be based on how people are actually using things. I listen to everyone's feedback. There's no need to go overboard to get me to listen. 🙂