Hi, I am trying to get my head around the UNET multiplayer HLAPI.
What I’m trying to achieve:
- Player1 hosts a server and spawns an
object with a specified name.
- Player2 joins Player1 and spawns an
object with a specified name.
- In the end both host and client both
sees 2 objects with the correct
names.
Currently I host the server using NetworkManager.StartHost();
Once this is called, the scene then changes and I have another script to then spawn the player. I do this by calling CmdSpawnPlayer in the following code:
if (NetworkClient.active) {
CmdSpawnPlayer(gameMan.playerName); // This is the name that the user specifies
}
[Command] //This should only run on the server, which should spawn the player to all clients
void CmdSpawnPlayer(string name){
print ("called spawn with " + name);
playerNames.Add(name);
GameObject player = GameObject.Instantiate (playerPiece);
player.GetComponent<playerCube>().playerName = name;
NetworkServer.Spawn(player);
}
There is another script called playerCube that is on the prefab I am trying to spawn, this script contains a SyncVar attribute that I thought would synchronise the name across the network
Below is the playerCube script which is on the spawned prefab(Sets the parent to canvas, sets the position and the color of the Image component on the gameObject):
[SyncVar]
public string playerName;
// Use this for initialization
void Start () {
gameObject.transform.name = playerName;
gameObject.transform.tag = "playerPiece";
gameObject.transform.parent = GameObject.Find ("Canvas").transform;
gameObject.transform.localPosition = new Vector3(-919.7f,503.1f,0f);
gameObject.GetComponent<Image>().color = Color.green;
}
This currently results in Player1(named “Host”) hosts the server and spawns a gameobject with the correct name. However when a Player2(named “Client”) joins, the CmdSpawnPlayer isn’t called on the server. Leaving the game in the following state:
- Player1 has one spawned object named
“Host”
- Player2 has one spawned object named
“Host”
The diagram below shows the state of the Host and Client instances:
I realise there is a lot of documentation around this topic but despite reading and trying different bits of code I still don’t fully understand, any help on the matter would be greatly appreciated
You can not call commands from non-player object. You will need to create a NetworkManager wrapper which allows the player to pass the data on connecting. Here is my NetworkManager Replacement which you can freely use.
public class NetworkManagerReplace : NetworkManager {
public class NetworkMessage : MessageBase {
public string nickname;
public string GUID;
public MorphGroup[] appearance;
}
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId, NetworkReader extraMessageReader) {
NetworkMessage message = extraMessageReader.ReadMessage< NetworkMessage>();
GameObject player = Instantiate(playerPrefab);
PropertiesManager.manager.LoadPlayer (message.GUID, player, message.appearance);
player.GetComponent<PlayerProperties> ().GUID = message.GUID;
player.GetComponent<PlayerProperties> ().nickname = message.nickname;
if (!player.GetComponent<PlayerProperties> ().properties.newPlayer) {
player.transform.position = player.GetComponent<PlayerProperties> ().properties.position;
player.transform.rotation = player.GetComponent<PlayerProperties> ().properties.rotation;
}
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
PropertiesManager.manager.LoadPlayer2 (message.GUID, player);
}
public override void OnClientConnect(NetworkConnection conn) {
NetworkMessage clientData = new NetworkMessage();
ProfileData profile = GetComponent<CharacterProfiles> ().currentProfile;
clientData.nickname = profile.nickname;
clientData.GUID = profile.GUID;
clientData.appearance = profile.morphs;
ClientScene.AddPlayer(conn, 0, clientData);
}
public override void OnServerRemovePlayer(NetworkConnection conn, PlayerController pcon) {
if (pcon.gameObject.GetComponent<PlayerProperties> () != null)
PropertiesManager.manager.RemovePlayer (pcon.gameObject.GetComponent<PlayerProperties> ());
NetworkServer.Destroy (pcon.gameObject);
}
public override void OnStartServer () {
UIStatics.singleton.adminUI.SetActive (true);
}
public override void OnStopServer () {
UIStatics.singleton.adminUI.SetActive (false);
PropertiesManager.manager.Quit ();
}
public override void OnMatchList (bool success, string extendedInfo, List<UnityEngine.Networking.Match.MatchInfoSnapshot> matchList) {
if (success) {
matches = matchList;
gameObject.SendMessage ("OnMatchesListed");
} else
Debug.LogError (extendedInfo);
}
}
Notice the NetworkMessage. This is the data the player will send after connecting. And OnServerAddPlayer is the actual called function on the server. You will need to remove a lot of code inside the functions since it deeply integrates with what I am doing. Inside OnServerAddPlayer, you will also need to actually spawn the player game object or use something like in the docs.