Search Unity

In Unity 2018.2, the Animation C# Jobs feature extends the animation Playables with the C# Job System released with 2018.1. It gives you the freedom to create original solutions when implementing your animation system, and improve performance with safe multithreaded code at the same time. Animation C# Jobs is a low-level API that requires a solid understanding of the Playable API. It’s therefore aimed at developers who are interested in extending the Unity animation system beyond its out-of-the-box capabilities. If that sounds like you, read on to find out when is it a good idea to use it and how to get the most out of it!

With Animation C# Jobs, you can write C# code that will be invoked at user-defined places in the PlayableGraph, and thanks to the C# Job System, the users can harness the power of modern multicore hardware. For projects which see a significant cost in C# scripts on the main thread, some of the animation tasks can be parallelized. This unlocks valuable performance gains. The user made C# scripts can modify the animation stream that flows through the PlayableGraph.

Features

  • New Playable node: AnimationScriptPlayable
  • Control the animation data stream in the PlayableGraph
  • Multithreaded C# code

Disclaimer

The Animation C# Jobs is still an experimental feature (living in UnityEngine.Experimental.Animations). The API might change a bit over time, depending on your feedback. Please join the discussion on our Animation Forum!

Use cases

So, say, you want to have a foot-locking feature for your brand new dragon character. You could code that with a regular MonoBehaviour, but all the code would be run in the main thread, and not until the animation pass is over. With the Animation C# Jobs, you can write your algorithm and use it directly in a custom Playable node in your PlayableGraph, and the code will run during PlayableGraph processing, in a separate thread.

Or, if you didn’t want to animate the tail of your dragon, the Animation C# Jobs would be the perfect tool for setting up the ability to procedurally compute this movement.

Animation C# Jobs also gives you the ability to write a super-specific LookAt algorithm that would allow you to target the 10 bones in your dragon’s neck, for example.

Another great example would be making your own animation mixer. Let’s say you have something very specific that you need – a node that takes positions from one input, rotations from another, scales from a third node, and mixes them all together into a single animation stream – Animation C# Jobs gives you the ability to get creative and build for your specific needs.

Examples

Before getting into the meaty details of how to use the Animation C# Jobs API, let’s take a look at some examples that showcase what is possible to do with this feature.

All the examples are available on our Animation Jobs Samples GitHub page. To install it you can either git clone it or download the latest release. Once installed, the examples have their own scenes which are all located in the “Scenes” directory:

LookAt

The LookAt is a very simple example that orients a bone (also called a joint) toward an effector. In the example below, you can see how it works on a quadruped from our 3D Game Kit package.

TwoBoneIK

The TwoBoneIK implements a simple two-bone IK algorithm that can be applied to three consecutive joints (e.g. a human arm or leg). The character in this demo is made with a generic humanoid avatar.

FullbodyIK

The FullbodyIK example shows how to modify values in a humanoid avatar (e.g. goals, hints, look-at, body rotation, etc.). This example, in particular, uses the human implementation of the animation stream.

Damping

The Damping example implements a damping algorithm that can be applied to an animal tail or a human ponytail. It illustrates how to generate a procedural animation.

SimpleMixer

The SimpleMixer is a sort of “Hello, world!” of animation mixers. It takes two input streams (e.g. animation clips) and mixes them together based on a blending value, exactly like an AnimationMixerPlayable would do.

WeightedMaskMixer

The WeigthedMaskMixer example is a bit more advanced animation mixer. It takes two input streams and mixes them together based on a weight mask that defines how to blend each and every joint. For example, you can play a classic idle animation and take just the animation of the arms from another animation clip. Or you can smooth the blend of an upper-body animation by applying successively higher weights on the spine bones.

API

The Animation C# Jobs feature is powered by the Playable API. It comes with three new structs: AnimationScriptPlayable, IAnimationJob, and AnimationStream.

AnimationScriptPlayable and the IAnimationJob

The AnimationScriptPlayable is a new animation Playable which, like any other Playable, can be added anywhere in a PlayableGraph. The interesting thing about it is that it contains an animation job and acts as a proxy between the PlayableGraph and the job. The job is a user-defined struct that implements IAnimationJob.

A regular job processes the Playable inputs streams and mixes the result in its stream. The animation process is separated in two passes and each pass has its own callback in IPlayableJob:

  1. ProcessRootMotion handles the root transform motion, it is always called before ProcessAnimation and it determines if ProcessAnimation should be called (it depends on the Animator culling mode);
  2. ProcessAnimation is for everything else that is not the root motion.

The example below is like the “Hello, world!” of Animation C# Jobs. It does nothing at all, but it allows us to see how to create an AnimationScriptPlayable with an animation job:

The stream passed as a parameter of the IAnimationJob methods is the one you will be working on during each processing pass.

By default, all the AnimationScriptPlayable inputs are processed. In the case of only one input (a.k.a. a post-process job), this stream will contain the result of the processed input. In the case of multiple inputs (a.k.a. a mix job), it’s preferable to process the inputs manually. To do so, the method AnimationScriptPlayable.SetProcessInputs(bool) will enable or disable the processing passes on the inputs. To trigger the processing of an input and acquire the resulting stream in manual mode, call AnimationStream.GetInputStream().

AnimationStream and the handles

The AnimationStream gives you access to the data that flows through the graph from one playable to another. It gives access to all the values animated by the Animator component

It isn’t possible to have a direct access to the stream data since the same data can be at a different offset in the stream from one frame to the other (for example, by adding or removing an AnimationClip in the graph). The data may have moved, or may not exist anymore in the stream. To ensure the safety and validity of those accesses, we’re introducing two sets of handles: the stream and the scene handles, which each have a transform and a component property handle.

The stream handles

The stream handles manage, in a safe way, all the accesses to the AnimationStream data. If an error occurs the system throws a C# exception. There are two types of stream handles: TransformStreamHandle and PropertyStreamHandle.

The TransformStreamHandle manages Transform and takes care of the transform hierarchy. That means you can change the local or global transform position in the stream, and future position requests will give predictable results.

The PropertyStreamHandle manages all other properties that the system can animate and find on the other components. For instance, it can be used to read, or write, the value of the Light.m_Intensity property.

The scene handles

The scene handles are another form of safe access to any values, but from the scene rather than from the AnimationStream. As for the stream handles, there are two types of scene handles: TransformSceneHandle and PropertySceneHandle.

A concrete usage of a scene handle is to implement an effector for a foot IK. The IK effector is usually a GameObject not animated by an Animator, and therefore external to the transforms modified by the animation clips in the PlayableGraph. The job needs to know the global position of the IK effector in order to calculate the desired position of the foot. Thus the IK effector is accessed through a scene handle, while stream handles are used for the leg bones.

AnimationJobExtensions

The last piece is the AnimationJobExtension class. It’s the glue that makes it all work. It extends the Animator to create the four handles seen above, thanks to these four methods: BindStreamTransform, BindStreamProperty, BindSceneTransform, and BindSceneProperty.

The “BindStream” methods can be used to create handles on already animated properties or for newly animated properties in the stream.

See also

API documentation:

If you encounter a bug, please file it using the Bug Reporter built in Unity.

For any feedback on this experimental feature, please go this forum thread: Animation C# jobs in 2018.2a5

返信する

これらの HTML タグや属性を使用できます: <a href=""> <b> <code> <pre>

  1. Hello

    Are you going to visually upgrade animation timeline ? I mean, in the current version we have all keyframes in the same color, shape and (up down) lines are very close to eachother. Work flow is very very weak compared to this in the Spine. In the Spine we have colors for each key frame’s type, scaling is red, transform blue etc. What also is importing, filtering would be a great idea, when I click on the texture I could see only this texture’s key frames, not all. Hope that timeline will be upgraded because currently really slow down work flow and new features are not so wonderful when we cant use them in the work flow enought.

    Regards,
    Michał

    1. Romain Failliot

      9月 15, 2018 5:17 am 返信

      Hi Michał,

      We are actually working on it, but I can’t really say more here since it is not really related to this blog post. If you would like to know more, or if you would like to express your ideas, I encourage you to use our animation forums: https://forum.unity.com/forums/animation.52/

      Thank you ;)
      Romain

  2. Aubrey Hesselgren

    8月 30, 2018 5:31 pm 返信

    Really great to have some examples. I’ve been trying some things with this, and so it’s great to have something to sanity check against.

    I’d love to see more about how we are supposed to use the Root Motion aspect of this. I’ve been using OnAnimatorMove() to receive the output of root animation on a generic rig, from our own custom graph, but any time a loop occurs in an AnimationClipPlayable, the delta I’m given jumps me back (i.e. it’s not noticing the loop, and thus doesn’t). I wonder if this is something you intended the AnimationStream to allow you to fix? And if so, how should I go about signalling that an AnimationClipPlayable has looped this frame? Should I have one of these animation scripts wrapping every AnimationClipPlayable? Or is this simply a bug?

    1. Aubrey Hesselgren

      8月 30, 2018 10:13 pm 返信

      Please disregard my last post. It seems that the issues has been patched in a recent update. Phew.

      Anyway. Very happy about these extra docs. Thank you.

  3. Really great article! Lots of examples and useful information.

  4. “Romain Failliot”, I just wanted to thank you for a great blog post. Animation C# Jobs was a feature that was in dire need of some better explanation, and I think your blog post was a great mix of useful “laymans” translations of what things do, some conceptual ideas of what one may want to do with it, and actual examples. Was not too long or short a read either! Keep up the great articles, hopefully you will post some more!

    1. Romain Failliot

      8月 28, 2018 4:00 pm 返信

      Thank you! That’s really appreciated ;)