Draw network components and methods necessary for raycasting with the mouse for 2 player interaction with argos-sphere.
remember:
NetworkTransform
The NetworkTransform component synchronizes movement of game objects across the network. This component takes authority into account, so LocalPlayer objects (which have local authority) synchronize their position from the client to server, then out to other clients. Other objects (with server authority) synchronize their position from the server to clients.
To use this component, add it to the prefab or game object that you want to synchronize movement for. The component requires that the game objects has a NetworkIdentity. Note that networked objects must be spawned to synchronize.
Properties
Property: | Function: |
---|---|
characterContoller Cached | CharacterController. |
clientMoveCallback2D | A callback that can be used to validate on the server, the movement of client authoritative objects. |
clientMoveCallback3D | A callback that can be used to validate on the server, the movement of client authoritative objects. |
grounded | Tells the NetworkTransform that it is on a surface (this is the default). |
interpolateMovement | Enables interpolation of the synchronized movement. |
interpolateRotation | Enables interpolation of the synchronized rotation. |
lastSyncTime | The most recent time when a movement synchronization packet arrived for this object. |
movementTheshold | The distance that an object can move without sending a movement synchronization update. |
rigidbody2D | Cached Rigidbody2D. |
rigidbody3D | Cached Rigidbody. |
rotationSyncCompression | How much to compress rotation sync updates. |
sendInterval | The sendInterval controls how often state updates are sent for this object. |
snapThreshold | If an update puts an object further from its current position than value, it will snap to the position |
syncRotationAxis | Which axis should rotation by synchronized for. |
targetSyncPosition | The target position interpolating towards. |
targetSyncRotation2D | The target rotation interpolating towards. |
targetSyncRotation3D | The target position interpolating towards. |
targetSyncVelocity | The velocity send for synchronization. |
transformSyncMode | What method to use to sync the object’s position. |
- There is a NetworkSendRate slider on the inspector of the NetworkTransform. For objects that do not need to update after being created – such as bullet, set this slider to zero.
- The NetworkTransformVisualizer will help NetworkTransform be debugged.
Player Objects
In the HLAPI of the network system players are special kinds of objects. They represent the player on the server and so have the ability to run commands (which are secure client-to-server remote procedure calls) from the player’s client.
In this server authoritative system, other non-player server side objects do not have the capability to receive commands directly from objects on clients. This is both for security and to reduce the complexity of working in a distributed environment. Routing all incoming commands from users through the player object ensures that these messages came from the right place, the right client, and can be handling in a central location.
When using the NetworkManager, a player is added by default when a client connects to the server. In some situations though, adding players should be deferred until some input event happens, so this behaviour can be turned off with the AutoCreatePlayer checkbox on the NetworkManager.
When a player is added, the NetworkManager will instantiate an object from the PlayerPrefab and associate it with the connection. This is actually done by the NetworkManager calling NetworkServer.AddPlayerForConnection.
This behaviour can be modified by overriding NetworkManager.OnServerAddPlayer. The default implementation of OnServerAddPlayer instantiates a new player instance from the PlayerPrefab and calls NetworkServer.AddPlayerForConnection to spawn the new player instance.
A custom implementation of OnServerAddPlayer must also call NetworkServer.AddPlayerForConnection, but is free to perform any other initialization it requires. The example below customizes the color of a player:
class Player : NetworkBehaviour { [SyncVar] public Color color; } class MyManager : NetworkManager { public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId) { GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.Zero, Quaternion.Identity); player.GetComponent<Player>().color = Color.Red; NetworkServer.AddPlayerForConnection(conn, player, playerControllerId); } }
The function NetworkServer.AddPlayerForConnection does not have to be called from within OnServerAddPlayer. As long as the correct connection object and playerControllerId are passed in, it can be called after OnServerAddPlayer has returned. This allows asynchronous steps to happen in between, such as loading player data from a remote data source.
The HLAPI treats players and clients as separate objects. In most cases there is a single player for each client. But, in some situations – such as when there are multiple controllers connected to a console system, they could be multiple player objects for a single connection.
When there are multiple players for a connection, the playerControllerId property is used to tell them apart. This is an identifier that is scoped to the connection – so that it literally maps to the id of the controller associated with the player on that client.
The player object passed to NetworkServer.AddPlayerForConnection on the server is automatically spawned by the system, so there is no need to call NetworkServer.Spawn for the player. Once a player is ready, the active NetworkIdentity objects in the scene will be spawned on the player’s client. So all networked objects in the game will be created on that client with their latest state, so they are in sync with the other participants of the game.
The playerPrefab on the NetworkManager does not have to be used to create player objects. You could use different methods of creating different players.
The function AddPlayerForConnection does not have to be called from within OnServerAddPlayer. It could be called asynchronously, such as when a request to another service like a database returns information on what kind of player to created.
Ready State
In addition to players, client connections also have a “ready” state. A client that is ready is sent spawned objects and state synchronization updates; a client that is not ready, is not sent these updates. When a client initially connects to a server it is not ready. While in this state, the client is able to do things that don’t require real-time interactions with the server simulation, such as load scenes, choose avatars or fill out login boxes. Once a client has all their pre-game work done, and all their assets loaded, they can call ClientScene.Ready to enter the ready state. The simple example above also works, as adding a player with NetworkServer.AddPlayerForConnection also puts the client into the ready state if it is not already in that state.
Clients can send and receive network messages without being ready, which also means without having an active player. So a client at a menu or selection screen can be connected to the game and interact with it, even though they have no player object. There is a section later in this document about sending messages without using commands and RPC calls.
Switching Players
The player object for a connection can be replaced with NetworkServer.ReplacePlayerForConnection. This can be useful to restrict the commands that can be issued by players at certain times, such as in a pre-game lobby screen. This function takes the same arguments as AddPlayerForConnection, but allows there to already be a player for that connection. The old player object does not have to be destroyed. The NetworkLobbyManager uses this technique to switch from the LobbyPlayer to a game-play player when all the players in the lobby are ready.
This can also be used to respawn a player after their object is destroyed. In some cases it is better to just disable an object and reset its game attributes on respawn, but to actually replace it with a new object you could use code like:
class GameManager { public void PlayerWasKilled(Player player) { var conn = oldPlayer.connectionToClient; var newPlayer = Instantiate<GameObject>(playerPrefab); Destroy(oldPlayer.gameObject); NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0); } }
If the player object for a connection is destroyed, then that client will be unable to execute Commands. They can however still send network messages.
To use ReplacePlayerForConnection you must have the NetworkConnection object for the player’s client to establish the relationship between the object and the client. This is usually the property connectionToClient on the NetworkBehaviour class, but if the old player has already been destroyed, then that may not be readily available.
To find the connection, there are some lists available. If using the NetworkLobbyManager, then the lobby players are available in lobbySlots. Also, the NetworkServer has lists of connections and localConnections .
- Synchronized Variables
- Network callbacks
- Server and Client functions
- Sending Commands
- Client RPC Calls
- Networked Events
NetworkBehaviour
NetworkBehaviours are special scripts that work with objects with the NetworkIdentity component. These scripts are able to perform HLAPI functions such as Commands, ClientRPCs, SyncEvents and SyncVars.
With the server authoritative system of the Unity Network System, networked objects with NetworkIdentities must be “spawned” by the server using NetworkServer.Spawn(). This causes them to be assigned a NetworkInstanceId and be created on clients that are connected to the server.
Properties
Property: | Function: | |
---|---|---|
isLocalPlayer | True if this object is the player object for the local client. | |
isServer | True if this object is running on the server, and has been spawned. | |
isClient | True if this object is running on a client. | |
hasAuthority | True if this object is the authoritative version of the object. So either on the server, or on the client with localPlayerAuthority. | |
assetId | This is the assetId of the object’s NetworkIdentity. | |
netId | This is the netId of the object’s NetworkIdentity. | |
playerControllerId | This is the playerControllerId of the object’s NetworkIdentity. | |
connectionToServer | NetworkConnection object to use to send to the server. | |
connectionToClient | NetworkConnection object to use to send to the client. |
Synchronized Variables
Member variables of NetworkBehaviours can be synchronized from the server to clients. Since the server is authoritative in this system,
synchronization is only in the server-to-client direction.
Requests for clients to do things are handled by Commands, not by variables synchronized from clients.
The SyncVar attribute is used to tag member variables as being synchronized. SyncVars can be any basic type, not classes, lists, or other collections.
public class ArgosObj : NetworkBehaviour { [SyncVar] public int health; [SyncVar] public string userName; }
When the value of a SyncVar is changed on the server, it will be sent to all of the ready clients in the game. When objects are spawned, they are created on the client with the latest state of all SyncVars from the server.
Network callbacks
There are callback functions that are invoked on NetworkBehaviour script for various network events. These are virtual functions on the base class, so they can be overridden in use code like this:
public class ArogsObj : NetworkBehaviour { public override void OnStartServer() { // disable client stuff } public override void OnStartClient() { // register client events, enable effects } }
The OnStartServer function is called when an object is spawned on the server, or when the server is started for objects in the scene.
The OnStartClient function is called when the object is spawned on the client, or when the client connects to a server for objects in the scene.
These functions are useful to do things that are specific to either the client or server, such as suppressing effects on a server, or setting up client-side events.
Note that when a local client is being used, both of these functions will be called on the same object.
Other callbacks include:
- OnSerialize – called to gather state to send from the server to clients
- OnDeSerialize – called to apply state to objects on clients
- OnNetworkDestroy – called on clients when server told the object to be destroyed
- OnStartLocalPlayer – called on clients for player objects for the local client (only)
- OnRebuildObservers – called on the server when the set of observers for an object is rebuilt
- OnSetLocalVisibility – called on a host when the visibility of an object changes for the local client
- OnCheckObserver – called on the server to check visibility state for a new client
i.e.
using UnityEngine; using UnityEngine.Networking; public class EnemySpawner : NetworkBehaviour { public GameObject enemyPrefab; public int numEnemies; public override void OnStartServer() { for (int i = 0; i < numEnemies; i++) { var pos = new Vector3( Random.Range(-8.0f, 8.0f), 0.2f, Random.Range(-8.0f, 8.0f) ); var rotation = Quaternion.Euler(Random.Range(0, 180), Random.Range(0, 180), Random.Range(0, 180)); var enemy = (GameObject)Instantiate(enemyPrefab, pos, rotation); NetworkServer.Spawn(enemy); } } }
Server and Client functions
Member functions in NetworkBehaviours can be tagged with custom attributes to designate them as server-only or client-only functions. For example:
using UnityEngine; using UnityEngine.Networking; public class SimpleArgosObj : NetworkBehaviour { int expertise; [Server] public void IncExpertise( int amount) { // will only work on server expertise += amount; } [Client] void ShowPrecision() { // will only run on client } [ClientCallback] void Update() { // engine invoked callback - will only run on client } }
These attributes make the function return immediately if they are called when the client or server is not active. They do not generate compile time errors, but they will emit a warning log message if called in the wrong scope. The attributes ServerCallback and ClientCallback can be used for engine callback functions that user code does not control the calling of. These attributes do not cause a warning to be generated.
Sending Commands
Commands are the way for clients to request to do something on the server. Since the HLAPI is a server authoritative system, clients can ONLY do things through commands.
Commands are run on the player object on the server that corresponds to the client that sent the command. This routing happens automatically, so it is impossible for a client to send a command for a different player.
Commands must begin with the prefix “Cmd” and have the [Command] custom attribute on them, like below:
using UnityEngine; using UnityEngine.Networking; public class ArgosObj : NetworkBehaviour { bool bDrawing; float markVal; int spin; [Command] public void CmdMark(float markVal, int spin) { if (!bDrawing) { this.markVal= 0; this.spin = 0; return; } this.markVal = markVal; this.spin = spin; } [ClientCallback] void Update() { int spin = 0; if (Input.GetKey(KeyCode.LeftArrow)) { spin += 1; } if (Input.GetKey(KeyCode.RightArrow)) { spin -= 1; } // this will be called on the server CmdMark(Input.GetAxis("Vertical"), spin); } }
Commands are called just by invoking the function normally on the client. But instead of the command function running on the client, it will be invoked on the player object of that client on the server. So, commands are typesafe, have built-in security and routing to the player, and use an efficient serialization mechanism for the arguments to make calling them fast.
Client RPC Calls
Client RPC calls are a way for server objects to cause things to happen on client objects. This is the reverse direction to how commands send messages, but the concepts are the same. Client RPC calls however are not only invoked on player objects, they can be invoked on any NetworkIdentity object. They must begin with the prefix “Rpc” and have the [ClientRPC] custom attribute, like below:
using UnityEngine; using UnityEngine.Networking; public class ArgosRpc : NetworkBehaviour { [ClientRpc] public void RpcDoOnClient(int foo) { Debug.Log("OnClient " + foo); } [ServerCallback] void Update() { int value = UnityEngine.Random.Range(0,100); if (value < 10) { // this will be invoked on all clients RpcDoOnClient(value); } } }
Networked Events
Networked events are like Client RPC calls, but instead of just calling a function on the client object, an Event on the client object is triggered. Other scripts that are registered for the event are then invoked – with the arguments from the server, so this allows networked inter-script interactions on the client. Events must start with the prefix “Event” and have the SyncEvent custom attribute.
Events can be used to build powerful networked game systems that can be extended by other scripts. This example shows how an effect script on the client can respond to events generated by a combat script on the server.
using UnityEngine; using UnityEngine.Networking; // Server script public class MyArgosSession : NetworkBehaviour { public delegate void TakeDamageDelegate(int side, int damage); public delegate void DieDelegate(); public delegate void RespawnDelegate(); float deathTimer; bool alive; int health; [SyncEvent(channel=1)] public event TakeDamageDelegate EventTakeDamage; [SyncEvent] public event DieDelegate EventDie; [SyncEvent] public event RespawnDelegate EventRespawn; [Server] void TakeDamage(int amount) { if (!alive) return; if (health > amount) { health -= amount; } else { health = 0; alive = false; // send die event to all clients EventDie(); deathTimer = Time.time + 5.0f; } } [ServerCallback] void Update() { if (!alive) { if (Time.time > deathTimer) { Respawn(); } return; } } [Server] void Respawn() { alive = true; // send respawn event to all clients EventRespawn(); } }
Trad RPC
-
Client marshals arguments to form a message. The first part of the message is a function opcode.
-
Client sends message via the kernel to the server and blocks for the result.
-
Server dispatches request according to the opcode stored in the message.
-
Server unmarshalls function arguments,
-
Server executes the function,
-
Server marshals the produced results.
-
Server sends result message to the client.
-
Client unmarshals results.