How to sync player position?

Hi all.

Not the greatest with networking, but here goes:

The connection is good. Server can be created, and clients can connect. Player 1 starts server and spawns. Player 2 connects to server and spawns. Player 1 can see player 2 spawn, but cannot see player 2 move at all. Player 2 also cannot see player 1 at all.

So I guess I need to get the NetworkViews to sync their data with the server. The code I have is from the M2H tutorials, but I’m not doing something right, obviously…

@RPC
function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo)
{
	if (stream.isWriting){
		//This is executed on the owner of the networkview
		//The owner sends it's position over the network
		
		var pos : Vector3 = transform.position;		
		stream.Serialize(pos);//"Encode" it, and send it
				
	}else{
		//Executed on all non-owners
		//This client receive a position and set the object to it
		
		var posReceive : Vector3 = Vector3.zero;
		stream.Serialize(posReceive); //"Decode" it and receive it
		
		//We've just recieved the current servers position of this object in 'posReceive'.
		
		transform.position = posReceive;		
		//To reduce laggy movement a bit you could comment the line above and use position lerping below instead:	
		//transform.position = Vector3.Lerp(transform.position, posReceive, 0.9); //"lerp" to the posReceive by 90%
		
	}
}

So how do I get the player positions, rotations, and other info to sync?

C# - use this script, its similar to M2H Leepos script, but a little different :

using UnityEngine;
using System.Collections;

/// <summary>
/// This script is attached to the player and it 
/// ensures that every players position, rotation, and scale,
/// are kept up to date across the network.
/// 
/// This script is closely based on a script written by M2H.
/// </summary>


public class PlayerSync : MonoBehaviour {
	
	private Vector3 lastPosition;
	
	private Quaternion lastRotation;
	
	private Transform myTransform;
	
	// Use this for initialization
	void Start () 
	{
		if(networkView.isMine == true)
		{
			myTransform = transform;
			
			
			//Ensure taht everyone sees the player at the correct location
			//the moment they spawn.
			
			networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
		}
		else
		{
			enabled = false;	
		}
	}
	
	// Update is called once per frame
	void Update () 
	{
		//If the player has moved at all then fire off an RPC to update the players
		//position and rotation across the network.
		
		if(Vector3.Distance(myTransform.position, lastPosition) >= 0.1)
		{
			//Capture the player's position before the RPC is fired off and use this
			//to determine if the player has moved in the if statement above.
			
			lastPosition = myTransform.position;
			networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
		}
		
		if(Quaternion.Angle(myTransform.rotation, lastRotation) >= 1)
		{
			//Capture the player's rotation before the RPC is fired off and use this
			//to determine if the player has turned in the if statement above.	
			
			lastRotation = myTransform.rotation;	
			networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
		}
	}
	
	[RPC]
	void UpdateMovement (Vector3 newPosition, Quaternion newRotation)
	{
		transform.position = newPosition;
		transform.rotation = newRotation;
	}
}

Attach to a player prefab (works on rigidbodies too), then have a network view, observing nothing and not sending any data (leave both fields set to None), then, thats it.

This script updates scale (incase u got power ups), rotation and position

You’re my hero for the month :smile:

Now what about syncing other variables, like health, etc?

I would not recommend using RPC’s for syncing the player position and rotation.

What you do is correct, however it is NOT an RPC so remove the @RPC on top of the method.

When the server is authoritative, there isn’t another way.
Don’t give false advice.

Wrong, it is an RPC call:

networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);

So “networkView.RPC(“function”)” is the function for syncing the variables across the network?

Kinda, it’s more like the ability to call methods (e.g. functions) on the same “instance” of the script on different machines on the network with a number of different parameters.
E.g it can be used to set data on every machine to the servers data as seen above, but it could also be used to start a victory animation on every machine, or enable a rain particle emitter etc.
It is essentially a way of running a function across all machines on a network.
I feel you should be made aware that the RPCMode is what determines what machines receive the call to run the method and how this call is made.
E.g. RPCMode.Server sends it only to the server machine, whilst RPCMode.Others sends it to all machines that aren’t the machine that is making the RPC call.
To see what the difference between buffered and non buffered RPC calls, see the bottom of this page: http://docs.unity3d.com/Documentation/Components/net-RPCDetails.html
Keep in mind that you can remove buffered rpc calls that a machine has sent, e.g. when they disconnect this might be useful.

EDIT for further info:
Typically try to let the server do most of the work; e.g unless health needs to be displayed on client, mess with it on the server and when something dies, then let the client know.
I’m not a networking expert, but if you have a variable that needs to be displayed constantly (like health), but is calculated on the server, you could send it by RPC, but limit the amount of RPC calls per second. For example is it really necessary that 60 updates are made per second to health?, you’d probably get away with 1/2 a second to be cheeky, but something like 1/10 -1/20 would probably be safer.
Would be nice to get a professional opinion on this.
I’m also unsure on any overhead that may occur when using RPC calls, I am presuming it is the same expense of using TCP sockets and streams, which can typically send/recieve data frequently and in decent amounts with no trouble at all.

Thank you, you’ve all been very helpful :smile:

Also, to help myself better understand what the code does, I’ve taken the time to convert DryTear’s code to JavaScript:

#pragma strict

private var lastPosition : Vector3;
private var lastRotation : Quaternion;
private var myTransform : Transform;

function Start () {
	if(networkView.isMine == true){
		myTransform = transform;
		networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
	}
	else{
		enabled = false;
	}
}

function Update () {
	if(Vector3.Distance(myTransform.position, lastPosition) >= 0.1){
		lastPosition = myTransform.position;
		networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
	}
	if(Quaternion.Angle(myTransform.rotation, lastRotation) >= 1){
		lastRotation = myTransform.rotation;
		networkView.RPC("UpdateMovement", RPCMode.OthersBuffered, myTransform.position, myTransform.rotation);
	}
}

@RPC
function UpdateMovement (newPosition : Vector3, newRotation : Quaternion){
	transform.position = newPosition;
	transform.rotation = newRotation;
}

In converting, I’ve found the cause of a small issue I’ve been issue I’ve had. After player 2 spawns, he can’t see player 1 until player 1 moves or turns, because of the conditions in the Update() function. Recommend invoking the RPC call at about the same interval recommended by Glynn Taylor, so that the player doesn’t have to move or rotate to make the RPC.

I’m presuming that you know about network view tracking of transform and animation etc, and are opting to take this route to prevent cheating etc.
Movement may require faster/more updates to make it appear more fluid instead of jumpy; also my main reason for this post is that if you find that your movement is still jumpy regardless of send rate, you will either need to increase the Network.sendrate, or the more “professional”/safer way to do it is to make your characters interpolate/extrapolate/roughly move between updates in the vagueish direction of where they were going.
I only have used these fixes in games using the transform tracking part of network view, and not by RPC calls, it is probable that RPC calls do not have a fixed send rate, and so you wont have this problem; though you may still be able to reduce the amount of information you are sending further, by having larger intervals between RPC calls, and interpolating player position.
Just a few thoughts if you’re concerned about network performance, a large amount of information on optimisation can be found here:Source Multiplayer Networking - Valve Developer Community Though honestly if small scale you probably won’t need to optimise it that much/at all, is just better practice too.

Glad to hear you’re having success; I feel like networking is a very fun topic, just is a bit hard to truly know the best ways of doing things, since no-one seems to be sure ^^

if you’re not comfortable with a C# booger being alone with Java, you can just attach a NetworkView onto your player prefab to send data accross network, so everyone else will recieve the data they want to recieve

for that, use rpc calls to update that accross the network, so that others will have healths updated with rpc’s. Also use RPCMode.AllBuffered, so that everyone, and joining players get info

I was not refering to UpdateMovement, but hes OnSerializedNetwork() method…

As he states that the player starts the server it does not seem much like an authoritative setup.

Server is authoritative period. I just didn’t work it correctly.

So theoretically, this should work:

var smooth : float;

function UpdateMovement (newPosition : Vector3, newRotation : Quaternion){
    transform.position = Mathf.Lerp(transform.position, newPosition, (Time.time * smooth));
    transform.rotation = Mathf.Lerp(transform.rotation, newRotation, (Time.time * smooth));
}

Yes, your right on that one, but you should have specified it clearly.
Other people can also refer to this post and make the same conclusion I made.

TIP: don’t assume things… you can’t see by his posts if it is or not.

To clarify:

@RPC
function OnSerializeNetworkView(stream : BitStream, info : NetworkMessageInfo)
{

OnSerializeNetworkView does not need the to be tagged as an RPC.

Probably not; firstly because the third argument of .Lerp needs to be a value between 0 and 1, so Time.time times by a constant will only work for the first 1/k seconds of the game; see this answer: http://webcache.googleusercontent.com/search?q=cache:GMDt3tQDaXgJ:answers.unity3d.com/questions/237294/how-the-heck-does-mathflerp-work.html+&cd=2&hl=en&ct=clnk&gl=uk
Also I think .Lerp needs to be called constantly to have an effect, rather than just once which is what would happen if that was an rpc call or something. Perhaps a more easier/simpler approach would simply be to add the transform.forward to the transform as that would be the direction of motion from the last update.
I feel like the discussion on this thread surrounding the labelling of RPC in the initial code is unnecessary, seeing as the author has stated that it was tutorial code and has changed his approach since.

As stated then Lerp needs to run over several frames and not a single as it goes from one position to another over time. Cause lets say you send out position and rotation 40 times per second, but the player plays at 60 FPS, then it will seem laggy.

What you can do is saving them as variables in UpdateMovement(), and then use some sort of interpolation in update or fixed update depending on your game.

Furthermore mathf.Lerp is for single floating points, you need some kind of interpolation for Vector3 and Quaternions, like Vector3.MoveTowards, Vector3.Lerp and Quaternion.Lerp.

var smooth : float;
var newPos : Vector3;
var newRot : Quaternion;

function UpdateMovement (newPosition : Vector3, newRotation : Quaternion)
{
    //save variables here
    newPos = newPosition;
    newRot = newRotation
}

function Update()
{
    //Now interpolate to the newest position and rotation
      transform.position = Vector3.Lerp(transform.position, newPos, (Time.deltaTime * smooth));
       transform.rotation = Quaternion.Lerp(transform.rotation, newRot, (Time.deltaTime * smooth));
}