Monkanim: new animation system in the works

So I’m working on a new animation system for JME. It’s in its early stage and I’d like to expose the design and open it to discussion as many of you may have some ideas / requests.
The design is widely inspired from Unity’s Mecanim that I find simple and elegant.

I will update this post as the design document are done, but feel free to post your opinion.
here is the repository where it will be developed before the merge into the engine (if ever btw…)

What do we want?

Animation Blending: today’s system supports some sort of blending, but it’s only used to smooth the transitions between animations. We want to be able to blend between animations according to a parameter.

State machine: I totally hate the listener pattern we have on animations. I want to be able to design a state machine that will layout the animations with proper conditional transitions that will of course also blend the animations.

Additional motion events: sound or particle effects on footsteps, triggered at the proper timing, a dashing shock wave effect when that punch hit your foe’s stomach… We already have something like that in the current system, but it’s not in the right place, it’s cumbersome, and I’m not sure anyone but me ever used it.

Root motion: All animations right now have to be made “in place”. Meaning that the root bone should never move else your model is offset from its world position, which is an issue for collision / picking and so on. usually you have to give some kind of displacement value along with the animation to know “how much distance the character will cover in this animation”, so that the code moves the character of that distance. Root motion allows animators to move the root bone in the animations, but the system extract that motion and use it on the model/capsule local transforms. This way the system is more artist driven and it’s a lot less cumbersome for developers.

A little bit border line out of scope
Inverse Kinematic
Adding a bit of IK in the mix would be like the cherry on the cake, It’s a little bit out of scope because it only applies to bone animation, and I think it can be implemented entirely separately and independently of the animations system, with our control pattern (An IKControl right after the animation control that would constrain some bone position). Note that there are technique to fake IK with animation blending, so it might also be an option…

Ragdoll Physics: We already have this, but it would need a bit more love… Right now our ragdoll just drops like a dead body when it’s triggered, but in real life that a situation you will seldom run into, unless the character is instantly killed. Usually when falling he will try to protect himself, or try to regain balance in some way. So we need to add some intelligence to the current ragdoll, and work on a more friendly API. It’s a bit out of the scope for the same reasons as IK.

Animation retargetting: I have a working prototype of this … and it could definitely help in the overall animation creation process, but not completely in the scope.

That’s pretty much it, if we end up having all this it will be a great step.

So first design document: Animation Blending:

Second design document: State Machine:

Read / Think / Comment / React / Whine (not too much please)

2016 - 07 - 18
Some follow up on this. I’ve implemented the Blending system and the State Machine. There are still some issues or things that could be better, but it’s working.
For now the project is done as if it was going to be merged with JME core, and overrides some of the core classes. Note that this override has been made making sure the old system still works. Though, monkanim could be a separate “plugin” of the engine and have it own versions of the core classes but, that’s a lot of duplicate code in the end… we’ll see.
I’ve been renaming things as I implement to find better names. Like BlendingDrivers became BlendSpace, and Transition conditions became transition triggers.

So what you can do with the system today:

  • migrate from the old system (this will create an animation sequence for each animation in the AnimControl)
  • create sequences composed of several animations, that will blend together.
AnimationSequence sequence = manager.createAnimationSequence("walk_jog_run", "walk", "jog", "run");

This creates an animation that will linearly blend from walk to run going through jog: 0 = walk, 0.5 = jog, 1 = walk.
For now only a LinearBlendSpace is available, but anyone can override the BlendSpace of a sequence and have a blend method that will take care of the blending. I want to enhance this so that you could add some kind of “watch” over a variable and the BlendSpace would blend accordingly.
Note that you can combine sequence together not only basic animations allowing for complex blend trees.

  manager.createAnimationSequence("walk_jog", "walk", "jog");
  manager.createAnimationSequence("walk_jog_nestedRun", "walk_jog", "run");

The walk_jog is a sequence and it’s combined with the run animation. This will result in different a blending than the walk_jog_run from before.

  • Create a state machine
    here is an example of animations that are triggered one after the other (what we were doing with onAnimCylcleDone sort of things before)
        AnimState idleState = manager.createStateForSequence("idle");
        AnimState walkState = manager.createStateForSequence("walk");
        AnimState jogState = manager.createStateForSequence("jog");
        AnimState kickState = manager.createStateForSequence("kick");
        AnimState runState = manager.createStateForSequence("run");
        
        manager.ANY_STATE.addTransition(new InterruptingTransition(walkState, () -> currentState.equals("anim_chain")));
        walkState.addTransition(new Transition(jogState, () -> currentState.equals("anim_chain")));
        jogState.addTransition(new Transition(runState, () -> currentState.equals("anim_chain")));
        runState.addTransition(new Transition(kickState, () -> currentState.equals("anim_chain")));
        kickState.addTransition(new Transition(idleState, () -> currentState.equals("anim_chain")));

this will play the animations like this: walk → jog → run → kick → idle then loop on idle.
Note that the currentState.equals(“anim_chain”) is just inherent to the game logic, it’s the trigger of the transition, could be foo == 42. Note also that it’s been made so that it’s nice with java 8 lambdas, won’t be as nice with java 7.
Here you have an InterruptingTransition coming form the ANY_STATE. This means that this transition trigger will be evaluated on each update and that if you are in whatever state and the the currentState becomes “anim_state” it will follow the transition…
the other transitions are classic transitions and will be evaluated only when the current state’s sequence is done…
A transition will blend from one state to another, the default time is 0.3 for now, and does pretty much the same blending as the old system. Note that if you don’t set a trigger the transition will always be followed (like if the trigger always return true).

For the record this is the equivalent code with current system to achieve the same kind of animation chain.

 AnimChannel channel = control.createChannel();
        channel.setAnim("walk");
        channel.setSpeed(1);
        channel.setLoopMode(LoopMode.DontLoop);

        control.addListener(new AnimEventListener() {
            @Override
            public void onAnimCycleDone(AnimControl control, AnimChannel channel, String animName) {
                if(animName.equals("walk")){
                    channel.setAnim("jog", BLEND_TIME);
                    channel.setLoopMode(LoopMode.DontLoop);
                    return;
                }
                if(animName.equals("jog")){
                    channel.setAnim("run", BLEND_TIME);
                    channel.setLoopMode(LoopMode.DontLoop);
                    return;
                }
                if(animName.equals("run")){
                    channel.setAnim("kick", BLEND_TIME);
                    channel.setLoopMode(LoopMode.DontLoop);
                    return;
                }
                if(animName.equals("kick")){
                    channel.setAnim("idle", BLEND_TIME);
                    channel.setLoopMode(LoopMode.DontLoop);
                    return;
                }
            }

            @Override
            public void onAnimChange(AnimControl control, AnimChannel channel, String animName) {

            }
        });

The API might change, as I’m not completely satisfied (I’d like something like manager.makeTransisiton(“walk”, “jog”, trigger) sort of api), but it works right now and you can check this in the repo.

2016 - 07 -25
Some news. Still actively working on this.
I’ve tried myself at functional API a bit, so that defining transitions is not too complex. So I’ve come up with this:

 manager.findState("walk").transitionTo("jog").when(() -> foo == 1).in(0.5f);

So basically this is creating a transition between the “walk” and “jog” states. When foo == 1, and when the walk state ends, it will transition to the “jog” state in 0.5 seconds.
I like it so far because it’s pretty clear what’s going on when you read it.
I know some people just hate functional APIs and java 8 lambdas, so I thought about them and kept an old style API too:

        AnimState state = manager.getState("walk");
        if(state != null) {
            AnimState targetState = manager.getState("jog");
            if(targetState != null) {
                Transition t = new Transition(targetState);
                t.setTrigger(new TransitionTrigger() {
                    @Override
                    public boolean evaluate() {
                        return foo == 1;
                    }
                });
                t.setDuration(0.5f);
                state.addTransition(t);
            }
        }

This is the equivalent code with the regular API and with java < 8… So you can do this too… or a mix of the 2 solutions :wink:
Yeah I kinda exaggerated it a bit to make my point :wink:

29 Likes

Nice concept! I have no idea how much work this means for you?! Do you have something like a time plan, because I would be interested in how long it does take to develop something like that :wink:

usually basic concepts are easy to lay out, and implement, and I already have the blending almost completely done, but you know it’s like when you build a house… bringing up the walls is relatively fast… but the rest can take forever :stuck_out_tongue:
Makes me think that I didn’t link the code repo.

I am not sure how hard this going to be as well, but I am thinking about an option to interpolate between last frame and first frame option to add the possibility of making the animation run in loop

The blending looks amazing, really hope this makes it to completion! Brilliant work we are all lucky to have you working on this

Thanks for your great work.

State machine would be a nice idea. I hate the listener pattern soooooooo much.

I discussed this yesterday with @zzuegg and I’m afraid I will have the case where I need the “onStateChange” event emitted… we’ll see…

I edited the first post to add the State machine design document.
Also added a requirement in the “a bit out of scope” section for animation retargetting.

1 Like

Which program do you use to make such concept documents?

draw.io.
It’s free and fantastic

My contributions, will be only few links that could interest you.

If you have the opportunity, you could be interested by the animation session at : nucl.ai conf this year, the July 20th
Last year it was free to view in direct (via web). (accessing after record is only for premium members).

An alternative to state machine is to use behavior tree (also for animation).

iirc, the Bungy slides about Destiny Animation at Siggraph 2014 was interesting:
http://advances.realtimerendering.com/destiny/siggraph2014/animation/index.html

Few video :

2 Likes

Updated the first post with heads up marked as “2016 - 07- 18”. Feel free to comment.

1 Like

New update marked as “2018 - 07 - 25”.
I would love some feed back.

4 Likes

This makes me uneasy. Wouldn’t ‘walk’ be in a loop? It shouldn’t matter what part of the walk cycle the animation is at, it should be able to transition to jog at any point, or visa versa. Which makes me think jog and walk need to be playing in sync.

It might be an idea to change your example to some other actions that dont require blending, like dodge and attack.

Which also makes me think, have you addressed the concept of interruptible actions? Use case : ‘Die’ needs to be able to play at anytime, overriding the current action, UNLESS in the ‘jumping in the air’ state is being played…

Sorry, I tried to make a quick post before work but as always, it has blow out, I can give more details / ideas this evening.

Yes this is maybe a lousy example :stuck_out_tongue:
How it work exactly:
you can have 2 types of transitions : A Transition that will trigger at the end of the state if the trigger returns true, and InterruptingTransition that will trigger anytime in the loop whenever the trigger goes from false to true.

Also there is an ANY_STATE state, on which you can add transition and those transition will be checked whatever state is currently active.
This allow you for example to have the walk anim play in cycle… and when you hit a key have the jump animation play.
That would look like that:

manager.findState("walk").interruptTo("jump").when(() -> myKeyHasBeenHit());

while the key is not hit “walk” will loop.
InterruptTo creates an Interrupting transition.

I hope I’m clear in my explanations.
For now the next challenge is using masks (to play an anim on only a subpart of the skeleton).
I started to use the exact same way as we have chennels today… but you may actually want to blend between anims even if they are not on the same mask… so that’s my challenge for now :wink:

4 Likes

awesome stuff, exactly what i was hoping for =)

Hey @nehon - Do you plan on having a Node based interface for artists?

As a person who has used tools such as Mechanim this API is very welcome. :slight_smile:

Well not sure it will come to this. Would be nice but definitely at the bottom of the list.
I’m a bit stuck on this, I’m at the point where I have to tackle a design issue with the animation masks (pretty much the channels we have today) and can’t find an elegant solution.
I need to take some time and think about it.

1 Like

Hope you can find a good solution soon. :slight_smile:

Currently i am developing my AnimationSystem (yet it is just a bare bone class :wink:) and is going to be integrated with GDX-AI Behavior Trees and i am considering to use Monkanim for animation transition between my characters ai states .