Hello, I am working in a game that the server shows a 3D model with Vuforia and clients can see the movements server does to the model via networking. The server move the model and clients observe, nothing more.
My question is, how can I manage this situation just the server can create an instance of one prefab and clients just observe this?
Actually server creates an instance of the prefab in the target but when a client is connected many instances are created too, and only the server’s prefab instance is moving on client and server at the same time. This is what I want. Just one instance of the prefab.
As well I have to enable in NetworkIdentity section the LocalPlayerAuthority option or if not clients don’t see nothing. Can’t I use Server only option? Because I need just server can move the model
Keep autoCreatePlayer bool enabled and spawn an empty for every player, including Server. Run this code on emptyPlayer.
public override void OnStartLocalPlayer()
{
if (isServer)
{
GameObject newPlayer = (GameObject)Instantiate(playerObj,
transform.position, transform.rotation);
NetworkServer.ReplacePlayerForConnection(base.connectionToClient, newPlayer, 0);
//This destroys the empty gameObject running this code, freeing up your
//Server's connection to use the new playerObject.
NetworkServer.Destroy(gameObject);
}
base.OnStartLocalPlayer();
}
Thank you for your answer but I am new with Unity and I don’t understand your code. You say that this code associate players with a prefab, right? because prefab is empty. Ok but later destroy a gameObject. Which? and for what? Finally code uses a recursive call every client will use with infinite recursion.
Sorry but I am novice yet, I don’t understand nothing.
And I am using Vuforia, then a method is called every time a target is detected, this is OnTrackingFound() and this method add to the target the prefab if it is not set.
I paste here my code for my instatiator class:
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;
using System.Collections;
using Vuforia;
public class MyPrefabInstantiator : NetworkBehaviour, ITrackableEventHandler {
private TrackableBehaviour mTrackableBehaviour;
public Transform myModelPrefab = null;
private Text debug;
private bool modelCreated = false;
// Use this for initialization
void Start ()
{
mTrackableBehaviour = GetComponent<TrackableBehaviour>();
if (mTrackableBehaviour) {
mTrackableBehaviour.RegisterTrackableEventHandler(this);
}
debug = GameObject.FindObjectOfType<Text> ();
}
// Update is called once per frame
void Update ()
{
}
public void OnTrackableStateChanged(
TrackableBehaviour.Status previousStatus,
TrackableBehaviour.Status newStatus)
{
if (newStatus == TrackableBehaviour.Status.DETECTED ||
newStatus == TrackableBehaviour.Status.TRACKED ||
newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
{
OnTrackingFound();
}
}
private void OnTrackingFound()
{
if (myModelPrefab != null)
{
Transform myModelTrf = GameObject.Instantiate(myModelPrefab) as Transform;
myModelTrf.parent = mTrackableBehaviour.transform;
myModelTrf.localPosition = new Vector3(0f, 0f, 0f);
myModelTrf.localRotation = Quaternion.identity;
//myModelTrf.localScale = new Vector3(0.0005f, 0.0005f, 0.0005f);
myModelTrf.gameObject.active = true;
modelCreated = true;
}else {
myModelPrefab = GameObject.Instantiate(Resources.Load("almacen")) as Transform;
myModelPrefab.parent = mTrackableBehaviour.transform;
}
}
public override void OnStartLocalPlayer()
{
if (isServer)
{
debug.text = "onstartlocalplayer";
GameObject newPlayer = (GameObject)Instantiate(Resources.Load("almacen"),
transform.position, transform.rotation);
NetworkServer.ReplacePlayerForConnection(base.connectionToClient, newPlayer, 0);
//This destroys the empty gameObject running this code, freeing up your
//Server's connection to use the new playerObject.
NetworkServer.Destroy(gameObject);
}
base.OnStartLocalPlayer();
}
}
playerObj is empty, because it was a reference that wasn’t fulfilled. You will have to make a reference to it:
public GameObject playerObj; //Add this to the top of your script where the rest of your references go.
And then you drop in a prefab to the inspector. Make sure the prefab is the player you want and not only does it have a Network Identity script as a component, but it’s also registered within the “Registered Spawnable Prefabs:” array in your NetworkManager.
In the NetworkManager, under Spawn Info, you will see an empty variable called PlayerPrefab. Make a second prefab as an EmptyPlayer. The code on the EmptyPlayer will look like this:
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class EmptyPlayer : NetworkBehaviour {
public GameObject playerObj;
public override void OnStartLocalPlayer()
{
if (isServer)
{
GameObject newPlayer = (GameObject)Instantiate(playerObj,
transform.position, transform.rotation);
NetworkServer.ReplacePlayerForConnection(base.connectionToClient, newPlayer, 0);
NetworkServer.Destroy(gameObject); //This line of code will destroy Server's emptyPlayer after it's new playerObj is Instantiated.
}
base.OnStartLocalPlayer();
}
void Update()
{
if (isServer)
{
return; //This prevents the Server from reading updates while it's still an emptyPlayer.
}
if (isLocalPlayer)
{
//All of your spectators will use code here in-order to move around in the world and watch the Host.
}
}
}
Make sure you attach a Network Identity component to this gameObject in the Inspector as well. Should be good to go.
First I have to put in the Registered Spawnable Prefabs from script, because my app should load a model in runtime, then can’t a default prefab.
But if I do this, client throw an error and no prefab is showing saying next error: Failed to spawn server object. assetid=d12e1d12d12d1d12d1 (invented) netid=2> UnityEngine.Networking.NetworkIdentity:UNetStaticUpdate()
I have to add in the list dragging a prefab, but I need do this in runtime.
This is the code you give me modified:
public override void OnStartLocalPlayer()
{
if (isServer)
{
//empty player for showing nothing in client, just server's player
GameObject newPlayer = (GameObject)Instantiate(playerObj,
transform.position, transform.rotation);
//add to Registered Spawnable list the resource for spawn in client
GameObject resource = GameObject.Instantiate (Resources.Load ("Cube")) as GameObject;
FindObjectOfType<NetworkManager>().spawnPrefabs.Add(resource);
NetworkServer.ReplacePlayerForConnection(base.connectionToClient, newPlayer, 0);
NetworkServer.Destroy(gameObject); //This line of code will destroy Server's emptyPlayer after it's new playerObj is Instantiated.
//spawn this player in client
NetworkServer.Spawn (newPlayer);
}
base.OnStartLocalPlayer();
}
Technically, ReplacePlayerForConnection should spawn a new playerObject for the Host’s connection on all the client’s automatically. Unless you’re overriding functions that I’m not seeing. In any regard, I’m glad you’ve got it worked out for your needs.
Thank you so much, I was looking everywhere for a place to put the isServer check on the server player instance. I was using the start() function but it gets called before the player is fully set up and so isServer was always returning false.