This is going to be a lot of text, scroll down to
I’m trying to place 2 of the same of the same script onto a single object.
PlayerPrefab scripts/behaviours
The script is set up to sync the position/rotation/networktimestamp of the “Transform Sync”. Here I’m using the basic FirstPersonController renamed PlayerPrefab and the camera renamed PlayerCamera. There is also a capsule on it and an object attached to the camera so that I can see where they are and so that I can see the rotation of the PlayerPrefab and the PlayerCamera
PlayerPrefab in the editor
Here’s what it looks like in the inspector. The Capsule and the LocalRotationPositionArms are just meshes with no colliders, no funny business on them.
So the script I wrote should sync the Transform of the PlayerPrefab itself, and also sync the Transform of the PlayerCamera object.
The Problem
It appears only one script, the later script, is being given information. Both scripts are working side by side for the clients connecting to the host. The functionality I tried to implement falls apart.
Code
The code I have looks like this. A single instance of the script appears to work fine for me.
StringNetworkBehaviour
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public abstract class StringNetworkBehaviour : NetworkBehaviour {
[SyncVar (hook = "SyncMessage")]
private string Message = string.Empty;
protected StringSerializable MessageModel { get; set; }
protected virtual void FixedUpdate()
{
this.TransmitMessage ();
}
public virtual bool TransmitCondition()
{
return this.isLocalPlayer;
}
public virtual StringSerializable GetStringSerializable()
{
return this.MessageModel;
}
public virtual void SendNext(StringSerializable messageModel)
{
this.MessageModel = messageModel;
}
public string GetLastRecievedMessage(){
return this.Message;
}
[Command]
protected virtual void Cmd_TrasmitMessageFromClientToServer(string message)
{
this.Message = message;
}
[ClientCallback]
protected virtual void TransmitMessage()
{
if(this.TransmitCondition())
{
StringSerializable message = this.GetStringSerializable();
if(message != null)
{
this.Cmd_TrasmitMessageFromClientToServer(message.SerializeToString());
}
}
}
[Client]
protected virtual void SyncMessage(string message)
{
this.Message = message;
}
}
StringSerializable
using UnityEngine;
using System.Collections;
public interface StringSerializable {
string SerializeToString();
void DeSerializeFromString(string serialized);
}
NetworkSyncPositionRotation
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
[NetworkSettings (channel = 0, sendInterval = 0.1f)]
public class NetworkSyncPositionRotation : StringNetworkBehaviour {
[Tooltip("This transform will be synced. Defaults to the object's tranform.")]
public Transform TransformSync;
[Tooltip("Each time the position and rotation are Lerped, the amount it lerps will be this percentage towards the target.")]
public float LerpRate = 10f;
[Tooltip("If set to true, the local position and rotation will be synced instead of the world position and rotation.")]
public bool UseLocalPositionAndRotation = false;
void Awake()
{
if (this.TransformSync == null) {
this.TransformSync = this.transform;
}
}
void Update ()
{
if (this.isLocalPlayer)
{
int time = NetworkTransport.GetNetworkTimestamp ();
StringSerializable message = null;
if(!this.UseLocalPositionAndRotation)
{
message = new PositionRotationMessageModel (this.TransformSync.position, this.TransformSync.rotation, time);
} else {
message = new PositionRotationMessageModel (this.TransformSync.localPosition, this.TransformSync.localRotation, time);
}
this.SendNext (message);
}
else
{
this.LerpPositionRotation ();
}
}
void LerpPositionRotation()
{
if (!string.IsNullOrEmpty (message))
{
var model = PositionRotationMessageModel.BuildFrom(message);
if(!this.UseLocalPositionAndRotation)
{
this.TransformSync.position = Vector3.Lerp (this.TransformSync.position, model.Position, LerpRate * Time.deltaTime);
this.TransformSync.rotation = Quaternion.Lerp (this.TransformSync.rotation, model.Rotation, LerpRate * Time.deltaTime);
}
else
{
this.TransformSync.localPosition = Vector3.Lerp (this.TransformSync.localPosition, model.Position, LerpRate * Time.deltaTime);
this.TransformSync.localRotation = Quaternion.Lerp (this.TransformSync.localRotation, model.Rotation, LerpRate * Time.deltaTime);
}
}
}
public override bool TransmitCondition()
{
return this.isLocalPlayer;
}
}
PositionRotationMessageModel
using UnityEngine;
using System.Collections;
public class PositionRotationMessageModel : StringSerializable
{
public Vector3 Position {get;set;}
public Quaternion Rotation {get;set;}
public int TimeStamp {get;set;}
public PositionRotationMessageModel()
{
}
public PositionRotationMessageModel(Vector3 pos, Quaternion rot, int timeStamp)
{
this.Position = pos;
this.Rotation = rot;
this.TimeStamp = timeStamp;
}
public static PositionRotationMessageModel BuildFrom(string serialized)
{
var obj = new PositionRotationMessageModel ();
obj.DeSerializeFromString (serialized);
return obj;
}
public string SerializeToString()
{
string message = string.Empty;
message += this.Position.x + " " + this.Position.y + " " + this.Position.z;
message += " ";
message += this.Rotation.x + " " + this.Rotation.y + " " + this.Rotation.z + " " + this.Rotation.w;
message += " ";
message += this.TimeStamp;
return message;
}
public void DeSerializeFromString(string serialized)
{
string[] split = serialized.Split (' ');
this.Position = new Vector3 (float.Parse (split [0]), float.Parse (split [1]), float.Parse (split [2]));
this.Rotation = new Quaternion (float.Parse (split [3]), float.Parse (split [4]), float.Parse (split [5]), float.Parse (split [6]));
this.TimeStamp = int.Parse(split [7]);
}
}
NetworkSetupPlayerControl
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class NetworkSetupPlayerControl : NetworkBehaviour
{
[Tooltip("The camera that this player object controls.")]
public Camera PlayerCamera;
[Tooltip("The audio listener that this player object controls.")]
public AudioListener PlayerAudioListener;
public override void OnStartLocalPlayer ()
{
GameObject.Find("Scene Camera").SetActive(false);
this.PlayerCamera.enabled = true;
this.PlayerAudioListener.enabled = true;
this.GetComponent<UnityStandardAssets.Characters.FirstPerson.FirstPersonController>().enabled = true;
this.transform.position = new Vector3 (0, 3, 0);
}
}
The general idea is that I wanted to make a simple way to send lots of information using a string. The problem is that placing two of my NetworkSyncPositionRotation scripts on the same object seems to break it. I’m concerned that if I extend anything else off of StringNetworkBehaviour that it’ll also break, since I believe it’s breaking at the hook.
My goal was to create a simple to extend way to deal with Unity’s networking so I wouldn’t have to write too much to create my scripts. That’s why all the syncing logic is in StringNetworkBehaviour and my NetworkSyncPositionRotation just builds and serializes a message and sets it to be sent then forgets about it.
Question
Is the behaviour that’s breaking my script intentional? Or am I overlooking something? Can it be fixed?
Notes
The behaviour seems to function perfectly as I intended for the client who is Hosting locally. All the clients can see the host perfectly as he is supposed to be displayed. The host and the clients see all the non hosts as messed up. Not sure why that’s going on.