Networking: How to spawn dynamically created objects

Hi all,
Starting my first foray into networking. Been programming a long time, but would like to create a multiplayer game.

So far, I understand the concept of creating prefabs and registering them with the spawn handler. That seems pretty straight forward. However, the game I want to create has a lot of custom objects made on the fly, and I really don’t want to have to create a prefab of each and every one. It would be tedious and a waste of time. So, I’m trying to figure out how to spawn an object created dynamically over the network.

I’ve been playing with code for a few days and am very frustrated with the dead ends and lack of direction.

The current project has an empty GameObject player registered as the spawnable player within the NetworkManager. Attached to each player object is a script: “Player.cs”.

Here’s the code I’m playing with. I simply want to create a cube on the spot, register it, attach it to the player, and spawn it on both the host and the client. It seems like it should be really straight forward but I am simply at my wits end.

Player.cs:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public class Player : NetworkBehaviour {

	GameObject myCube;
	NetworkHash128 spawnNum;

	GameObject playerCube() {
		GameObject temp = GameObject.CreatePrimitive(PrimitiveType.Cube);

		temp.AddComponent<NetworkIdentity>();
		temp.AddComponent<NetworkTransform>();

		temp.transform.position = new Vector3(
				Random.Range (-4, 4), 
				Random.Range (-4, 4), 
				Random.Range (-4, 4)
			);
		temp.transform.localScale = new Vector3(
				Random.Range (1, 3), 
				Random.Range (1, 3), 
				Random.Range (1, 3)
			);
		return temp;
	}

	GameObject onSpawn(Vector3 position, NetworkHash128 hash) {
		return playerCube ();
	}

	void onDespawn(GameObject obj) {
		Destroy (obj);
	}

	void Start () {
		spawnNum = NetworkHash128.Parse ("abcde" + Random.Range (0, 1000000));
		ClientScene.RegisterSpawnHandler (spawnNum, onSpawn, onDespawn);
		Init ();
	}

	[Server]
	void Init() {
		myCube = playerCube ();
		myCube.transform.parent = transform;
		NetworkServer.Spawn (myCube, spawnNum);
	}
}

This will spawn two objects on the host, but the client gets the error of:

Failed to spawn server object, assetId=0000000000abcde32127
UnityEngine.Networking.NetworkIdentity:UNetStaticUpdate()

I know I’m doing something wrong, but am clueless as to what it is. I’ve read dozens of posts, but most of them are specific situations that aren’t my case.
Any help or guidance? Even just a reposting of this code in a form that actually works correctly would greatly help me. I simply need to see the right way to do it because I don’t know what it is.

Thanks.

In order to avoid sending large amounts of data across the network, network spawning works by never actually sending networked objects across the network. This is the reason for having ClientScene.RegisterPrefab and ClientScene.RegisterSpawnHandler. What these functions do is associate an existing object or a function that creates the correct object with a 128 bit asset ID. When an attempt is made to spawn an object, only the asset id (and relevant information like the position) is sent.

There are a couple of crucial components. First, neither Register function sends anything across the network. In order for dynamic spawning to work, you have to call register on both the client and the server, with the same NetworkHash128.

So in your example, you do this:

spawnNum = NetworkHash128.Parse ("abcde" + Random.Range (0, 1000000));

since this happens in Start, this does happen on both the client and the server. However, the client and the server both generate a different random Network Hash to register the spawn handler functions under.

The general use case for this would be loading from external configuration rather than prefabs. In that case, you would load the configuration on both the client and server and then spawn using the server object.

If you want to spawn random generated objects on both client and server which cannot be loaded from elsewhere, you will need to serialize the relevant information about the spawn yourself, and then you can have the server make a ClientRPC with the serialized object. The client would then deserialize and register the appropriate spawn handler, which would allow you to spawn it. Note that this would still be an issue with new clients joining midgame, as they would not have the appropriate spawn handlers if they had not received the earlier ClientRPCs.