[UNET] Syncing the position of a non-player object such as a trap (no player authority whatsoever)
I have a game where players are dropped into an arena from a Lobby. Everything is working well, except the traps. There are animated sawblades that slide back and forth in the Z direction. On the host, everything is okay, as is expected. On the clients, their position is shifted (so that they dont even reach the same place as the host's). However, because I am properly using commands, the player still dies appropriately only when they are hit by the sawblades actual position (server/host position).
The issue is, when using a custom script, their position is not synced. When using a Network Transform component, their transform is constantly flickering, however one of the two flickering positions is the actual correct position.
Here is my custom sync position script: using UnityEngine; using System.Collections; using UnityEngine.Networking;
public class CTFSawMove : NetworkBehaviour {
[SyncVar (hook="SyncPositionValues")] private Vector3 syncPos;
private Vector3 lastPos;
private float threshold = 0.2f;
private float lerpRate = 16.0f;
[SerializeField] private Transform myTransform;
public int speed;
public int length;
private float offset;
public int bladeDamage;
public Animator sawAnimation;
public override void OnStartLocalPlayer() {
sawAnimation = myTransform.GetComponent<Animator> ();
}
public override void OnStartClient() {
sawAnimation = myTransform.GetComponent<Animator> ();
}
void Start () {
offset = myTransform.position.z;
syncPos = new Vector3(myTransform.position.x, myTransform.position.y, myTransform.position.z);
}
void Update() {
MoveSaw ();
LerpPosition ();
}
void MoveSaw() {
myTransform.position = new Vector3(transform.position.x, transform.position.y, offset + (Mathf.PingPong(Time.time * speed, length)));
}
void LerpPosition() {
myTransform.position = Vector3.Lerp (myTransform.position, syncPos, Time.deltaTime * lerpRate);
}
void FixedUpdate () {
TransmitPosition ();
}
[Command]
void CmdProvidePositionToServer(Vector3 pos) {
syncPos = pos;
}
[ClientCallback]
void TransmitPosition() {
if( isLocalPlayer && Vector3.Distance(myTransform.position, lastPos) > threshold ) {
CmdProvidePositionToServer(myTransform.position);
lastPos = myTransform.position;
}
}
[Client]
void SyncPositionValues(Vector3 latestPos) {
syncPos = latestPos;
}
void OnTriggerEnter(Collider o){
if (o.transform.tag == "Player" || o.transform.tag == "You") {
CmdTellServerWhoWasHitByBlades (o.transform.name, bladeDamage);
}
}
[Command]
void CmdTellServerWhoWasHitByBlades(string hitName, int dmg) {
GameObject go = GameObject.Find (hitName);
go.GetComponent<CTFPlayerHealth> ().DeductHealth (dmg);
}
}
I've been googling this for quite some time, but it seems there isn't a single person who has ever had trouble with syncing non-player objects. Everyone just has issues with non-player objects that need authority.
Answer by LutzKellen · Apr 20, 2017 at 03:02 AM
@ThinhHB I actually solved this issue some time back. I refactored all of my code to use a more structured Server Authoritative system. Client only ever sends inputs and just runs a local simulation of the server's scene. Along with that I had an issue where if I had movement turned on from the beginning (in the prefab), it would cause desync's in the movement.
So I solved that by turning off the movement script in the prefab, spawning the prefab, and then using an RPC to turn on the movement script for all clients.
Answer by ThinhHB · Apr 19, 2017 at 01:44 PM
I've worked on some multiplayer projects with UNET, and faced the same problem with sync NPC objects states. My solution was (It worked for me already) :
The NPC on server will take the authority (both transform + physics collision)
Server NPC will transmit it's position to clients (include Local clients).
Client replicate the positions they received from Server.
So, the lerping task will execute only on clients, not on Server.
The same with physics collisions, I would disable NPC's collider on clients, so the OnTriggerEnter() will only be called on Server.
Take a look into your code, I see some problems:
The local take the authority on Transform, it transmit it's position to server, but also do the lerping task, I think It cause position flicking on clients.
It look like you let the Local take the physics collision authority too. But, if you not disable the Collider on other places (Server, remote clients), the OnTriggerEnter() will be call in all places, may cause some warning messages. I think it not good ;)
Some more suggestions :
With the simple moving (in your case, the trap only moving in one coordinate, and just slide back and forth), we dont need to sync all of it positions, just need to sync when and where it start sliding, that enought infomation for clients to replicating NPC's state, instead of sending too much positions, in case you have a lot of network bandwidth.
Answer by jeck001 · May 30, 2017 at 01:27 PM
hello @LutzKellen I am facing same problem. Host can easily drag a non player object which is seen from client side also. but when client try to drag that same object then client not able to do it.
public class OnTouchTest : NetworkBehaviour
{
private Transform myTransform;
[SerializeField] float lerpRate = 15;
[SyncVar] private Vector3 syncPos;
[SyncVar]
private GameObject objectID;
private NetworkIdentity objNetId;
private Vector3 lastPos;
private float threshold = 0.5f;
void Start () {
if (isLocalPlayer) {
objectID = GameObject.FindGameObjectWithTag ("cubeObject");
myTransform = objectID.GetComponent<Transform> ();
}
}
void FixedUpdate () {
TransmitPosition ();
LerpPosition ();
}
void LerpPosition () {
if (!isLocalPlayer) {
objectID.transform.position = Vector3.Lerp (objectID.transform.position, syncPos, Time.deltaTime * lerpRate);
}
}
[Command]
void Cmd_ProvidePositionToServer (Vector3 pos) {
syncPos = pos;
}
[ClientCallback]
void TransmitPosition () {
Cmd_ProvidePositionToServer (objectID.transform.position);
lastPos = objectID.transform.position;
}
}
Read below for information I wrote regarding your question before reading your code.
The issue in your code is that you have Cmd_ProvidePositionToServer(objectID.transform.position); inside of a ClientCallback.
ClientCallbacks can only be run on clients, and cannot be run on the server/host. However, ClientCallbacks can only be called from the server/host. Everything inside of that function will run on the clients.
Commands can only be run on the server/host, but can only be called from clients. Everything inside of the Command function will run on the server/host.
With this knowledge you can see that you are only ever calling Cmd_ProvidePositionToServer in a ClientCallback, which means that your Command is only executed after the server/host calls the ClientCallback. You need to call the Command when your local client tries to move the object.
Example of how your current game runs:
The host attempts to drag the object.
The object is moved, the host sends a ClientCallback called TransmitPosition() to update the clients on where the object's new position is.
The client receives the ClientCallback TransmitPosition() and runs the code:
Cmd_ProvidePositionToServer (objectID.transform.position); lastPos = objectID.transform.position;
The client calls the Command Cmd_ProvidePositionToServer()
The server/host receives the Command and says syncPos = pos; This effectively updates the host with information the host already knows.
The client attempts to drag the object but nothing happens (or nothing is sync'd) because no information is ever sent across the network when the client drags the object.
I am assu$$anonymous$$g by "client not able to do it" you mean the client can move the object in their scene, but no one else sees the object has moved?
If that is the case, you are not properly telling the server that you are attempting to move it. If you are unable to move the object, then it is either your code is incorrect (I would check your variables/click listener), or the input you are sending to the host is not being applied on the host.
If the input you are sending is not being applied on the host, then I would double check that you are sending the information, then double check that you are attempting to move the object with the new information.
Your answer
Follow this Question
Related Questions
how to split player objects over network? 0 Answers
local scale network 0 Answers
Error when using NetworkList "Don't know how to serialize List`1 " 0 Answers
How to make my game multiplayer? 1 Answer
UDP Client receiving Disposed error 0 Answers