Available in the Gaming / Industries Circle
quantum | v2 switch to V1  

Custom Animator

Level Advanced

Contents

Introduction

Quantum’s deterministic animator works by baking information from Unity’s Mecanim Controller. It imports every configuration such as the states, the transitions between the states, the motion clips and so on.

The main advantage of using it is that it can control animations to be 100% in sync across machines. Animations will snap to the correct state if a rollback informs that a transition happened some frames ago. So it provides very accurate tick based animations, which is something that is usually needed on Fighting and some Sports games because the animations should be perfectly in sync between all client simulations.

The Animator is not a part of the default Quantum SDK 2.0 version, but it was on the SDK 1.2.x versions. But we decided to stop improving this Animation tool because of the dependencies that it currently has on Unity's Mecanim, and because by the date that this document was written, Unity still hadn't launched any new stable animation tool. Be aware that if you use the tool, your team might have to improve it based on your game's animation needs.

Because of that, we decided to have the Animator as open source code that you can import into your quantum.code project, change and rebuild it accordingly to your needs.

This document explains how the Custom Animator should be imported and used into your own projects.

This package was lastly tested with Quantum SDK 2.1.0 A1 Nightly Build 659

Back To Top
 

Importing The Custom Animator Package

  1. Download the Custom Animator here;
  2. Unzip it and put the content of the folders QuantumCustomAnimator_state and QuantumCustomAnimator_systems wherever your prefer on your quantum.code project;
  3. The package comes with a partial implementation of the Frame.InitUser method, which is responsible for initializing the CustomAnimatorUpdater. If your project already have a partial implementation of the same method, please just make sure to include a call to the InitializeAnimatorUpdater method inside your own InitUser, and do not import the Frame.User.cs file from this package;
  4. With everything included in your solution, build your quantum.code project;
  5. On Unity, import the file QuantumCustomAnimator.unitypackage to the project;

This completes the initial steps to have it all building and running. Now, in order to actually use the component, assets and baking tools, here are the instructions:

Back To Top
 

Using The Custom Animator

  • To use the component, you can add it directly to an Entity Prototype on Unity. Otherwise, it is possible to create a new instance of the CustomAnimator component and add it to your entity directly via code:
var customAnimator = new CustomAnimator();
f.Set(fighter, customAnimator);
  • On Unity, you will create a new data asset of type CustomAnimatorGraph. This is the asset responsible for storing information related to Unity's Mecanim controller. So if you are using Entity Prototypes to setup your custom animator, then you can reference the animator graph asset there, on the AnimatorGraph field. If you want to find the asset on code, this is how you do it:

var animatorGraphAsset = f.FindAsset<CustomAnimatorGraph>(assetGuid);

  • Initializing the CustomAnimator component: if you are using Entity Prototypes and, on Unity, you have already defined which is the CustomAnimatorGraph to be used by the CustomAnimator as described above, then you don't need to initialize it, as it is already done using a Reactive Callback, on the CustomAnimatorSystem class. But in case you didn't define either the component using an Entity Prototype, or if you didn't choose the graph asset, then you can find the asset and set it to the animator like this:
CustomAnimator.SetCustomAnimatorGraph(&knight->CustomAnimator, animatorGraphAsset);
f.Set(fighter, customAnimator);
  • Similarly on how it is done on Unity, use Getters and Setters in order to read/write to the Animator:
// Getters
CustomAnimator.GetBoolean(frame, &fighter->Animator, "Defending");
CustomAnimator.GetFixedPoint(frame, &fighter->Animator, "Direction");
CustomAnimator.GetInteger(frame, &fighter->Animator, "State");

// Setters
CustomAnimator.SetBoolean(frame, &fighter->Animator, "Defending", true);
CustomAnimator.SetInteger(frame, &fighter->Animator, "Direction", 25);
CustomAnimator.SetFixedPoint(frame, &fighter->Animator, "Speed", FP._1);
CustomAnimator.SetTrigger(frame, &fighter->Animator, "Shoot");
  • The getter/setter methods has a series of method overloads that you can use if you already have the AnimatorGraph or the Variable ID. It is very useful as you can get the animator graph just one time, and re-use it for every getter/setter. Otherwise, the graph asset will need to be found every frame. To exemplify:

      var animatorGraph = frame.FindAsset<CustomAnimatorGraph>(animator->animatorGraph.Id);
    CustomAnimator.SetTrigger(frame, animator, animatorGraph, "Shoot");
    CustomAnimator.SetFixedPoint(frame, animator, animatorGraph, "Speed", 0);
    CustomAnimator.SetBoolean(frame, animator, animatorGraph, "Defending", true);
    
    // This way you will only need to find the asset one time, instead of 3 times
  • It is important to notice that Unity’s Trigger parameters works like Booleans here. So, in order to trigger some param, you need to use the SetBoolean(&fighter→Animator, "MyTrigger", true) to set it to true, and a SetBoolean(&fighter→Animator, "MyTrigger", false) on the next tick so the trigger isn’t activated again. The package already includes a system to be run before the animation system, which sets every trigger parameter to false every tick. Just insert this code new CustomAnimatorResetTriggersSystem(), into your SystemSetup.cs file;

  • Every AnimatorState has an AssetRef that you can use in order to make references to custom assets. You can extend the baking process on Unity, grabbing even more information from the Unity's animator, save these information on you custom asset and reference it on the state, so you can read it on the simulation. You can, for example, create assets which has hitbox/hurtbox information baked in the form of FPAnimationCurves. You can save Unity events names/time into the asset so on the simulation you know when some animation event happens, etc;

  • On your SystemSetup.cs file, insert this line of code after your own systems:

new CustomAnimatorSystem(),
  • On Unity, create the Custom Animator Graph asset, from the context menu: Create/Quantum/CustomAnimatorGraph;

This is how it looks like on its default state:

Custom Animator Default
  • Reference an Unity’s Animator Controller asset using the Controller field and hit the Import Mecanim Controller button. You should then see the animations information filled, such as the states, transitions, parameters and so on:
Custom Animator Baked
  • Open your EntityView class, and add this field:

       public CustomQuantumAnimator CustomQuantumAnimator;
     **PS:** Remember that this file can be overwritten upon Quantum version upgrade, so remember to re-insert these custom codes if needed.
    
  • Add the component CustomQuantumAnimator to the GameObjects that will be animated. Of course, it needs to have an EntityPrefabRoot on it, so make the reference to the component that you just added, using the field that you created on the instruction above;

Custom Animator Game Object
  • Open your EntitiyViewUpdater class and, on its OnUpdateView method, it has a foreach loop iterating over the active views. It should now include the custom animator updating, so this is how your foreach should look like:
// Iterate over all view instances and update components using only entities from current frame.
foreach (var kvp in _activeViews) {
    // ... updates 2D and 3D transforms

    // At the end of the foreach, add this.
    // This is responsible for actually animating the entity views
    if (instance.CustomQuantumAnimator)
    {
        if (game.Frames.Current.Unsafe.TryGetPointer<CustomAnimator>(instance.EntityRef, out var animator))
        {
            instance.CustomQuantumAnimator.Animate(game.Frames.Current, animator);
        }
    }
}
  • The last step is to add a Unity's Animator component to your object. It can be placed on the same object which has the EntityPrefabRoot or it can be placed on a child object:
Custom Animator Mecanim Controller

Now that you have done the replacements on your quantum code, the replacements needed on the Unity side, and you have your CustomAnimatorGraph with information baked directly from a Unity's Mecanim controller, you are ready to go.

Parts of the code already comes with simple comments. On the Quantum side you’ll find everything related to the internal state machine itself, such as the AnimatorConditions, which are used on the AnimatorTransitions, the AnimatorVariables (parameters), Layers, Motions and so on.

On the Unity side, you will find the code related to drawing the custom editor for the Custom Animator Graph asset, and all of the code responsible for the baking process, which you can surely extend in order to bake more data into your animator asset, such as FPAnimationCurves for collision information, animation events from Unity and so on.

Back To Top
 

Known Issues/Limitations

The CustomAnimator, as it comes by default, has these known limitations:

  1. No support for hierarchical states;
  2. No support for Unity’s animation events;
  3. No support for states with no motion (no animation clip set);
  4. No support for the Layers menu, which means that it's not possible to mix animations between more than one layer, such as using Avatar Masks, Blending and so on;
  5. When using Blend Trees, it is mandatory to have the blending value between [-1, 1], on both 1D and 2D Blend Trees;
  6. No support for Root Motion on 3D Transforms/PhysicsBody.

To Document Top