Updated Spawn Object doc and replace image

This commit is contained in:
Chris Langsenkamp 2019-07-15 09:19:35 -04:00
parent a2d6cf23f8
commit 122e7e79b6
3 changed files with 65 additions and 44 deletions

View File

@ -4,66 +4,64 @@ In Mirror, you usually “spawn” (that is, create) new game objects with `Inst
Once the game object is spawned using this system, state updates are sent to clients whenever the game object changes on the server. When Mirror destroys the game object on the server, it also destroys it on the clients. The server manages spawned game objects alongside all other networked game objects, so that if another client joins the game later, the server can spawn the game objects on that client. These spawned game objects have a unique network instance ID called “netId” that is the same on the server and clients for each game object. The unique network instance ID is used to route messages set across the network to game objects, and to identify game objects. Once the game object is spawned using this system, state updates are sent to clients whenever the game object changes on the server. When Mirror destroys the game object on the server, it also destroys it on the clients. The server manages spawned game objects alongside all other networked game objects, so that if another client joins the game later, the server can spawn the game objects on that client. These spawned game objects have a unique network instance ID called “netId” that is the same on the server and clients for each game object. The unique network instance ID is used to route messages set across the network to game objects, and to identify game objects.
When the server spawns a game object with a Network Identity component, the game object spawned on the client has the same “state”. This means it is identical to the game object on the server; it has the same Transform, movement state, and (if NetworkTransform and SyncVars are used) synchronized variables. Therefore, client game objects are always up-to-date when Mirror creates them. This avoids issues such as game objects spawning at the wrong initial location, then reappearing at their correct position when a state update arrives. When the server spawns a game object with a Network Identity component, the game object spawned on the client has the same “state”. This means it is identical to the game object on the server; it has the same Transform, movement state, and (if Network Transform and SyncVars are used) synchronized variables. Therefore, client game objects are always up-to-date when Mirror creates them. This avoids issues such as game objects spawning at the wrong initial location, then reappearing at their correct position when a state update arrives.
The Network Manager before trying to register it with the Network Manager. The Network Manager before trying to register it with the Network Manager.
To register a Prefab with the Network Manager in the Editor, select the Network Manager game object, and in the Inspector, navigate to the Network Manager component. Click the triangle next to Spawn Info to open the settings, then under Registered Spawnable Prefabs, click the plus (+) button. Drag and drop Prefabs into the empty field to assign them to the list. To register a Prefab with the Network Manager in the Editor, select the Network Manager game object, and in the Inspector, navigate to the Network Manager component. Click the triangle next to Spawn Info to open the settings, then under Registered Spawnable Prefabs, click the plus (+) button. Drag and drop Prefabs into the empty field to assign them to the list.
![The Network Manager Inspector with the Spawn Info foldout expanded, displaying three assigned Prefabs under Registered Spawnable Prefabs](UNetManagerSpawnInfo.png) ![Registered Spawnable Prefabs](SpawnObjects.PNG)
## Spawning Without Network Manager ## Spawning Without Network Manager
For more advanced users, you may find that you want to register Prefabs and spawn game objects without using the NetworkManager component. For more advanced users, you may find that you want to register Prefabs and spawn game objects without using the Network Manager component.
To spawn game objects without using the Network Manager, you can handle the Prefab registration yourself via script. Use the ClientScene.RegisterPrefab method to register Prefabs to the Network Manager. To spawn game objects without using the Network Manager, you can handle the Prefab registration yourself via script. Use the `ClientScene.RegisterPrefab` method to register Prefabs to the Network Manager.
### Example: MyNetworkManager ``` cs
```cs
using UnityEngine; using UnityEngine;
using Mirror; using Mirror;
public class MyNetworkManager : MonoBehaviour public class MyNetworkManager : MonoBehaviour
{ {
public GameObject treePrefab; public GameObject treePrefab;
NetworkClient myClient;
// Create a client and connect to the server port // Register prefab and connect to the server
public void ClientConnect() { public void ClientConnect()
{
ClientScene.RegisterPrefab(treePrefab); ClientScene.RegisterPrefab(treePrefab);
myClient = new NetworkClient(); NetworkClient.RegisterHandler<ConnectMessage>(OnClientConnect);
myClient.RegisterHandler(MsgType.Connect, OnClientConnect); NetworkClient.Connect("localhost");
myClient.Connect("127.0.0.1", 4444);
} }
void OnClientConnect(NetworkMessage msg) { void OnClientConnect(NetworkConnection conn, ConnectMessage msg)
Debug.Log("Connected to server: " + msg.conn); {
Debug.Log("Connected to server: " + conn);
} }
} }
``` ```
In this example, you create an empty game object to act as the Network Manager, then create and attach the `MyNetworkManager` script (above) to that game object. Create a Prefab that has a Network Identity component attached to it, and drag that onto the treePrefab slot on the MyNetworkManager component in the Inspector. This ensures that when the server spawns the tree game object, it also creates the same kind of game object on the clients. In this example, you create an empty game object to act as the Network Manager, then create and attach the `MyNetworkManager` script (above) to that game object. Create a prefab that has a Network Identity component attached to it, and drag that onto the `treePrefab` slot on the `MyNetworkManager` component in the Inspector. This ensures that when the server spawns the tree game object, it also creates the same kind of game object on the clients.
Registering Prefabs ensures that the Asset, so that there is no stalling or loading time for creating the Asset. Registering prefabs ensures that there is no stalling or loading time for creating the Asset.
However, for the script to work, you also need to add code for the server. Add this to the MyNetworkManager script: For the script to work, you also need to add code for the server. Add this to the `MyNetworkManager` script:
```cs ``` cs
public void ServerListen() public void ServerListen()
{ {
NetworkServer.RegisterHandler(MsgType.Connect, OnServerConnect); NetworkServer.RegisterHandler<ConnectMessage>(OnServerConnect);
NetworkServer.RegisterHandler(MsgType.Ready, OnClientReady); NetworkServer.RegisterHandler<ReadyMessage>(OnClientReady);
if (NetworkServer.Listen(4444)) if (NetworkServer.Listen(7777))
Debug.Log("Server started listening on port 4444"); Debug.Log("Server started listening on port 7777");
} }
// When client is ready spawn a few trees // When client is ready spawn a few trees
void OnClientReady(NetworkMessage msg) void OnClientReady(NetworkConnection conn, ReadyMessage msg)
{ {
Debug.Log("Client is ready to start: " + msg.conn); Debug.Log("Client is ready to start: " + conn);
NetworkServer.SetClientReady(msg.conn); NetworkServer.SetClientReady(conn);
SpawnTrees(); SpawnTrees();
} }
@ -72,14 +70,14 @@ void SpawnTrees()
int x = 0; int x = 0;
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)
{ {
var treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity);
NetworkServer.Spawn(treeGo); NetworkServer.Spawn(treeGo);
} }
} }
void OnServerConnect(NetworkMessage msg) void OnServerConnect(NetworkConnection conn, ConnectMessage msg)
{ {
Debug.Log("New client connected: " + msg.conn); Debug.Log("New client connected: " + conn);
} }
``` ```
@ -87,11 +85,11 @@ The server does not need to register anything, as it knows what game object is b
When writing your own network manager, its important to make the client ready to receive state updates before calling the spawn command on the server, otherwise they wont be sent. If youre using Mirrors built-in Network Manager component, this happens automatically. When writing your own network manager, its important to make the client ready to receive state updates before calling the spawn command on the server, otherwise they wont be sent. If youre using Mirrors built-in Network Manager component, this happens automatically.
For more advanced uses, such as object pools or dynamically created Assets, you can use the ClientScene.RegisterSpawnHandler method, which allows callback functions to be registered for client-side spawning. See documentation on Custom Spawn Functions for an example of this. For more advanced uses, such as object pools or dynamically created Assets, you can use the `ClientScene.RegisterSpawnHandler` method, which allows callback functions to be registered for client-side spawning. See documentation on Custom Spawn Functions for an example of this.
If the game object has a network state like synchronized variables, then that state is synchronized with the spawn message. In the following example, this script is attached to the tree Prefab: If the game object has a network state like synchronized variables, then that state is synchronized with the spawn message. In the following example, this script is attached to the tree Prefab:
```cs ``` cs
using UnityEngine; using UnityEngine;
using Mirror; using Mirror;
@ -109,13 +107,13 @@ class Tree : NetworkBehaviour
With this script attached, you can change the `numLeaves` variable and modify the `SpawnTrees` function to see it accurately reflected on the client: With this script attached, you can change the `numLeaves` variable and modify the `SpawnTrees` function to see it accurately reflected on the client:
```cs ``` cs
void SpawnTrees() void SpawnTrees()
{ {
int x = 0; int x = 0;
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)
{ {
gameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity);
Tree tree = treeGo.GetComponent<Tree>(); Tree tree = treeGo.GetComponent<Tree>();
tree.numLeaves = Random.Range(10,200); tree.numLeaves = Random.Range(10,200);
Debug.Log("Spawning leaf with leaf count " + tree.numLeaves); Debug.Log("Spawning leaf with leaf count " + tree.numLeaves);
@ -129,6 +127,7 @@ Attach the `Tree` script to the `treePrefab` script created earlier to see this
### Constraints ### Constraints
- A NetworkIdentity must be on the root game object of a spawnable Prefab. Without this, the Network Manager cant register the Prefab. - A NetworkIdentity must be on the root game object of a spawnable Prefab. Without this, the Network Manager cant register the Prefab.
- NetworkBehaviour scripts must be on the same game object as the NetworkIdentity, not on child game objects - NetworkBehaviour scripts must be on the same game object as the NetworkIdentity, not on child game objects
## Game Object Creation Flow ## Game Object Creation Flow
@ -136,18 +135,31 @@ Attach the `Tree` script to the `treePrefab` script created earlier to see this
The actual flow of internal operations that takes place for spawning game objects is: The actual flow of internal operations that takes place for spawning game objects is:
- Prefab with Network Identity component is registered as spawnable. - Prefab with Network Identity component is registered as spawnable.
- game object is instantiated from the Prefab on the server. - game object is instantiated from the Prefab on the server.
- Game code sets initial values on the instance (note that 3D physics forces applied here do not take effect immediately). - Game code sets initial values on the instance (note that 3D physics forces applied here do not take effect immediately).
- `NetworkServer.Spawn` is called with the instance. - `NetworkServer.Spawn` is called with the instance.
- The state of the SyncVars on the instance on the server are collected by calling `OnSerialize` on [Network Behaviour] components. - The state of the SyncVars on the instance on the server are collected by calling `OnSerialize` on [Network Behaviour] components.
- A network message of type `MsgType.ObjectSpawn` is sent to connected clients that includes the SyncVar data.
- A network message of type `ObjectSpawn` is sent to connected clients that includes the SyncVar data.
- `OnStartServer` is called on the instance on the server, and `isServer` is set to `true` - `OnStartServer` is called on the instance on the server, and `isServer` is set to `true`
- Clients receive the `ObjectSpawn` message and create a new instance from the registered Prefab. - Clients receive the `ObjectSpawn` message and create a new instance from the registered Prefab.
- The SyncVar data is applied to the new instance on the client by calling OnDeserialize on Network Behaviour components. - The SyncVar data is applied to the new instance on the client by calling OnDeserialize on Network Behaviour components.
- `OnStartClient` is called on the instance on each client, and `isClient` is set to `true` - `OnStartClient` is called on the instance on each client, and `isClient` is set to `true`
- As gameplay progresses, changes to SyncVar values are automatically synchronized to clients. This continues until game ends.
- As game play progresses, changes to SyncVar values are automatically synchronized to clients. This continues until game ends.
- `NetworkServer.Destroy` is called on the instance on the server. - `NetworkServer.Destroy` is called on the instance on the server.
- A network message of type `MsgType.ObjectDestroy` is sent to clients.
- A network message of type `ObjectDestroy` is sent to clients.
- `OnNetworkDestroy` is called on the instance on clients, then the instance is destroyed. - `OnNetworkDestroy` is called on the instance on clients, then the instance is destroyed.
### Player Game Objects ### Player Game Objects
@ -155,14 +167,23 @@ The actual flow of internal operations that takes place for spawning game object
Player game objects in the HLAPI work slightly differently to non-player game objects. The flow for spawning player game objects with the Network Manager is: Player game objects in the HLAPI work slightly differently to non-player game objects. The flow for spawning player game objects with the Network Manager is:
- Prefab with `NetworkIdentity` is registered as the `PlayerPrefab` - Prefab with `NetworkIdentity` is registered as the `PlayerPrefab`
- Client connects to the server - Client connects to the server
- Client calls `AddPlayer`, network message of type `MsgType.AddPlayer` is sent to the server - Client calls `AddPlayer`, network message of type `MsgType.AddPlayer` is sent to the server
- Server receives message and calls `NetworkManager.OnServerAddPlayer` - Server receives message and calls `NetworkManager.OnServerAddPlayer`
- game object is instantiated from the PlayerPrefab on the server
- game object is instantiated from the Player Prefab on the server
- `NetworkManager.AddPlayerForConnection` is called with the new player instance on the server - `NetworkManager.AddPlayerForConnection` is called with the new player instance on the server
- The player instance is spawned - you do not have to call `NetworkServer.Spawn` for the player instance. The spawn message is sent to all clients like on a normal spawn. - The player instance is spawned - you do not have to call `NetworkServer.Spawn` for the player instance. The spawn message is sent to all clients like on a normal spawn.
- A network message of type `MsgType.Owner` is sent to the client that added the player (only that client!)
- A network message of type `Owner` is sent to the client that added the player (only that client!)
- The original client receives the network message - The original client receives the network message
- `OnStartLocalPlayer` is called on the player instance on the original client, and `isLocalPlayer` is set to true - `OnStartLocalPlayer` is called on the player instance on the original client, and `isLocalPlayer` is set to true
Note that `OnStartLocalPlayer` is called after `OnStartClient`, because it only happens when the ownership message arrives from the server after the player game object is spawned, so `isLocalPlayer` is not set in `OnStartClient`. Note that `OnStartLocalPlayer` is called after `OnStartClient`, because it only happens when the ownership message arrives from the server after the player game object is spawned, so `isLocalPlayer` is not set in `OnStartClient`.
@ -171,7 +192,7 @@ Because `OnStartLocalPlayer` is only called for the clients local player game
## Spawning Game Objects with Client Authority ## Spawning Game Objects with Client Authority
To spawn game objects and assign authority of those game objects to a particular client, use NetworkServer.SpawnWithClientAuthority, which takes as an argument the `NetworkConnection` of the client that is to be made the authority. To spawn game objects and assign authority of those game objects to a particular client, use `NetworkServer.SpawnWithClientAuthority`, which takes as an argument the `NetworkConnection` of the client that is to be made the authority.
For these game objects, the property `hasAuthority` is true on the client with authority, and `OnStartAuthority` is called on the client with authority. That client can issue commands for that game object. On other clients (and on the host), `hasAuthority` is false. For these game objects, the property `hasAuthority` is true on the client with authority, and `OnStartAuthority` is called on the client with authority. That client can issue commands for that game object. On other clients (and on the host), `hasAuthority` is false.
@ -179,14 +200,14 @@ Objects spawned with client authority must have `LocalPlayerAuthority` set in th
For example, the tree spawn example above can be modified to allow the tree to have client authority like this (note that we now need to pass in a NetworkConnection game object for the owning clients connection): For example, the tree spawn example above can be modified to allow the tree to have client authority like this (note that we now need to pass in a NetworkConnection game object for the owning clients connection):
```cs ``` cs
void SpawnTrees(NetworkConnection conn) void SpawnTrees(NetworkConnection conn)
{ {
int x = 0; int x = 0;
for (int i = 0; i < 5; ++i) for (int i = 0; i < 5; ++i)
{ {
var treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity); GameObject treeGo = Instantiate(treePrefab, new Vector3(x++, 0, 0), Quaternion.identity);
var tree = treeGo.GetComponent<Tree>(); Tree tree = treeGo.GetComponent<Tree>();
tree.numLeaves = Random.Range(10,200); tree.numLeaves = Random.Range(10,200);
Debug.Log("Spawning leaf with leaf count " + tree.numLeaves); Debug.Log("Spawning leaf with leaf count " + tree.numLeaves);
NetworkServer.SpawnWithClientAuthority(treeGo, conn); NetworkServer.SpawnWithClientAuthority(treeGo, conn);
@ -196,7 +217,7 @@ void SpawnTrees(NetworkConnection conn)
The Tree script can now be modified to send a command to the server: The Tree script can now be modified to send a command to the server:
```cs ``` cs
public override void OnStartAuthority() public override void OnStartAuthority()
{ {
CmdMessageFromTree("Tree with " + numLeaves + " reporting in"); CmdMessageFromTree("Tree with " + numLeaves + " reporting in");

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB