# Help needed with syncing animation speed of a player in a Networked game (Unity)

Follow-up with new code, as per below answer. Unfornutely i still have it wrong and now the character doesn't animate at all (it seems to stay at 0.00 of the clip for ever or constantly resets to 0.00 in each frame). The code looks a bit lengthy but there's really not that much too it. I appreciate any of you expert folk taking a closer look and would happily buy you a coffee or two via paypal if you can help me out with this mind-boggling problem :S :]

nb. The object has always had an Animator and NetworkAnimator on the parent object. It also has NetworkIdentity and NetworkTransform. The Identity of the player (the object in question) is set to 'Local Player Authority'

new code (not working still!):

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class Player_mp : NetworkBehaviour {
public float walkSpeed, sideSpeed, turnSpeed, aimSpeed;
Vector3 inputInWorldSpace;
public Gun currentGun;
public string walkAnim, walkBackAnim, strafeLeftAnim, strafeRightAnim, turnLeftAnim, turnrightAnim, jumpAnim, runAnim, runBackAnim;
public GameObject bulletPrefab;

[SyncVar(hook = "OnAnimChangeSpeed")] float animationSpeed;
[SyncVar(hook = "OnAnimChangeString")] string animationString;
Vector3 velLocalised;

private void Update()
{
if (!isLocalPlayer)
{
return;
}
// QUESTION:: Is this meant to also be a SyncVar????
velLocalised = transform.InverseTransformVector(GetComponent<CharacterController>().velocity);
}

void FixedUpdate () {
if (!isLocalPlayer)
{
return;
}

float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
transform.SetPositionAndRotation(transform.position, Quaternion.Euler(transform.eulerAngles.x, transform.eulerAngles.y + (mouseX * turnSpeed * Time.deltaTime), transform.eulerAngles.z));

if (Input.GetMouseButtonDown(0))
{
CmdFireBullet(); // NOTE::: Note this Command has always worked as I expected to shoot bullets
}
Vector3 inputs = new Vector3(Input.GetAxis("Horizontal") * walkSpeed * Time.deltaTime, 0, Input.GetAxis("Vertical") * sideSpeed * Time.deltaTime);
inputInWorldSpace = transform.TransformVector(inputs);

gunShoulderRestTransform.eulerAngles = new Vector3(gunShoulderRestTransform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
gunShoulderRestTransform.Rotate(mouseY, 0, 0);

if (inputs.z > 0)
{
animationSpeed = velLocalised.z;

}
else if (inputs.z < 0)
{
animationSpeed = velLocalised.z;
CmdChangeAnimString(walkBackAnim);
}
else if (inputs.x > 0)
{
animationSpeed = velLocalised.x;
CmdChangeAnimString(strafeRightAnim);
}
else if (inputs.x < 0)
{
animationSpeed = velLocalised.x;
CmdChangeAnimString(strafeLeftAnim);
}
else
{
animationSpeed = velLocalised.z;
}
OnAnimChangeSpeed(animationSpeed);
OnAnimChangeString(animationString);

}

private void LateUpdate()
{
if (!isLocalPlayer)
{
return;
}

GetComponent<CharacterController>().Move(inputInWorldSpace);

}

[Command] // Handles Firing Bullets..
void CmdFireBullet()
{
GameObject bullet = Instantiate(bulletPrefab);
NetworkServer.Spawn(bullet);
Vector3 bulletForce = currentGun.transform.forward * currentGun.bulletForce;
bullet.GetComponent<Bullet>().Fire(currentGun.bulletSpawn.transform.position, currentGun.bulletSpawn.transform.eulerAngles, bulletForce);

}

// Functions to change animation speed per client as per it's character controllers velocity....
void OnAnimChangeSpeed(float speed)
{
Debug.Log("anim speed change detected");
CmdChangeAnimSpeed(speed);
}
[Command]
void CmdChangeAnimSpeed(float animSpeed)
{
RpcChangeAnimSpeed(animSpeed);
}
[ClientRpc]
void RpcChangeAnimSpeed(float animSpeed)
{
animSpeed = Mathf.Abs(animSpeed);
Debug.Log("animspeed ABS = " + animSpeed);
GetComponent<Animator>().speed = animSpeed;
Debug.Log("actual animator speed = " + GetComponent<Animator>().speed);
}

// These functions will change the string of the current animation and use it to call anim.Play(string) when called upon.
void OnAnimChangeString(string animName)
{
CmdChangeAnimString(animName);
}
[Command]
void CmdChangeAnimString(string clipName)
{
SwitchCurrentAnimation(clipName);
}
void SwitchCurrentAnimation(string clipName)
{
RpcChangeAnimString(clipName);
}
[ClientRpc]
void RpcChangeAnimString(string clipName)
{
// QUESTION::: Does 'anim' need to be a SyncVar?????
GetComponent<Animator>().Play(clipName);

}

//  This is purely for the Hand to Gun IK (working already) it makes hands hold gun correctly....
private void OnAnimatorIK(int layerIndex)
{
GetComponent<Animator>().SetIKPosition(AvatarIKGoal.LeftHand, currentGun.leftGrip.position);
GetComponent<Animator>().SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);

GetComponent<Animator>().SetIKPosition(AvatarIKGoal.RightHand, currentGun.rightGrip.position);
GetComponent<Animator>().SetIKPositionWeight(AvatarIKGoal.RightHand, 1.0f);

GetComponent<Animator>().SetLookAtWeight(1.0f, 0f);
}
}


ORIGINAL QUESTION:

Hello again I am attempting to make a very basic multiplayer networked game using Unity. Right now to keep it fairly simple I just want to be able to have multiple players join the world and be able to walk around.

My problem arises because I am using the velocity of the player to adjust the animation speed. So if the player isn't moving the animation speed is 0, etc.

At one point i thought i had it working with ClientRPC calls, but I have since written more code (I added side stepping as well as forward backwards animations) and it seems to not work again.

The current code seems to be working great for the local client but not at all changing the animation speed on the remote clients, it just stays at 1.0f.

I'd be very grateful if someone can look at this code and tell me what I've done wrong. I've made this error and been stuck at this very point on other projects I've worked on. Heres the code. Thanks!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class Player_mp : NetworkBehaviour {
CharacterController characterCont;
Animator anim;
public float walkSpeed, sideSpeed, turnSpeed, aimSpeed;
Vector3 inputInWorldSpace;
public Gun currentGun;
public string walkAnim, walkBackAnim, strafeLeftAnim, strafeRightAnim, turnLeftAnim, turnrightAnim, jumpAnim, runAnim, runBackAnim;
public GameObject bulletPrefab;

float animationSpeed;
Vector3 velLocalised;

public override void OnStartClient()
{
characterCont = GetComponent<CharacterController>();
anim = GetComponent<Animator>();
}

void FixedUpdate () {
if (!isLocalPlayer)
{
return;
}

float mouseX = Input.GetAxis("Mouse X");
float mouseY = Input.GetAxis("Mouse Y");
transform.SetPositionAndRotation(transform.position, Quaternion.Euler(transform.eulerAngles.x, transform.eulerAngles.y + (mouseX * turnSpeed * Time.deltaTime), transform.eulerAngles.z));

if (Input.GetMouseButtonDown(0))
{
CmdFireBullet();
}

Vector3 inputs = new Vector3(Input.GetAxis("Horizontal") * walkSpeed * Time.deltaTime, 0, Input.GetAxis("Vertical") * sideSpeed * Time.deltaTime);
inputInWorldSpace = transform.TransformVector(inputs);

gunShoulderRestTransform.eulerAngles = new Vector3(gunShoulderRestTransform.eulerAngles.x, transform.eulerAngles.y, transform.eulerAngles.z);
gunShoulderRestTransform.Rotate(mouseY, 0, 0);

velLocalised = transform.InverseTransformVector(characterCont.velocity);

if (isServer)
{

// select current clip depending on input (would rather do it by the velocity if i could get it to work!):
if (inputs.z > 0)
{
animationSpeed = velLocalised.z;
RpcSetAnimClip(walkAnim);
}
else if (inputs.z < 0)
{
animationSpeed = velLocalised.z;
RpcSetAnimClip(walkBackAnim);
}
else if (inputs.x > 0)
{
animationSpeed = velLocalised.x;
RpcSetAnimClip(strafeRightAnim);
}
else if (inputs.x < 0)
{
animationSpeed = velLocalised.x;
RpcSetAnimClip(strafeLeftAnim);
}
else
{
animationSpeed = velLocalised.z;
}
RpcSetAnimSpeed(animationSpeed);
}
}

private void LateUpdate()
{
if (!isLocalPlayer)
{
return;
}

characterCont.Move(inputInWorldSpace);

}

[Command]
void CmdFireBullet()
{
GameObject bullet = Instantiate(bulletPrefab);
NetworkServer.Spawn(bullet);
Vector3 bulletForce = currentGun.transform.forward * currentGun.bulletForce;
bullet.GetComponent<Bullet>().Fire(currentGun.bulletSpawn.transform.position, currentGun.bulletSpawn.transform.eulerAngles, bulletForce);

}

[ClientRpc]
void RpcSetAnimSpeed(float animSpeed)
{
if (animSpeed < 0)
{
animSpeed *= -1;
}
anim.speed = animSpeed;
}
[ClientRpc]
void RpcSetAnimClip(string clipName)
{
anim.Play(clipName);
}
[ClientRpc]
void RpcSetAnimSpeedAndClip(float animSpeed, string clipName)
{
if (animSpeed < 0)
{
animSpeed *= -1;
}
anim.speed = animSpeed;

anim.Play(clipName);
}

private void OnAnimatorIK(int layerIndex)
{
anim.SetIKPosition(AvatarIKGoal.LeftHand, currentGun.leftGrip.position);
anim.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);

anim.SetIKPosition(AvatarIKGoal.RightHand, currentGun.rightGrip.position);
anim.SetIKPositionWeight(AvatarIKGoal.RightHand, 1.0f);

anim.SetLookAtWeight(1.0f, 0f);
}
}


If your intent is to create a basic multiplayer networked game, I raccomend following this tutorial series

In the past, I had the same problem with some of my variables that I was not synchronizing. Try to add the [SyncVar] attribute to your animSpeed variable in order to synchronize it correctly. You should also test the animation speed in a new empty project, just to make sure everything works properly.

To make your animation speed system network aware and working under Server authority, you need to use State Synchronization and a special member variable on networked objects called SyncVars. Network synchronized variables, or SyncVars, are indicated with the attribute [SyncVar]. For more information on SyncVars, please see the page on State

Your script is quite long so I decided to give you a basic solution to your problem:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;

public class Test : NetworkBehaviour
{
public Animator anim;

//Detects when a animation speed change happens and calls the appropriate function
[SyncVar(hook = "OnChangeSpeed")]
public float animSpeed = 1;

private void Update() {
if(islocalPlayer){//if player is mine
animSpeed = GetComponent<Rigidbody>().velocity.magnitude;
}
}

void OnChangeSpeed(float speed){
if(islocalPlayer){//if player is mine
CmdSetAnimSpeed(animSpeed);
}
}

//This is a Network command, so the animation speed is done to the relevant GameObject
[Command]
void CmdSetAnimSpeed(float animSpeed)
{
//Apply animSpeed
RpcSetAnimSpeed(animSpeed);
}

[ClientRpc]
void RpcSetAnimSpeed(float animSpeed)
{
//Instead of multiplying -1 use Absolution
// if (animSpeed < 0)
// {
//     animSpeed *= -1;
// }
animSpeed = Mathf.Abs(animSpeed);
anim.speed = animSpeed;
}
}

• Hi friend and thanks for the speedy help. Sadly I havent yet been able to get it working with this coding. I've tried to apply the above into my code and now the animation isn't moving the legs at all (or any part of the mesh, althogh the animSpeed and anim.speed variables are changing as expected !?!?!) I will keep trying more and then i will post additional code into my question to show what I've done so far (In the hope that you can kindly check it over for me if you get time ever. ) Many thanks for the help so far too. – Big T Larrity Oct 2 '18 at 17:36
• By the way, I have worked thru and fully completed the mentioned Tutorial more than a few times. I am now trying to build on that and make a game using proper animations and meshes etc – Big T Larrity Oct 2 '18 at 17:41
• @SuperMegaBroBro I didn't test my code but It should work! try to test my code In the new project and make sure that your player have NetworkIdentity.try again by using Network Animator. – Seyed Morteza Kamali Oct 2 '18 at 17:46
• will do that also now thanks. I was reading Unity documentation at earlier date, i was under illusions Rpc calls needed to be preceded by if (!isServer) return; ... im probably very wrong. thanks again, im going to make a fresh project and test now ... NOTE ::: I have now posted the updated code into the original question, the player does not even animate anymore :[ ) – Big T Larrity Oct 2 '18 at 17:49
• The object has always had a Animator and NetworkAnimator on the parent object. It also has NetworkIdentity and NetworkTransform. The Identity of the player (the object in question) is set to 'Local Player Authority' – Big T Larrity Oct 2 '18 at 18:03