- Home /
Networking HLAPI Spawning Objects
Hello,
I have been reading through the new networking manual and ran into a few pretty simple questions. http://docs.unity3d.com/Manual/UNetSpawning.html
"there is a registration step that clients have to perform; they must call ClientScene.RegisterPrefab to tell the system about the asset that the client object will be created from"
Lets say I want to call NetworkServer.Spawn(prefabHere); on the server...
Is this the correct function to call?
Do I need to register the prefab?
If so do I use ClientScene.RegisterPrefab or another class/function?
Answer by csisy · Jun 21, 2015 at 03:26 PM
Yes, it is, and as you wrote, you can call it only on the server side.
Only on the client-side as the documentation mentioned it.
That function is what you looking for. :)
If you don't register the prefab on the client-side, it won't be instantiated on the client. And of course, your prefab has to have a NetworkIdentity component.
Salut CSISY!
Thanks for answers, I'm currently seeking for the same wizdom too. Would you $$anonymous$$d adding a few details?
How exactly should I make it possible for clients to spawn objects?
(except putting dozens and hundreds of prefabs into Network$$anonymous$$anager\SpawnInfo\RegisteredSpawnablePrefabs)
1) What should a developer prepare on server-side programmatically?
2) What should a developer prepare on client-side programmatically?
3) What should a developer code to create an object on server and propagate it to all clients?
4) How can a developer set-up a just spawned object parameters (orientation, speed, color) from server to all clients?
Thank you!
Hey,
1) Basically, a client cannot spawn objects (security reasons). It can just ask the server to spawn an object.
As far as I know, you really have to register the spawnable objects. Here is my guess why:
When you have a NetworkIdentity attached to an object and it's a prefab, the NetworkIdentity has a unique assetId
When the server send a Spawn message to the clients, it can't serialize the GameObject itself, ins$$anonymous$$d it serializes the NetworkIdentity (with the assetId mentioned above)
When you register a prefab to the client, it stores the prefab object value with the unique assetId key in a dictionary
When the client receives the Spawn request, it checks this container and if it finds the assetId, it can Instantiate the required object.
2) On client-side, you have to setup the network configuration if you want (timeouts, channels, etc) - note: the network configurations have to match both on server and client side. After you setup your config, you have to connect to the server. When the connection is established, you can register message handlers with the NetworkClient.RegisterHandler function. This is just a basic client, you can expand it as you wish. As a side note, you can use the built-in Network$$anonymous$$anager which does a ton of job for you.
3) Here is a simple function which spawns an object across the network:
void SpawnObject(GameObject prefab)
{
GameObject obj = GameObject.Instantiate(prefab); // server-side copy
NetworkServer.Spawn(obj); // send spawn message to clients
}
In this example, the object's owner will be the server.
4) After the spawn request is sent, you can do an RPC call to setup some things. Or you can use custom messages and call something like this:
NetworkServer.SendToAll(messageId, new $$anonymous$$y$$anonymous$$essage(/*parameters*/);
However, I figured out this is not buffered, so the newly connected clients won't get this message automatically.
The third solution is simply use the [SyncVar]. This attribute can be used to mark a variable to be synced across the network. The spawned objects are owned by the server, so the server can modify the variables which are automatically synced to the clients.
Answer by EndUser · Jun 22, 2015 at 02:32 AM
Salut! My situation is:
void SvrExecGunFire()
{
if (this.isServer)
if (flagSvrGunFirePressed)
{
GameObject gobjProjectile =
(GameObject)GameObject.Instantiate
(
this.GetComponent<ArmourPerfData>().GunMainProjectilePrefab,
this.GunMuzzle.position,
this.GunMuzzle.rotation
);
Vector3 veloMuzzle =
this.GunMuzzle.TransformDirection(0, 10, this.GetComponent<ArmourPerfData>().GunMainMuzzleVeloMps);
gobjProjectile.GetComponent<Rigidbody>().AddForce(veloMuzzle, ForceMode.VelocityChange);
NetworkServer.Spawn(gobjProjectile);
RpcUpdateGunFire();
}
}
In this sample I added a projectile prefab to NetworkManager\SpawnInfo\RegisteredSpawnablePrefabs (which is quite inconvenient). I changed parameters of gobjProjectile and I'm trying propagated this projectile to all clients on the arena.
So the question is: How can I pass parameters to gobj?
Because NetworkServer.Spawn() strips all modifications to the object, and all clients receive a zeroed copy.
And this robbery is contradicting to documentation http://docs.unity3d.com/Manual/UNetSpawning.html which specifies exactly the opposite: "When this code runs, the tree objects created on clients will have the correct value for numLeaves from the server."
// See the comment above
In the example, they use a special attribute called SyncVar. As I mentioned above, it can be used to syncronize data from server to client. So before spawn, you assign the proper values to the syncable variables, and call the spawn. When spawning an object, all of the syncvar are automatically synced, so the object is spawned with the correct up-to-date values.
However, note that the projectiles should be handled on the server-side. So the client should receive only the transform of the projectile.
Here is a playlist with basic UNET tutorials.
First, the answer to my question. Thanks CSISY!
//ArmourGun$$anonymous$$ainFire.cs
void SvrExecGun$$anonymous$$ainFire()
{
if (this.isServer)
//Authoritative server decides about gunfire
{
if (flagSvrGun$$anonymous$$ainFirePressed)
{
if ((timeSvr$$anonymous$$ainGunFireLast + 60.0f / this.GetComponent<ArmourPerfData>().Gun$$anonymous$$ainFireRateRpm) < Time.time)
{
//Create and spawn a projectile
GameObject gobjProjectile = (GameObject)GameObject.Instantiate
(
this.GetComponent<ArmourPerfData>().Gun$$anonymous$$ainProjectilePrefab,
this.Gun$$anonymous$$ain$$anonymous$$uzzle.position,
this.Gun$$anonymous$$ain$$anonymous$$uzzle.rotation
);
//Fill initial data into projectile
gobjProjectile.GetComponent<ProjectileG7077>().message = "hello!";
gobjProjectile.GetComponent<ProjectileG7077>().veloInitial =
this.Gun$$anonymous$$ain$$anonymous$$uzzle.TransformDirection(Vector3.forward) * 10; //Dummy 10 metres/sec
//Command all players to have this projectile
NetworkServer.Spawn(gobjProjectile);
//Nothing important for the shot: visual effects, muzzle flash and light
RpcUpdateGun$$anonymous$$ainEffects();
//Store time of a shot
timeSvr$$anonymous$$ainGunFireLast = Time.time;
}
}
}
}
//ProjectileG7077.cs
using UnityEngine;
using System.Collections;
using UnityEngine.Networking;
[RequireComponent(typeof(Rigidbody))]
[RequireComponent(typeof(NetworkIdentity))]
public class ProjectileG7077 : NetworkBehaviour
{
[SyncVar] //tells all computers to have it synced
public string message; //must be public for programmer to poke
[SyncVar]
public Vector3 veloInitial;
void Start()
{
//Ensure we have variables initialized
ScreenPrinter.Print(message);
//Throw this projectile at given velocity
this.GetComponent<Rigidbody>().AddForce(veloInitial, Force$$anonymous$$ode.VelocityChange);
//Command this projectile to self-destruct in 2 seconds. On all computers.
Destroy(this.gameObject, 2);
if (this.isLocalPlayer)
{
//I never seen this message
ScreenPrinter.Print("I made it!!!");
Debug.Log("I made it!!!");
}
if (this.isServer)
{
//Check who's controlling the situation
ScreenPrinter.Print("I am the boss!!!");
Debug.Log("I am the boss!!!");
}
if (this.isClient)
{
//Who is observing this projectile
ScreenPrinter.Print("I see it");
Debug.Log("I see it");
}
}
void FixedUpdate()
{
if (this.isServer)
{
//Here you can code an authoritative logic about hit calculation with raycast and spherecast, you name it.
}
}
}
Second, for original question.
I currently use Network$$anonymous$$anagerHUD and cannot say how to spawn objects with this.isLocalPlayer == true.
Sorry.
The "isLocalPlayer" will be true if you spawn a Player, not a simple Object. The difference between the two is the ownership for the player is "transferred" to the corresponding client.
I'm sure you have a player prefab registered in the Network$$anonymous$$anager. If you check the isLocalPlayer, it will be true.
On the server-side, you can add a new player by calling
private static GameObject SpawnPlayer(GameObject prefab, NetworkConnection conn, short id)
{
GameObject obj = GameObject.Instantiate(prefab);
if (!NetworkServer.AddPlayerForConnection(conn, obj, id)) // this is the function what you're looking for
{
GameObject.Destroy(obj);
return null;
}
return obj;
}
And you can ask the server to spawn a player for you by calling
ClientScene.AddPlayer(id);