PUN Classic (v1), PUN 2 and Bolt are in maintenance mode. PUN 2 will support Unity 2019 to 2022, but no new features will be added. Of course all your PUN & Bolt projects will continue to work and run with the known performance in the future. For any upcoming or new projects: please switch to Photon Fusion or Quantum.

Bolt 106 - Animations

Bolt has built in support for replicating mecanim animations over the network, in this chapter we will look in detail on how you can play your animations over the network properly.

The first thing we need to do is to download a package that contains a model and some animations. This has just been ripped out of the Unity 'Stealth' sample from the asset store. If you prefer you can download the 'Stealth' package from the asset store and export the character yourself.

For the rest of this tutorial we will assume that you downloaded the package above. Import the package into your Tutorial project, create a new folder Robot and drag all related files to it.

Setup example package
Setup example package.

Find the Tutorial/Robot/Done/DonePrefabs/char_robotGuard prefab and drag it into our Tutorial/Prefabs folder and then rename it to Robot.

Use the Robot Prefab
Use the Robot Prefab.

Open up the Bolt Assets window and create a new state, call it RobotState. After you have create and named the state find the Import Mecanim Parameters setting on the state itself.

Create new Robot State
Create new Robot State.

Drag the mecanim controller for the Robot into the slot, you can find it in Tutorial/Robot/DoneAnimations/DoneEnemyAnimator.

Import Robot animator
Import Robot animator.

Click the Import button.

Click import Import to proceed
Click import Import to proceed.

Bolt will automatically import all the parameters in the mecanim controller for you into the state.

Imported mecanim properties
Imported mecanim properties.

After you imported the parameters from the animation controller a new setting pops up on the state called Mecanim (State Wide), this lets you control if you want to use the normal mecanim methods like animator.SetFloat and animator.SetBool to change the animator values or if you want to use the Bolt state properties instead.

If you are adding Bolt to a game that already exists and that's using the standard mecanim way of setting parameters on the animation controller it is best to leave this at the default Using Animator Methods setting. If you are building a new game with Bolt you should usually pick the Using Bolt Properties option, which is what we will do here.

Change Animator Methods to Bolt Properties
Change Animator Methods to Bolt Properties.

You will see that all of the parameters added by the import also get changed to Using Bolt Properties, you also get an additional option for controlling the mecanim damping time of the values. If you want you can run Bolt in Mixed Mode, where some properties are set to Using Animator Methods and some are set to Using Bolt Properties, in general we advice against this due to the confusion that can arise.

The last thing we need to do is to add a Transform property to our RobotState, we do the same as for the CubeState before.

  1. Click on 'New Property' at the top
  2. Name the created property 'Transform'
  3. Set the type of the property to 'Transform'
  4. Set 'Smoothing Algorithm' to 'Interpolation'
Robot Transform Asset
Robot Transform Asset.

Before we continue we need to do some configuration of our mecanim parameters also. First we want to disable network replication for the Shot and AimWeight parameters, as these are controlled by curves inside of the animations and not directly by the user. We don't actually use these parameters in the example, but if we don't disable them mecanim will complain as Bolt will try to set them.

There are two ways of disabling them, the easiest is just to remove them. The other way you can disable them is to set the Mecanim setting to Using Animator Methods and then Replication to Local. This means that Bolt will read out the values from the curves and expose them in properties for you, but it will not send anything over the network.

Disable some Robot State properties
Disable some Robot State properties.

The second part that we need to configure is the Damping Time on the Speed and AngularSpeed property, set it to 0.1 on both.

Change Damping Time
Change Damping Time.

Important: We are now done with the state for the robot character, make sure to compile Bolt before continuing.

It's time to setup our Robot prefab as a Bolt Entity, select the prefab in the Tutorial/Prefabs folder and add a Bolt Entity component to it.

Add Bolt Entity to Robot Prefab
Add Bolt Entity to Robot Prefab.

Change the State setting on the Bolt Entity component to IRobotState, and then compile Bolt again to get it to pick up the Robot prefab.

Set State and Compile Bolt
Set State and Compile Bolt.

Before we try out spawning our Robots in the world, we need to just tell Bolt to instantiate our new Robot prefab instead of the old Cube one. We do that by going into our NetworkCallbacks script and changing the BoltNetwork.Instantiate call to reference BoltPrefabs.Robot instead.

C#

// ...

public override void SceneLoadLocalDone(string map)
{
    // randomize a position
    var spawnPosition = new Vector3(Random.Range(-8, 8), 0, Random.Range(-8, 8));

    // instantiate cube
    BoltNetwork.Instantiate(BoltPrefabs.Robot, spawnPosition, Quaternion.identity);
}

// ...

If we build our game and start two instances, you should see something like below. Bolt will also report the following error to you, because we have not properly assigned an 'Animator' that we want to use to Bolt.

You have not assigned a mecanim animator to the state on Robot(Clone), but you have properties set to use mecanim.
First Gameplay with Robot Prefab
First Gameplay with Robot Prefab.

Create a new C# script called RobotBehaviour in the Tutorial/Scripts folder and like we did with our CubeBehaviour remove the default Unity methods and make the class inherit from Bolt.EntityBehaviour<IRobotState>.

C#

using System.Collections;
using UnityEngine;
using Bolt;

public class RobotBehaviour : Bolt.EntityBehaviour<IRobotState>
{

}

We are now going to override the Attached method and setup both our Transform property and assign an animator to the state for Bolt to use. The transform is assigned with state.SetTransforms(state.Transform, transform) like we did before with the CubeTransform property. The animator is assigned by calling state.SetAnimator and passing in the existing Animator component from our prefab.

We also want to make sure that 'root motion' is only used if you are the Owner, as otherwise the motion applied by it would be doubled on all remote peers seeing an entity, we simply assign entity.IsOwner to the state.Animator.applyRootMotion property.

C#

using UnityEngine;
using System.Collections;
using Bolt;

public class RobotBehaviour : Bolt.EntityBehaviour<IRobotState>
{
    public override void Attached()
    {
        state.SetTransforms(state.Transform, transform);
        state.SetAnimator(GetComponent<Animator>());

        state.Animator.applyRootMotion = entity.IsOwner;
    }
}

We also need to attach the RobotBehaviour component we just created to the actual Robot prefab.

Add Robot Behavior to Robot Prefab
Add Robot Behavior to Robot Prefab.

If you build and start the game now, our robot will keep standing still but we won't see any warnings. The last thing we need to do is to just add the code for controlling the root motion. We open up our RobotBehaviour again and implement the SimulateOwner method. The code in this method might look complex at first, but it's pretty simple.

  1. If W is down, we increase our speed (towards +1.5);
  2. If W is not down, we decrease our speed towards 0;
  3. If A is down, we change our angular velocity to negative (towards -1)
  4. If D is down, we change our angular velocity to positive (towards +1)
  5. If neither A or D is down, we move our angular velocity towards 0

C#

// ...

public override void SimulateOwner()
{
    var speed = state.Speed;
    var angularSpeed = state.AngularSpeed;

    if (Input.GetKey(KeyCode.W))
    {
        speed += 0.025f;
    }
    else
    {
        speed -= 0.025f;
    }

    if (Input.GetKey(KeyCode.A))
    {
        angularSpeed -= 0.025f;
    }
    else if (Input.GetKey(KeyCode.D))
    {
        angularSpeed += 0.025f;
    }
    else
    {
        if (angularSpeed < 0)
        {
            angularSpeed += 0.025f;
            angularSpeed = Mathf.Clamp(angularSpeed, -1f, 0);
        }
        else if (angularSpeed > 0)
        {
            angularSpeed -= 0.025f;
            angularSpeed = Mathf.Clamp(angularSpeed, 0, +1f);
        }
    }

    state.Speed = Mathf.Clamp(speed, 0f, 1.5f);
    state.AngularSpeed = Mathf.Clamp(angularSpeed, -1f, +1f);
}

// ...

At the start of the method we read out the state.Speed and state.AngularSpeed and store them in two local variables, we do this simply because it's easier to work with. At the end of the method we assign them back into the state and clamp speed between 0.0 and 1.5f, and clamp angularSpeed between -1 and +1.

If we build our game again, we can now walk around with the robot character using the WAD keys, below is a demonstration of the result.

Robot gameplay
Robot gameplay.

Congratulations, you have finished the Getting Started Tutorial! Great! Now you can go to the Advanced Tutorial and get a deeper look at the power of Bolt.

Back to top