CharacterStateManager

Plugins CharacterStateManager

ThatsLewd

Active member
Messages
7
Reactions
130
Points
28
ThatsLewd submitted a new resource:

CharacterStateManager - A powerful tool for layering multiple state machines to control a character in complex ways

What is it?
CharacterStateManager is meant to be a powerful tool for layering multiple state machines to control a character in complex ways. The main goal was to create an all-in-one plugin that could create dynamic procedural behavior. It has many options and there are many ways to do things, so have fun exploring and thinking creatively.

Features
  • Robust system for defining and transitioning between states, and assigning behaviors (in the the...

Read more about this resource...
 
This can be bring potentially a lot of interesting results, but unfortunately I am not a person who is familiar with state machines.
Any chance for a demo scene of some kind?
 
Wow, I was hoping someone fixes Animation Poser or doing a similar approach. Thank you!

I gave it a first try, needed some time to understand the workflow.
However, I seem not to understand it right ;)

I wanted a simple approach for my first run, just some slight head movements combined with Face Morphs.

So I created a group called "Head", made one state for it "State1"
Then I created two layers, one for head's Pos/Rot and another one for like a dozen Face Morphs and gave them all the needed keyframes (preview is running fine on both).
Both layers had their own animations, like "Head Idle" and "Face Morphs"
In the "States" tab I added both animations to the State.

Now to my problem: if I check "Playback enabled", only the face morphs are playing, not the head movements.
I was under the impression that one State with two Layers plays them both at the same time? Or do I just have a logical failure between my ears?
 
Ok, this looks very promising!

Two questions:

1) Anchoring feature - Any plans on implementing that?

AnimationPoser was inspired by MacGruber's Idleposer. Imo, IdlePoser's most exciting feature from an animator's perspective was the possibility to "anchor" an animation to an atom's (moving) controllers, so you could eg set up an animation that started at {postion1,orientation1} relative to a char's left thigh and ended at {postion2,orientation2} relative to that char's right shoulder - and the animation would adapt to any changes in the character's pose. In other words, you could record the animation while the char was eg standing up, then move the char into a cowgirl position, and the animation would still work.

2) You mention "easings" or "tweens" - does that mean you've implemented some form of splines? Which form exactly (Hermite splines, Bernstein/De Casteljau, Cadmul-Rom, Cardinal? - Strictly speaking, they're all Bezier-splines, but there's a lot of variation as to how the smootheness-requirement ("tangent-matching") is implemented).


Edit: Wrt to 2)

I might be talking out my ass here (so ... "truckloads of salt" ...), but from looking at your GitHub ["public static float ApplyEasingFromSelection(float t, string easing)" (in source/lib/Easings.cs) and "void UpdateControllers()" (in source/engine/KeyframePlayer.cs)], it looks like you've set up the easings in such a way that they satisfy the "interpolation condition" (i.e. "Position should be at the "in" Keyframe for t=0, and at the "out" Keyframe at t=1, interpolate in between with a polynomial of degree 0-4") - but I'm not sure whether they also satisfy the "smoothness condition" ("first & second derivative of the function has to be continuous at t=0 and t=1")?

If true, that'd mean you could have sudden instantaneous "jumps" in velocity & acceleration at t=0 and t=1? (Doesn't happen in nature - which is the reason splines are popular in animations, bcs they're defined in such a way that such jumps don't happen)

Coding references:
a) Iirc, the "Global Smoothing" option in AcidBubbles' Timeline implements a cubic spline that is "smooth to degree 2" (C^2 in math-speak) - ie, position, velocity & acceleration are continous everywhere over a number of keyframes

b) Linear, quadratic and cubic "Bezier-splines" (expressed in "Berstein-Polynomials" - simply a fancy word for the De-Casteljau algorithm) are implemented in MacGruber's IdlePoser - "MacGruber.IdlePoser.9.var\Custom\Scripts\MacGruber\IdlePoser\Internal\MacGruber_IdlePoserCore.cs"

* Line 1340: private Vector3 EvalBezierLinearPosition(float t)
* Line 1345: private Vector3 EvalBezierQuadraticPosition(float t)
* Line 1355: private Vector3 EvalBezierCubicPosition(float t)

P.S.: Only talking about position data, not rotation - afaik, pretty much everybody just "(s)lerps" the latter.
P.P.S.: Apologies if you know all that already & I was just too stoopid & overlooked it in the code.
 
Last edited:
This can be bring potentially a lot of interesting results, but unfortunately I am not a person who is familiar with state machines.
Any chance for a demo scene of some kind?
Yes! I am actually planning on creating a demo scene here soon to show off some of the features and give a good idea of the things you can do.


...
So I created a group called "Head", made one state for it "State1"
Then I created two layers, one for head's Pos/Rot and another one for like a dozen Face Morphs and gave them all the needed keyframes (preview is running fine on both).
Both layers had their own animations, like "Head Idle" and "Face Morphs"
In the "States" tab I added both animations to the State.

Now to my problem: if I check "Playback enabled", only the face morphs are playing, not the head movements.
I was under the impression that one State with two Layers plays them both at the same time? Or do I just have a logical failure between my ears?
So if you want two animations playing in parallel, you actually need to create two groups. So maybe something like "Head Position" and "Head Expression". In a state, only one of the animations is playing at a time. The animation playlist is for playing a sequence of animations (instead of all of the animations in it at once). Hope this helps! Like I mentioned above I am planning on making a demo scene to show off some features.
 
...
1) Anchoring feature - Any plans on implementing that?
...
2) You mention "easings" or "tweens" - does that mean you've implemented some form of splines? Which form exactly (Hermite splines, Bernstein/De Casteljau, Cadmul-Rom, Cardinal? - Strictly speaking, they're all Bezier-splines, but there's a lot of variation as to how the smootheness-requirement ("tangent-matching") is implemented).
...
1) Yes, I do have plans on implementing some kind of anchoring feature at some point. I'm not sure how long it will take for me to do it, but it is definitely something I am thinking about and would like to have.

2) Yes, you are absolutely right. Basically the interpolation only looks at the current and next keyframe and interpolates directly between them using the easing function you select from the dropdown (i.e. it follows the "interpolation condition" you mention). There is no consideration for any "smoothness condition" and indeed the overall movement is only C0 continuous -- no consideration for velocity or acceleration smoothness. Currently you'd have to just eyeball it and manually make your animations look smooth.

It's worth mentioning that this would actually be a very difficult problem to solve because of the many, many factors that can cause animations to change -- they can even change mid-keyframe. Overall I think direct interpolation between keyframes is the best compromise of ease of implementation, ease of user understanding, and amount of user control. Its definitely something I will keep in the back of my mind though.
 
Ok, totally understood, thx for the explanation, worked well.

However, I have another comment:
If I save lets say the Head Movement as a group for re-use in another scene and load it on a person atom with a different position -> the head tries to reach the position where the group was created and model breaks.
Is there a way to save them w/o root nodes or fixed positions?
This way one could hardly use any saved stuff, unless the model is at the exact Pos/Rot/Pose like when the Group was saved.
 
This looks interesting! Eager to see that before mentioned demo to understand it better. Meanwhile I'll fiddle with it a bit and see how far I can go.

Anyway thanks for creating this and looking forward to see what fun can be done with it :)
 
2) Yes, you are absolutely right. Basically the interpolation only looks at the current and next keyframe and interpolates directly between them using the easing function you select from the dropdown (i.e. it follows the "interpolation condition" you mention). There is no consideration for any "smoothness condition" and indeed the overall movement is only C0 continuous -- no consideration for velocity or acceleration smoothness. Currently you'd have to just eyeball it and manually make your animations look smooth.

It's worth mentioning that this would actually be a very difficult problem to solve because of the many, many factors that can cause animations to change -- they can even change mid-keyframe. Overall I think direct interpolation between keyframes is the best compromise of ease of implementation, ease of user understanding, and amount of user control. Its definitely something I will keep in the back of my mind though.

Hmmmh - I'm not a coder, so take with truckloads of salt, but both Timeline and AnimationPoser (since v3.7) implement "GlobalSmoothing", which should be by far the hardest & computationally most challenging problem to solve?

I could be wrong, ofc, but my impression is that AnimationPoser should be similar enough to your usecase that you may even be able to "borrow" the respective code? (which HaremLife said is borrowed/adapted from TImeline, which in turn is based on a public tutorial (iirc) https://pomax.github.io/bezierinfo/)
 
How the heck do I even switch between 2 simple states?

 
Can't figure out how to manually trigger a state change either... Equivalent setup to @Saint66 above. StateA has some animations, stateB has others, and stateAB has one transition animation and goes to stateB when that animation ends. There's a button with a SendMessage action to the plugin, and a message __self:ab set up as a listener in stateAB, but it doesn't work. Could you explain the messaging system a bit more?

How embarrassing, figured it out almost immediately after posting ... :p
Apparently, when sending a custom message to a character manually, you don't include the "role" part of the message. So if stateAB has a "__self::aaa" listener, the button action should be a SendMessage action with only "aaa" as the text. If a different character with a role sends a message (done automatically I believe?) _then_ that character's role is prefixed to the message. Messages with other roles than __self can only come from characters. I think that's correct?
 
Last edited:
Trying to use it, but I keep getting errors and crashes.

What I want to do is to have a few states that have different poses. For each State (and pose, like standing, sitting,etc) i want to have a few small movement animations.

How do I handle the change between states? I tried to have a LoadPose trigger on state entry, and it kinda works, but has a long delay and breaks the model.

I tried to have different keyframes that look kinda like different pose, but when it loads that State it crashes.

How should I handle this idea?
 
Back
Top Bottom