# WIP - Everyone loves zombies! (plus occasional screenshots)

#25

Today I am trying to figure out a cheap way to implement Unity’s notion of BlendTrees for blending locomotion animations. Ideally, any number of input animations can be blended together, based on the velocity of the character (with respect to the direction it faces), but in practise, we only have to worry about a maximum of two animations at any moment. It works by defining a ‘characterspace direction’ in 2D for each animation to be blended - essentially we’re distributing animations around a 2D circle which is defined in the unrotated / identity space of the character - ie, relative to the direction that the character is facing.

My current idea goes something like this:
The character is moving with some linear velocity - a 3D vector.
Step number one is to transform that vector into the local space of the character, and drop the Y component, so we can think in terms of a 2D circle, on the XZ plane (and normalize it, so its just a 2D direction with unit length), and think in terms of trigonometry, where zero degrees is our Right vector, and ninety degrees is our Up vector.
Step two: without making assumptions about how many animations are involved?
We find the 2D dot product between the (transformed, 2D) velocity vector, and the direction associated with each animation. We capture the results in an array, and once we have all the dot values, we normalize the array, by dividing each value with the sum of all values. Note that if a dot value is negative, that animation is effectively disabled, and we set the value as zero. The remaining (positive, and normalized) dot values are the weights we should be applying to our animations.

I’m aware that this could be optimized by computing which Quadrant we’re working in, however that introduces a bunch of conditional logic, which is likely to cause the cpu to stall, as the compiler can’t optimize on branched logic. For a fistful of operations, it’s generally faster to avoid the branches, and simply perform operations that were not really necessary for the solution of the problem.

Anyone done any work in this area? I would love to hear your ideas / opinions!

0 Likes

#26

See the Doom animation talk:

They’re quite clear on the animation. There were some older GDC animation talks about correction for foot sliding and root motion, basically boiled down to correct for the constant motion not correct to lock a root in place.

I’ve been trying out the quat based retargeting from DOOM and using jiggle bones on everything for naturalizing stilted-programmer-animation. Not quite there, but getting there,

1 Like

#27

I don’t plan to recreate GDC solutions, I plan to find the cheapest path that works for me. I don’t think that gdc is the right path, just because someone there said it - math has two altruisms, the direct path to the answer, and the shortcut to the answer

0 Likes

#28

Today I fixed a bug in my foot-planting solution whereby the zombie was able to walk straight through static scenery. The fix involved two parts - first, I am careful to ignore the Y component of my error term, because I want the physics hull to look after changes of position in Y axis. This looked a lot better, but still not good enough.

Secondly, I needed to add a raycast to correct the resulting Y coordinate to account for the fact that my hull is a Capsule, not a box - the feet are not positioned neatly at the bottom of the capsule.

Now the zombie is able to roam “without foot slipping” across uneven terrain without sinking into it, floating above it, or any other weirdness. Results are “close to perfect”

The only current issue with the Zombie physics is that it’s using weak impulses to drive the character, which I deliberately have given a lot more mass than the Player - the result is that a slow moving zombie can’t climb gentle slopes, it needs to get some momentum happening to make it up a hill.
I’ll try switching to a force-based controller later today, and see if I can give the zombie some more “grunt”.

[EDIT] Switched to using Forces instead of Impulses - will take a bit of tweaking to get the values right, but there’s a lot more control, and no apparent problems with “hill-climbing”.

0 Likes

#29

I have no root motion, and the walk cycle is not constant, not linear… In the case of my zombie, we don’t want constant motion - it’s limping, or staggering, and in this case, constant motion actually causes foot-slipping. Therefore, I use a constant (but shifting) frame of reference, being the world position of the planted foot, assuming that only one (or not any) foot is planted at any time. The frame of reference is shifting (not moving) at a rate dictated by the animation, not based on any constant. It’s actually working pretty nicely, though there are still some small teething issues to sort out.

0 Likes

#30

Started working on blending locomotion animations (not using a proper blend tree, I’m using some switched logic and AnimationController layers).
First impressions are pretty bad - but the assets need some polish. My animations are not of equal length - it looks like Urho’s animationcontroller somehow compensates for this, because there’s no visual glitching occuring in the legs of my character, but there is some glitching in the arms and hands, which are further from the root node. I’ll start by making all my walk animations have the same play length, and see if that improves things at all. If all else fails, I can ask an artist for some diagonal walking animations.

0 Likes

#31

Today I synchronized the play length of my four walk animations (for player character).
Previously, they had the following lengths:
WalkForwards = 40 frames
WalkBackwards = 30 frames
StrafeLeft = 45 frames
StrafeRight = 45 frames

I chose to make them all 45 frames in length.
In order to reduce the glitching that remained when blending / moving diagonally, I set the Weight of forward/backward to 0.7, and the Weight of left/right to 0.3

The only thing that’s stopping me from applying my “foot-planting” solution to the player character, is related to how I created the “strafe right” animation - it’s a mirror of “strafe left”, which means that the leading foot is not the same in those animations - I’ll need to cut and paste half of my keyframes in order to rectify that.

Rather than screw around with the playback speed of individual animations, I used Blender to adjust the play lengths of my animations… if anyone is interested in how to do that, feel free to ask me.

0 Likes

#33

Work has begun on an improved ragdoll implementation.
The idea is to attach our ragdoll armature to our model on instantiation, with all the bodyparts set to Kinematic mode, so that they are driven by animations. If done correctly, we don’t care about the initial pose - bodyparts are instantianted in “bonespace”. I did not rotate these bodyparts! When it comes time to switch (some or all of) the armature to ragdoll mode, the bodyparts are already aligned to the skeletal armature. There are other advantages, too.

If this experiment works out well, I can probably afford to throw away the coarse outer collision hull entirely.

1 Like

#34
``````            CreateRagdollPart(adjustNode, "RightUpLeg", ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);
CreateRagdollPart(adjustNode, "RightLeg",   ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);

CreateRagdollPart(adjustNode, "LeftUpLeg",  ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);
CreateRagdollPart(adjustNode, "LeftLeg",    ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);

CreateRagdollPart(adjustNode, "RightArm",    ShapeType::SHAPE_CAPSULE, Vector3(0.15f, .25f, 0.0f),Vector3(-0.15f, 0, 0), Quaternion(0,0,90)) ;
CreateRagdollPart(adjustNode, "RightForeArm",ShapeType::SHAPE_CAPSULE, Vector3(0.1f,  .25f, 0.0f),Vector3(-0.15f, 0, 0), Quaternion(0,0,90)) ;

CreateRagdollPart(adjustNode, "LeftArm",    ShapeType::SHAPE_CAPSULE, Vector3(0.15f, .25f, 0.0f),Vector3(0.15f, 0, 0), Quaternion(0,0,90)) ;
CreateRagdollPart(adjustNode, "LeftForeArm",ShapeType::SHAPE_CAPSULE, Vector3(0.1f,  .25f, 0.0f),Vector3(0.15f, 0, 0), Quaternion(0,0,90)) ;``````

I’m starting with a code-driven approach for testing and debug purposes, but as soon as I’m happy, I’ll shove this data into a file and load it per character, as I already do for animation lists.

0 Likes

#35

At this point, I don’t even need physics constraints between bodyparts, so this armature is still incomplete, yet each bodypart is doing what it should - under kinematic mode, the bodypart constraints are already enforced without need for physics constraints.

1 Like

#36

Armature is completed (but still unconstrained by joint constraints). Note that I have bodies on the Feet, I plan to experiment with using collision detection to trigger my footfalls, instead of the animation triggers currently used. This will allow the foot-planting solver to work intelligently with uneven terrain and obstacles such as rubble. Whether or not I’ll end up using IK as part of this solver is not yet decided.
The reason this character appears to step outside its outer bounding hull is because I don’t enable the foot-planting solver until the first footfall is detected.

0 Likes

#37
``````            CreateRagdollPart(adjustNode, "Hips",   ShapeType::SHAPE_BOX,       Vector3(0.30f, .15f, 0.2f), Vector3(0,0.05f,0),     Quaternion::IDENTITY);
CreateRagdollPart(adjustNode, "Spine1", ShapeType::SHAPE_BOX,       Vector3(0.35f, .35f, 0.25f),Vector3(0,0.1f,0),      Quaternion::IDENTITY);

CreateRagdollPart(adjustNode, "RightUpLeg", ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);
CreateRagdollPart(adjustNode, "RightLeg",   ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);

CreateRagdollPart(adjustNode, "LeftUpLeg",  ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);
CreateRagdollPart(adjustNode, "LeftLeg",    ShapeType::SHAPE_CAPSULE, Vector3(0.2f, .45f, 0.0f),Vector3(0.0f, -0.2f, 0.0f), Quaternion::IDENTITY);

CreateRagdollPart(adjustNode, "RightArm",    ShapeType::SHAPE_CAPSULE, Vector3(0.15f, .25f, 0.0f),Vector3(-0.15f, 0, 0), Quaternion(0,0,90)) ;
CreateRagdollPart(adjustNode, "RightForeArm",ShapeType::SHAPE_CAPSULE, Vector3(0.1f,  .25f, 0.0f),Vector3(-0.15f, 0, 0), Quaternion(0,0,90)) ;

CreateRagdollPart(adjustNode, "LeftArm",    ShapeType::SHAPE_CAPSULE, Vector3(0.15f, .25f, 0.0f),Vector3(0.15f, 0, 0), Quaternion(0,0,90)) ;
CreateRagdollPart(adjustNode, "LeftForeArm",ShapeType::SHAPE_CAPSULE, Vector3(0.1f,  .25f, 0.0f),Vector3(0.15f, 0, 0), Quaternion(0,0,90)) ;

CreateRagdollPart(adjustNode, "RightFoot",  ShapeType::SHAPE_CAPSULE, Vector3(0.125f, .25f, 0.0f),Vector3( 0, -0.1f, -0.1f), Quaternion(90,0,0)) ;
CreateRagdollPart(adjustNode, "LeftFoot",  ShapeType::SHAPE_CAPSULE, Vector3(0.125f,  .25f, 0.0f),Vector3( 0, -0.1f, -0.1f), Quaternion(90,0,0)) ;``````

I’ve found these values, for this one character, empirically. This sucks, I can’t be doing this on every zombie model. I need to find a way to automate it, and make it data-driven. I already load a custom xml file per character model for the purposes of getting a list of animation info that are tied to a hardcoded enumeration. I could just extend that custom file to include bodyparts.

0 Likes

#38

Zombie can now detect when a foot touches the ground, and when it leaves the ground. I don’t need animation triggers to tell me, which bodes well for uneven terrain, and basic hill climbing/descent.
Looking forward to bringing in some new zombies and animations

0 Likes

#39

Oh no, it appears that listening for start and end of collision is totally unreliable for the purpose of detecting footfalls! Here’s what happens at runtime:

When the zombie begins to walk, I detect that a foot is beginning to touch the ground. A mere few frames later, the physics step happens (again), and despite the fact that my foot bodies are kinematic (so it’s not about collision response), I’m told that the foot has left the ground again - which is simply untrue! That foot is still touching the ground, yet I am told that collision has ended!

I am just lost for words. I really expected this to work reliably. I’ll tweak some numbers, but I have a really bad feeling about how stable a solution this will yield.

0 Likes

#40

The collision EVENT ended, though the foot is on the ground.

0 Likes

#41

I was under the impression that “collision ended” indicated that two colliding bodies have separated - if that is not the case, what exactly constitutes the “end of a collision event” ? I ask because several render frames and one physics step have passed between receipt of “collision start” and “collision end” events.

[EDIT]
I’ve just stared down the relevant code in PhysicsWorld.cpp and it appears that my assumption was correct: “collision end” is only sent when a collision that was collected in a previous physics step has ceased to exist, indicating that two colliding bodies have just separated. I’m going to debug-spew the world position of one of the feet, taking into account the radius on the foot body, just to double-check that my walk animation does not contain any translational glitch that would explain early detection of separation.

0 Likes

#42

Tweaking the numbers did help, but now I have yet another new issue - the zombie walk cycle is almost a shuffle - the feet are only raised by a very small amount when walking.
The position of the pelvis is currently dictated by an outer physics hull. That hull is a dynamic body, and now I am fighting the “jitter” introduced by bullet’s collision response (impulse-based penetration correction). The numbers introduced by the “jitter” of a dynamic body in constant contact with the ground, are in the same order of magnitude as my “foot lift” maximum height, leading to erratic and unpredictable triggering of the start/end of collision events.
I’m going to need to slightly adjust my animation curves on the zombie walk cycle to defeat the “jitterbug”, by raising the foot more than the “skin thickness” that bullet applies during restitution.

[EDIT]
I’ve just read somewhere that the default skin thickness for bullet is set to 0.001 units : problem is, I am seeing jitter values that average more than ten times that much! I’m seeing values in the order of 0.01 to 0.02 - still very small numbers, but big compared to the raised foot height of a shuffling zombie.
Ah! I remember giving the zombie a mass of 10 (so the player can’t push the zombie around). That might explain a lot about the numbers I am seeing

[POST EDIT]
I’ve just hit up one of my 3D artist contacts to beg for some help to adjust the curves on the problem walk cycle. He does not owe me any favours, and I did not offer to pay for his time, so chances are high that I’ll end up doing it myself. Technical art is within my realm, but I am definitely not an artist.

0 Likes

#43

You’re probably too deep into it for it to matter now, but Bullet has a PID example in the InverseDynamics section: https://github.com/bulletphysics/bullet3/tree/master/src/BulletInverseDynamics it’s a raw PID so it’s sort of sketchy with high-velocity and sudden stops, but a safe base.

I was under the impression that “collision ended” indicated that two colliding bodies have separated - if that is not the case, what exactly constitutes the “end of a collision event” ? I ask because several render frames and one physics step have passed between receipt of “collision start” and “collision end” events.

IIRC, bullet constructs the manifold over time/substeps, collision separation likely doesn’t occur until the active manifold no longer contains a point sourced from the respective other object.

There is a check in PhysicsWorld in Urho3D for the number of contacts to minimize that, but it can only be as reliable as Bullet is. If you really care about accuracy, you’re going to have to use a ghost-object/query to find it on demand.

0 Likes

#44

I don’t really care that much about accuracy. All my current problems stem from the fact that the zombie walk is almost a shuffle, and so contact make and break is not reliable. Today I enabled IK, and now the feet of my zombie are spinning around wildly Gah.

0 Likes

#45

Maybe you forgot a minus or transform space conversion?

0 Likes