I am currently working on a game and I have the following situation:
I have a Player prefab gameobject with a script attached to it (see below). I have set up Network Manager and I have set up an account under "Services" to be able to use the multiplayer aspect.
I have the basics set up so that the player does spawn and multiplayer does work. The player is able to move and I see the movement on the other players on each build session.
I have a chunk of code that when the player is "walking" (if either keys A, W, S, or D is pressed, I call "CmdWalk()".
Basically CmdWalk() makes it so that it changes my player's legs rotation so that it makes it seem like it is walking. (I am not into animation so this is the only way I know).
The issue is that only the local player is able to see their player "walk", the other players online does not see the movement. I am not sure what I have done wrong, can someone please help.
Below is the entire script I have for the player:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class PlayerController : NetworkBehaviour
{
public float speedH = 2.0f;
private float yaw = 0.0f;
public float WalkingTime; //timer for walking animation
public GameObject PlayerLeftLeg;
public GameObject PlayerRightLeg;
private float PlayerStatMenuTimer;
public GameObject PlayerStatsMenu;
// Update is called once per frame
void Update ()
{
if (!isLocalPlayer)
{
return;
}
//keep track of time for player stat menu
//if not here than menua will show and hide like a thousand times when pressed once due to update reading code per frame
PlayerStatMenuTimer = PlayerStatMenuTimer + 1 * Time.deltaTime;
//moving player left right forward backward
var x = Input.GetAxis ("Horizontal") * Time.deltaTime * 50.0f;
var z = Input.GetAxis ("Vertical") * Time.deltaTime * 50.0f;
transform.Translate (x, 0, z);
//rotating player or "Looking"
yaw += speedH * Input.GetAxis ("Mouse X");
transform.eulerAngles = new Vector3 (0.0f, yaw, 0.0f);
//if player is using WASD to move then do leg moving animation
//if not moving then set legs to be still and reset in standing position
//FYI: "transform.TransformVector(1,0,0)" was used instead of "Vector3.forward" was because
// vector3.forward is local space, so when i rotate player the sense of "forward" also changes, thus i needed
// a code that uses the world space, thus i used "transform.TransformVector(1,0,0)"
if (Input.GetKey (KeyCode.W) || Input.GetKey (KeyCode.S) || Input.GetKey (KeyCode.A) || Input.GetKey (KeyCode.D))
{
CmdWalk ();
}
else
{
//if player not walking then reset
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis (0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis (0, Vector3.forward);
WalkingTime = 0;
}
//get hidden mouse pointer back and unlock
if (Input.GetKey (KeyCode.Escape))
{
Cursor.lockState = CursorLockMode.None;
}
//opens and closes stat menu
if (Input.GetKey (KeyCode.Return) && (PlayerStatMenuTimer >= 1) && (PlayerStatsMenu.activeSelf == false))
{
Cursor.lockState = CursorLockMode.None;
PlayerStatsMenu.SetActive (true);
PlayerStatMenuTimer = 0;
//call the script "GetplayerStats" and call function "retrieceplayerstats"
var GetStats = GetComponent<GetPlayerStats> ();
GetStats.RetrievePlayerStats ();
}
else if (Input.GetKey (KeyCode.Return) && PlayerStatMenuTimer >= 1 && PlayerStatsMenu == true)
{
Cursor.lockState = CursorLockMode.Locked;
PlayerStatsMenu.SetActive (false);
PlayerStatMenuTimer = 0;
}
}
private void Awake ()
{
//this code locks mouse onto center of window
//Screen.lockCursor = true;
Cursor.lockState = CursorLockMode.Locked;
}
[Command]
void CmdWalk ()
{
//timer
WalkingTime += Time.deltaTime;
//right leg stepping forward
if (WalkingTime > 0 && WalkingTime < .4)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis (PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector (1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis (PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector (1, 0, 0));
}
//left leg stepping forward
if (WalkingTime >.4 && WalkingTime < 1.2)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis (PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector (1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis (PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector (1, 0, 0));
}
//right leg stepping forward
if (WalkingTime > 1.2 && WalkingTime < 1.59)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis (PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector (1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis (PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector (1, 0, 0));
}
//resetting
if (WalkingTime > 1.6)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis (0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis (0, Vector3.forward);
WalkingTime = 0;
}
}
}
Sorry for the amount of code but the only parts that needs to be looked at is the "IF statement" for keys "A" "W" "S" "D" and the "Void CmdWalk()"
Thanks.
It surprises me that the local Player
is seeing the movement. I'ld say only the Host / Server Player
can see it since
[Command]
void CmdWalk()
{
//timer
WalkingTime += Time.deltaTime;
//right leg stepping forward
if (WalkingTime > 0 && WalkingTime < .4)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
}
//left leg stepping forward
if (WalkingTime > .4 && WalkingTime < 1.2)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
}
//right leg stepping forward
if (WalkingTime > 1.2 && WalkingTime < 1.59)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
}
//resetting
if (WalkingTime > 1.6)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
WalkingTime = 0;
}
}
is only executed on the server. So actually it should also be possible already for the Host to see also the movements of other Clients.
However to pass the call back to all clients you should rather add a ClientRpc
[Command]
void CmdWalk()
{
RpcWalk();
}
[ClientRpc]
void RpcWalk()
{
//timer
WalkingTime += Time.deltaTime;
// also it is slightly more efficient to use if-else
// to avoid unneccesary checks since
// only one of those conditions can be true at a time
//right leg stepping forward
if (WalkingTime > 0 && WalkingTime < .4)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
}
//left leg stepping forward
else if (WalkingTime > .4 && WalkingTime < 1.2)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
}
//right leg stepping forward
else if (WalkingTime > 1.2 && WalkingTime < 1.59)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
}
//resetting
else if (WalkingTime > 1.6)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
WalkingTime = 0;
}
}
Update
The problem in general with this approach is still the network lag. The user (if not the Host) won't see the result of his own movements until the call was sent to the server and than back to himself. So I would additionally do the movement already on the local player and skip for him the Rpc call:
void Walk()
{
//timer
WalkingTime += Time.deltaTime;
//right leg stepping forward
if (WalkingTime > 0 && WalkingTime < .4)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * WalkingTime), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * WalkingTime), transform.TransformVector(1, 0, 0));
}
//left leg stepping forward
else if (WalkingTime > .4 && WalkingTime < 1.2)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x + (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x - (60 * (WalkingTime - .8f)), transform.TransformVector(1, 0, 0));
}
//right leg stepping forward
else if (WalkingTime > 1.2 && WalkingTime < 1.59)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(PlayerRightLeg.transform.rotation.x - (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(PlayerLeftLeg.transform.rotation.x + (60 * (WalkingTime - 1.6f)), transform.TransformVector(1, 0, 0));
}
//resetting
else if (WalkingTime > 1.6)
{
PlayerRightLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
PlayerLeftLeg.transform.rotation = Quaternion.AngleAxis(0, Vector3.forward);
WalkingTime = 0;
}
if(!isLocalPlayer) return;
// if executed by the local Player invoke the call on the server
CmdWalk(gameObject);
}
// passing the GameObject reference over network works
// since the player GameObject has a unique identity on all instances
// namely the NetworkIdentity
[Command]
void CmdWalk(GameObject caller)
{
Walk();
RpcWalk(caller);
}
[ClientRpc]
void RpcWalk(GameObject caller)
{
// skip if server since already done it in CmdWalk
if(isServer) return;
// skip if this is caller since already done locally
if(caller == gameObject) return;
Walk();
}
and in Update
use
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
{
Walk();
}
instead