Happy Thursday all. First things first:
PROJECT ZOMBOID 40% OFF, NOW AND ALL WEEKEND. TELL YOUR FRIENDS. ALL YOUR FRIENDS. Etc. Etc.
(Sorry about all this.)
So anyway, it’s been a week of fixing stuff on the animations build generally this week – and usually we’d be doing a ‘sorry, dull blog today’ blog.
HOWEVER some of you might find the deeper elements of the animations system interesting, and we’re also secretly hoping that a fair few of you will be interested in digging into the dev/modding tool yourselves once AnimZed is released alongside Build 41.
Sooooo… Zac has kindly written something up for us that’s a bit more technical than we usually talk about – but thankfully RJ has also provided a quick vid of something fun he’s implementing before we get into the nitty-gritty.
What you can see in the vid below is our first pass on the various animations that can impact on your walking and running – all of which will get some balance love before any test release.
First off the video you see a scratch cause a light limp – and a limping run. Burns will also have the same impact, alongside cuts – a new injury type that’s somewhere between our existing scratch and deep wound.
After this: a deep wound that triggers heavy walk limps. You can still try to run, but it’ll be slow – on a par with the walk speed with a light limp. Finally in the vid: a fracture. This is currently a heavy limp, but it will forbid a run. This will also be the case if you have glass shards, bad burns or a leg bite.
Okay, with that all explained: here comes the science.
Over to you Zac.
THE ANIM PROBLEM
The Animation system needed a big quality pass. Plagued by dozens of tiny issues with snaps and odd transitions, the system was functional – but a rough diamond in need of refinement.
THE ANIM OVERVIEW
So, basically, the Animation system is divided into three layers: the AnimState, AnimNode and AnimTracks.
The AnimState handles the big picture: what the character is currently doing. For example, the character is standing idle, walking, sneaking, and so on. The character can only ever be in any one state at a time.
Next layer down is the AnimNode. These handle the middle-detail of what the character is up to, like walking while holding a crowbar, sneaking while walking sideways, etc. The state determines what nodes should be active, and assigns a weight to each. As nodes come and go, their weights are faded in and out.
Finally, the bottom-most layer is the AnimTracks. These are the fine-grained animations that get mixed and applied to the character’s bones. Tracks are also weighted and transitioned, similar to nodes.
One of the common issues we had was visible snapping between nodes. For example, when a character goes from strafing to sprinting, while holding a 2-handed weapon, the weapon would go from being nearly vertical to suddenly snapping to being horizontal, in under 3 frames.
The first step was to refactor the code itself. From the ground up, the code was tidied and tightened up to be cleaner and more efficient.
Next, it was a matter of finding out exactly what the weightings were at the time the issues were occurring.
There was no easy way to make the game stop and show us the data. There was no crash, no exception, no stackdump.
So, a recorder was written up to take the values of weights and times, and dump them out to the console in a comma-separated format. Next, it was a simple matter of putting it into a spreadsheet, and having a close look at the various curves.
It was immediately apparent that the curves were not smooth. The blending algorithm needed some work.
So, we needed to use math(s).
A set of quadratic tweening functions were written up, called EaseOut, EaseIn, and EaseOutIn. A square-root function was also written, but turned out to be too CPU-intensive for what it was intended.
The AnimNode weight transitions were upgraded with these easing functions, and pretty soon we had some nice and smooth transitions between nodes.
The AnimTracks got the same treatment, and transitions between tracks became much smoother as well. The number of snapping issues greatly decreased, and much joy was had. However, a few issues continued to stubbornly evade us.
Deciding to focus on one issue at a time, we chose the crowbar strafe->run transition. A video was taken, alongside the recorded weight data for states, nodes, and tracks. The individual frames were then painstakingly arranged alongside the generated curves, and then closely examined.
Once the track weight curves were stacked and normalized, a problem became visible. There was a rogue Idle animation sneaking into the mix. Its weight would reach barely 40% before it disappeared, over a period of just 10 frames. This made us intrigued, but also sad and weary.
With this clue, the field of possibilities was greatly narrowed. The cause was soon found. As the strafe node was transitioning out, it was still processing its blend-field. Even though the character was now in the sprint state, its strafe state was still active, and it thought that the character was standing still.
So, in its few last dying frames, the strafe state would blend in the Idle animation, just before snapping it off again.
With the root cause discovered, the solution was simple. Stop any transitioning-out nodes from applying their blend-field. The fix was made, and the last remaining snapping issues were conquered. HOORAY!
There were a few lessons learned. Firstly, Math(s) is great – even when we got down to brass tacks and went old school on this issue.
The second was how helpful it was to observe, reproduce, and document every known issue in a visible list. Then performing a ground-up service and tune-up, systematically fixing each issue one at a time.
Finally, that it is important to have visibility on the internal workings of the system. Instead of relying on stop-start debugging, we need a mechanism for recording all state data as it changes over time, and present it in a human-readable format.
Thing was, the spreadsheet and curves were incredibly useful, but were slow and cumbersome to use.
So! We are now building a fully-fledged recording viewer as a part and parcel of the AnimZed dev and modding tool. Huzzah!
We’re sorry this has been such a technical Thursdoid, easily the most involved one we’ve ever done, but in amongst all the cool vids we’ve been showing in past weeks we wanted to spotlight some of the work that’s been happening that’s not quite as sexy. (Well, not mainstream sexy anyway.)
We also know that there are some among you who really dig this sort of thing – but apologise whole-heartedly if you’re not. We’ll be a lot less clever-clever next week, and that’s a promise/inevitability.
This week’s winter that’s coming from eHxAtoMiic. A general list of stuff added to PZ, and vids of features being worked on, is kept here – so you don’t have to plough through endless dev blogs for info. The Centralized Block of Italicised Text would like to direct your attention to the PZ Wiki should you feel like editing or amending something, and the PZ Mailing List that can send blogs like this and patch notes direct to your mailbox. We also live on Twitter right here! Our Discord is open for chat and hijinks too!