Search Unity

Spawn a player and set their name

Discussion in 'Multiplayer' started by peterept, Jun 30, 2015.

  1. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    Hi, Sorry all I'm stuck on something really basic!

    If I have a player prefab with their name on it via this component:

    public class PlayerInfo : NetworkBehaviour {
    [SyncVar]
    public string Name;
    }


    When a player is spawned on the server, how do I get the name from the client?? ANd to ensure it is synced prior to the players prefabs being instantiated on the remote clients.

    class MyManager : NetworkManager {
    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId) {
    GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.Zero, Quaternion.Identity);
    player.GetComponent<PlayerInfo>().Name = ?????? how does server know this ????;
    NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
    }
    }


    TIA!
     
  2. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    Here's how I currently have it, but it doesn't feel completely correct.

    public class PlayerInfo : NetworkBehaviour {
    [SyncVar(hook="OnSyncMyNameHook")]
    public string MyName;

    void OnSyncMyNameHook(string value) {
    // make sure we save the value. On remove clients if we have a hook the value isn't set for us!
    MyName = value;
    GetComponentInChildren<TextMesh>().text = value;
    }

    [Command]
    voidCmdSetMyName(string name) {
    MyName = name;
    }

    void Start () {
    // because SyncVar hook is not fired on startup only for future changes?
    OnSyncMyNameHook(MyName);
    }

    public override void OnStartLocalPlayer () {
    CmdSetMyName("Mary Or Whatever");
    }

    So this works with the following caveats from what I can see:

    1. When a player prefab is instantiated for a remote client, I do not get the OnSyncMyNameHook() fired, but the name has consistently been synced prior to Start() firing, so I re-use my hook to get the 3d text to display:
    Q: Is that guaranteed? Will syncvar values from remote client always be valid prior to Start calling? What about on a slow or unreliable network?

    2. When on the local client, then Start() the name is invalid but I do get OnSyncMyNameHook() fired once it is set.
     
    Last edited: Jun 30, 2015
    jonablo007 likes this.
  3. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    More playing with 1 above I can see it is not guaranteed. Sometimes it is not set in Start. And immeadiately after I get the OnSyncMyNameHook() fired with the value.

    On top of that, on the remote client I see the OnSyncMyNameHook() called before the Start() method sometimes. so need to watch that also by making sure the hook sets the syncvar value in the hook.

    So, is the best practise that you should send command (or use a syncvar) to setup your players prefab before you display it? Obviously it is instantiated already, but it could just be not visible (or empty) until it gets that setup completed? eg; [syncvar] bool isReady ?

    It would be more helpful if when calling OnSyncMyNameHook() we could pass params that are guranteed to be setup before Start() is called ?
     
    Last edited: Jun 30, 2015
  4. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    To summarize, I see all these scenarios during instantiation of the player prefab by server onto a client:

    1. SyncVar has already been set, No Hook fired. Variable all good in Start()
    2. SyncVar has not been set. Start() shows blank. Immeadiately get a Hook fired afterwards with the value.
    3. SyncVar has not been set. I get a Hook fired _before_ Start() is called. Then Start() is called.
    (This scenario I find troubling b/c I don't want to start accessing my gameobject/components before Start() is called)

    Is scenario 3 a bug maybe?
     
  5. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    use OnStartClient() instead of Start()
     
  6. peterept2

    peterept2

    Joined:
    Aug 1, 2012
    Posts:
    41
    Thanks, I'll try that. However, the docs say OnStartClient() is "Called when the client connects to a server." and I am already connected to the server and then later I add the player.
     
  7. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    OnStartClient on NetworkBehaviour, not on NetworkManager
     
  8. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    Right, when I override it I see it get called. But in a few tests it is getting called before Start() and before the SyncVar is valid.

    Here's one flow: I'm on a client (not host), and another client spawns a player, I get this:

    Players Prefab instantiated:
    Awake() Name=""
    OnStartClient() Name=""
    Hook() value = “Players Name”
    Start() Name = "" (so name is not set, unless I save it in my Hook() event)

    Note: Different flow if we connect to the server _after_ the player is already created:

    Awake() Name=""
    OnStartClient() Name="Players Name"
    Start() value = “Players Name”

    Wouldn't it make sense to not call the hook if the Start() method isn't called yet.

    This is doing my head in!
     
  9. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    The docs here say:

    The state of SyncVars is applied to objects on clients before OnStartClient() is called, so the state of the object is guaranteed to be up-to-date inside OnStartClient().

    And so I guess because i set the players name in OnStartLocalPlayer() via a Command, then it is technically correct that the remote clients see a player instantiated that doesn't have the name set yet. However, it is frustrating that the players name then pops in via the hook event _before_ Start() is called.
     
  10. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    OK, I have figured out a solution that works for me:

    Goal: How to instantiate a new player with their name.

    Original Approach above: Client calls ClientScene.AddPlayer(), which then instantiates the player prefab, which I have OnStartLocalPlayer() call a server Command to set the name in a syncvar MyName.
    Original Problem: So the above works, but depending on whether you are a remote client, local client, etc the name may or may not be set prior to the StartClient(), or Start() method, and you may or may not get the syncvar hook event, and if you get the hook event you may get it before Start() where I have some initialization.

    New Method: To avoid the timing issues, instead of using the ClientScene.AddPlayer() built-in call, I register my own network message that can be sent to the server with all the parameters (eg: players name), and then get the server to instatiate and link it to the player.

    Code (CSharp):
    1. public class MyNetworkManager : NetworkManager {
    2.  
    3.     public class MyAddPlayerMessage : MessageBase
    4.     {
    5.         public static short MSG_TYPE = 2000;
    6.         public string name = "";
    7.         public short playerControllerId = 0;
    8.     }
    9.  
    10.     public override void OnStartServer ()
    11.     {
    12.         base.OnStartServer();
    13.         NetworkServer.RegisterHandler(MyAddPlayerMessage.MSG_TYPE, OnServerMyAddPlayerMessage);
    14.     }
    15.  
    16.     public override void OnStopServer ()
    17.     {
    18.         base.OnStopServer ();
    19.         NetworkServer.UnregisterHandler(MyAddPlayerMessage.MSG_TYPE);
    20.     }
    21.  
    22.  
    23.     public static void MyAddPlayer(string name, short playerControllerId)
    24.     {
    25.         singleton.client.Send(MyAddPlayerMessage.MSG_TYPE, new MyAddPlayerMessage() { name = name, playerControllerId = playerControllerId });
    26.     }
    27.  
    28.     private void OnServerMyAddPlayerMessage(NetworkMessage netMsg)
    29.     {
    30.         MyAddPlayerMessage msg = netMsg.ReadMessage<MyAddPlayerMessage>();
    31.         GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
    32.         player.GetComponent<PlayerSetup>().MyName = msg.name;
    33.         NetworkServer.AddPlayerForConnection(netMsg.conn, player, msg.playerControllerId);
    34.  
    35.     }
    36.  
    37. }
    Hope that helps someone else.

    Note: The hook still may get called before Start() though! Geez. But I'm scrapping the hook b/c my players can't change names without re-spawning.
     
    Last edited: Jul 1, 2015
    StarKRE97 and emotitron like this.
  11. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    for networked objects, dont use Start() at all. Look a the Object Creation Flow section of the "Object Spawning" page of the manual.
     
  12. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    It is amazing that this stuff isn't better explained in the documentation. The sample projects are horrible and all over the place.

    Forgive me for my venting/frustration, but my goodness this is nothing like the rest of Unity's features. It was a lot easier to understand and implement Unity's multiplayer before 5.1. (What little I did, anyway).

    I've read the entire multiplayer documentation four times now. Very slowly and thoroughly, trying to grasp networking from very little networking experience (although I have some understanding of networking and some experience dealing with servers).

    Googling for Unet is almost non-existent right now since it's so new, so we're left with just the documentation and examples. May god have mercy on our souls...

    I've spent the last 6 hours and 39 minutes trying to figure out how to add a player and customize them via a simple button choice (instantiate a different Sprite and parent it under the playerPrefab's "Graphic" child).

    How do you give a client's spawned object localAuthority without making it another instance of a player or without replacing the playerPrefab?

    I have so many questions, and absolutely no idea how to engineer a system to do all this. I find myself trying all kinds of different strategies, clearly demonstrating I am not understanding some of the basics.
    How do I get access to the Client's connection, from a script only Clients have? I am having trouble just figuring out how to get the information needed to fill parameters for various functions.

    What's the difference between OnStartClient() in NetworkManager and NetworkBehaviour?
    What the HELL is ClientScene? (ex. ClientScene.AddPlayer() )

    Where are the more detailed examples of the excerpts in the documentation? (ex. setting a tree's leaves or character's color.)

    Do clients/games have a persistent/unique identifier besides IP address (which would be idiotic to rely on for character persistent on a server)? I assume we have to create this ourselves, but sending Messages/Commands needs more examples or better explanation. (Trying to register a new player character so the server can save it via some identifier is not the simplest thing).

    This should be so simple or so easily explained in the docs, but where are the examples for this simple stuff? (I know I am close. Literally if I had an example or two, this would have taken a few minutes rather than endless hours only to delete everything and start over.)

    Even worse are all the bugs or at least quirks. (NetworkProximityChecker not even working by giving an error in a new empty project is a great example of how NOT ready this beta Unet is. I dislike how early Unity releases its major updates. It's not ready yet, especially with no (good) example project).

    Anyway, I'll start again tomorrow to try to figure all of this out- but I wanted to post my grievance somewhere. I mean, seriously...6 hours and 40 minutes later and I actually have LESS than I did earlier. At least the auto-spawn of a good playerprefab works easily right out of the box. Doing anything else is met with lots of frustration.

    Doesn't help that I have to go over the documentation repeatedly just to understand a general idea. This is very uncommon for me. I usually can skim documentation and understand it entirely. I rarely have trouble like this, especially when it comes to programming. I wanted to emphasize how incomplete the documentation is.
     
    Last edited: Jul 1, 2015
  13. Necromantic

    Necromantic

    Joined:
    Feb 11, 2013
    Posts:
    116
    Yes, the documentation is not really that fleshed out yet.

    The thing is that the new networking solution is a brand new thing and not many people are working with networking. It's also only the first phase. So there isn't all that much to find and all of us have to establish our own best practices for the time being.

    NetworkManager has some default behaviours already built-in while for NetworkBehaviour it's just a Callback as far as I can tell.

    ClientScene is the client-side equivalent of NetworkServer which handles establishment of collections, registration of NetworkMessages and some other basic connection stuff.

    I have a simple game where you can customize your color in the menu and it will be propagated as your color over the network.
    I don't use NetworkManager and have built my own implementation of the basic connection logic.
    I've simply created my own NetworkMessage type and I send that in OnClientConnect which is m Callback for when the Client connects.

    Clients have an ID but it's not a persistent UUID.

    You can simply do something like:
    Code (CSharp):
    1. private class ExtMsgType : MsgType {
    2.     public const short PlayerID = 64;
    3. }
    Register the MessageType so it can be received.
    Code (CSharp):
    1. NetworkServer.RegisterHandler(ExtMsgType.PlayerID, OnServerPlayerIDReceived);
    For registering it on the Client use ClientScene instead of NetworkServer.

    Somewhere use something like:
    Code (CSharp):
    1. client.Send(ExtMsgType.PlayerID, new IntegerMessage(playerID));
    Where client is a NetworkClient with an established connection.

    And to receive it:
    Code (CSharp):
    1. public void OnServerPlayerIDReceived(NetworkMessage msg) {
    2.     IntegerMessage message = msg.ReadMessage<IntegerMessage>();
    3.     playerID = message.value;
    4. }
    The same thing can be done with a StringMessage for names.

    Yes, it's buggy and stuff, but I like getting things as early as possible to play around with and nobody is forcing you to use it, the legacy stuff is still around and working..

    It's a dynamic release cycle, things change and the UNET documentation is a perfect example of how sometimes some outdated information gets left on live systems.
     
    Last edited: Jul 1, 2015
    CarterG81 likes this.
  14. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    I have to agree with you @CarterG81. It is very difficult to work out once you get passed the functionality of the basic quick start autospawn a player prefab. The timing of events is not consistent making it difficult.

    Why is StartClient() even needed? UNet should be consistent and have everything done prior to Start() unless they need to inform of something specific.

    So, both you and @Necromantic used the same approach as me in the end - send a custom message from the client to the server?

    If that is the case, then *please* I wish Unity would add an optional NetworkMessage to ClientScene.AddPlayer() :

    Code (CSharp//):
    1. Proposed API improvement - Add optional NetworkMessage
    2.  
    3. // ClientScene:
    4. public static bool AddPlayer(Networking.NetworkConnection readyConn, short playerControllerId, NetworkMessage msg);
    5.  
    6. // NetworkManager:
    7. public void OnServerAddPlayer(Networking.NetworkConnection conn, short playerControllerId, NetworkMessage msg);
    I don't want to be overlay negative, the UNet looks very promising and very exciting architecture. But it is hard to get your head around. Well, back to more explorations today.
     
    CarterG81 and Vanamerax like this.
  15. Necromantic

    Necromantic

    Joined:
    Feb 11, 2013
    Posts:
    116
    It is a utility method solely used in the NetworkManager for establishing a connection and maybe doing some initialization dependent on that. Aside from being a method that can be extended it really doesn't have all that much in common with the Start callback.

    I think once people stop using the NetworkManager and actually build their own implementation they will learn to understand those things better.
     
  16. peterept

    peterept

    Joined:
    Dec 6, 2012
    Posts:
    22
    That is definitely true, but right now all the demos and HLAPI talk steers developers to start with NetworkManager...
     
  17. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    Thank you very much. All of that helps immensely.

    I didn't realize exactly how "New" this is. Last time I "came back to Unity" there were quite a lot of changes. The biggest and most latest one being the new 2D system. Perhaps it wasn't as new as I thought, because when I came around it was really easy to understand and much better documented. The experience was wonderful- nothing like this :p

    Maybe I can do some good and write up a crappy tutorial for others. I don't really know what I'm doing, but at least I can figure it out. Especially with the help of this wonderful community. Then maybe help others do the same- or at least provide a crappy (but working) example for others to see.
     
  18. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    15 hours total trying to figure just this one thing out (how to add a player and customize a single variable).

    Unfortunately, I can't even get this far to even try the solutions presented in this thread. I am failing to spawn the characters on both the server and client.

    I'm not really sure what to do. I usually resolve any problem in a few minutes to a 4-6 hours max.
    15 hours just trying to do a simple thing? I am clearly not getting this. Without assistance, I'm pretty sure I will just spend another 15 doing the same things over and over- failing again and again.

    Maybe I should pay someone to tutor me? Maybe I should just forget multiplayer until much later in the project? I am getting pretty desperate.

    The worst part is that all the different ways I'm trying seem to be working great in the UnityEditor. However anytime I build, it is nothing like the results hitting "Play" in the UnityEditor. I finally got it to not freeze up (no idea why it did this when I sent a command) but only the server sees the player and the variable does not sync as it should because I'm not handling my playerPrefab correctly (all players are spawning only on the server, and take the host client's information to determine its sprite/color/name/whatever. Although that is a bit more clear on why that is happeing, but I haven't even tried to touch fixing that since I can't even get spawning the players right. What's the point in customization if they dont even spawn?).

    Any links to tutorials or examples or assistance in this thread would be very much appreciated.
     
  19. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    I got it to work. I took a break from the 16 hour 2-day marathon of learning and just relaxed for the day. Came back to it with a notepad & pen, and went through the documentation slowly- reading everything for the Nth time.

    Their example in the documentation is total crap, but what I finally understood despite how horribly it is explained and how incomplete it is. As the OP stated, the server doesn't know the player's color. To rectify this, I made my own solution.

    We start with our own custom NetworkManager. I call mine "GameNetworkManager" which inherits from "NetworkManager". Be careful - you only want ONE NetworkManager, so replace your NetworkManager with GameNetworkManager.

    In our own GameNetworkManager, we customize OnClientConnect. What I do is create a "Client" gameobject which stores a lot of relevant information. (It also handles multiple GUI's for 4-player splitscreen and I use it for some global variables too.)

    First thing I do is create the "Client" gameobject, set its name to not have (Clone) in it so it is correctly named "Client". Remember to set it on Don'tDestroyOnLoad, because after this is instantiated, it will load the multiplayer Scene. This seemed to be a requirement.

    Irrelevant Details (Skip): (You could also have this or something similar in the multiplayer Scene, but I advice against it. Let's just instantiate it when it's needed rather than have it in the scene when loaded. This "Client" gameobject is also destroyed in OnStopClient. I also have an object called "Server" which does the same thing, but in OnStartServer and OnStopServer.)

    After this, I determined if it was a NewGame or not. So I then instantiate the CharacterSelectionGUI so the user can choose their character in-game. THIS IS NOT REQUIRED. If you don't have persistent characters on the server, just skip the conditional (IF) statement.

    Finally, I call base.OnStartClient(client) because I have no idea what that does, and just wanted to be safe in case I missed something.


    Code (csharp):
    1.  
    2.  
    3. public class GameNetworkManager : NetworkManager
    4. {
    5.  
    6. public GameObject ClientObject; //The prefab with a script that holds your player's choice/customization variables.
    7. public GameObject CharacterSelectGUI; //The prefab CANVAS with all the GUI. Remember you also need an EventSystem for UnityEngine.UI stuff to work.
    8.  
    9. public override void OnStartClient(NetworkClient client)
    10.   {
    11.   Debug.Log("Starting Client...");
    12.  
    13.   thisClientObject = (GameObject)Instantiate(ClientObject, Vector3.zero, Quaternion.identity);
    14.   thisClientObject.name = ClientObject.name; //Could also be thisClientObject.name = "Client";
    15.   DontDestroyOnLoad(thisClientObject);
    16.  
    17.   if (isNewGame)
    18.   {
    19.   if (!GameObject.Find(CharacterSelectGUI.name)) //Create the Character Select GUI to make a new Character.
    20.   {
    21.   thisCharacterSelectGUI = (GameObject)Instantiate(CharacterSelectGUI, Vector3.zero, Quaternion.identity);
    22.   thisCharacterSelectGUI.name = CharacterSelectGUI.name;
    23.   DontDestroyOnLoad(thisCharacterSelectGUI);
    24.   }
    25.   }
    26.   else
    27.   {
    28.     //Request Load player from Server
    29.   }
    30.  
    31.   base.OnStartClient(client);
    32.   Debug.Log("I am Client.");
    33.   }
    34.  
    35. }
    36.  
    This is the important bit:

    Code (csharp):
    1.  
    2. public GameObject ClientObject; //The prefab with a script that holds your player's choice/customization variables.
    3.  
    4. public override void OnStartClient(NetworkClient client)
    5.   {
    6.   thisClientObject = (GameObject)Instantiate(ClientObject, Vector3.zero, Quaternion.identity);
    7.   thisClientObject.name = ClientObject.name; //Could also be thisClientObject.name = "Client";
    8.   DontDestroyOnLoad(thisClientObject);
    9.  


    Once OnStartClient is called, the player is now connected and NOT ready. Being "Not Ready" means no network data being sent yet across the network. If you want the network to start the gameworld/send data from everything on the server with NetworkIdentity, then set the client to Ready.


    Now that the CharacterSelectGUI is created, we have some buttons to click to choose a character. I have two buttons: "Character1" button, and "Character2" button. Both have UI Button "OnClick()" events set to this script: CharacterSelectMenu .cs, calling this function: SelectCharacterButton(string name).

    Code (csharp):
    1.  
    2. public class CharacterSelectMenu : NetworkBehaviour
    3. {
    4.  
    5. public void SelectCharacterButton(string name)
    6.    {
    7.      LoadCharacterData(name); //Load all the PlayerData for that character. (ex. Name, Stats, Special Starter Items, etc.)
    8.  
    9.      //SpawnPlayer on Server - WARNING - This may not be correct.
    10.   ClientScene.AddPlayer(0); //Sends the message to the server asking to spawn playerobject for this client.
    11.      //Maybe requires ClientScene.AddPlayer(ClientScene.conn, 0);
    12.  
    13.   //Delete this GUI
    14.   Destroy(this.gameObject);
    15.   }
    16.  
    17.   void LoadCharacterData(string name)
    18.   {
    19.   GameObject multiplayerClient = GameObject.Find("Client");
    20.   multiplayerClient.GetComponent<MultiplayerClient>().myPlayerData.characterName = name;
    21.  
    22.   //MultiplayerClient.playerData.BackPack
    23.   //MultiplayerClient.playerData.CurrentHealth
    24.   //MultiplayerClient.playerData.CurrentHunger
    25.   //MultiplayerClient.playerData.CurrentThirst
    26.   //MultiplayerClient.playerData.Equipment
    27.   //MultiplayerClient.playerData.inMouseHand
    28.   //MultiplayerClient.playerData.myPosition
    29.   }
    30. }
    31.  

    Click button "Character1" will call SelectCharacterButton("Carter")
    because that is the string I put in the button's parameter (in the inspector - "Carter").

    What this does is finds the "Client" gameobject we instantiated in our first call to OnStartClient(). This "Client" gameobject has a script attached to it which holds a string. Personally, I call my "Client" script "MultiplayerClient" and have a class called "PlayerData" which holds all possible playercharacter information (health, hunger, thirst, items they have, their name, etc.) THIS IS NOT REQUIRED, but I wanted to keep it in to show you how I do more complex character loading. (PlayerData is serialized, so it can be Saved/Loaded to file as well as sent across the network in whole).

    The final call to Destroy(this.gameObject) is to get rid of the CharacterSelectGUI. The user clicked the button to choose their character, so we are ready to start the game. If you don't want it to start yet, then don't call ClientScene.AddPlayer().

    Remember- ClientScene.AddPlayer() sends a message from the Client to the Server. The server then runs OnServerAddPlayer().

    Remember - NetworkServer.AddPlayerForConnection() will put the client in a Ready state.


    But you could do something as simple as this:

    Code (csharp):
    1.  
    2. public class myClient() : Monobehaviour
    3. {
    4.     public string characterName;
    5. }
    6.  
    7. public class CharacterSelectMenu : NetworkBehaviour
    8. {
    9. public void SelectCharacterButton(string name)
    10.    {
    11.        GameObject myClientGOB = GameObject.Find("Client"); //Find the "Client" GameObject we instantiated earlier in OnStartClient().
    12.        myClientGOB.GetComponent<myClient>().characterName = name; //Set the string (characterName) to what the player chose (name).
    13.  
    14.      ClientScene.AddPlayer(0); //Sends the message to the server asking to spawn playerobject for this client.
    15.   }
    16. }
    17.  


    Now, on the playerPrefab we instantiate for players, we need to have a script to ready the character as well as send [Commands].

    Remember- Only the playerPrefab can send [Commands] (localPlayer authority and all that), and it needs a NetworkIdentity.

    Code (csharp):
    1.  
    2. public class PlayerNetworkSetup : NetworkBehaviour
    3. {
    4. GameObject multiplayerClient;
    5.  
    6.   [SyncVar]
    7.   public string myCharacterName;
    8.  
    9.   bool doneOnce = false;
    10.  
    11.   void LateUpdate()
    12.   {
    13.   if (!doneOnce)
    14.   {
    15.     //This instantiates the player character graphic based on the user's character choice, then parents it to the graphic child of the playerObject. This is my own way.
    16.   GameObject playerGOB = (GameObject)Instantiate(Resources.Load("Prefabs/Player/" + myCharacterName));
    17.   playerGOB.transform.SetParent(graphic.transform);
    18.   doneOnce = true;
    19.   }
    20.   }
    21.  
    22.  
    23.   public override void OnStartLocalPlayer()
    24.   {
    25.   base.OnStartLocalPlayer();
    26.  
    27.   if(isLocalPlayer) //Is not the player's character.
    28.   {
    29.   //this.GetComponent<PlayerInputMovement>().enabled = true;
    30.   //playerCamera.SetActive(true);
    31.  
    32.   GameObject myClient = GameObject.Find("Client");
    33.   Cmd_SetCharacterName(myClient.GetComponent<MultiplayerClient>().myPlayerData.characterName);
    34.   }
    35.    }
    36.  
    37.   [Command]
    38.   void Cmd_SetCharacterName(string name)
    39.   {
    40.   myCharacterName = name;
    41.   }
    42.  
    43.  
    Since the "Client" gameobject is only available to the localplayer (not part of the network), we need to make sure that only localPlayers sync their name.

    This class is attached to the playerPrefab. "myCharacterName" is a [SyncVar] so it syncs across the server.

    OnStartLocalPlayer is called first. It finds that "Client" gameobject and then sends a command to the server.

    Remember - Commands are sent by the Client, TO the Server. Then the Server runs the code.

    The [Command] Cmd_SetCharacterName(string name) sets the [SyncVar]'d variable to the string chosen by the player.

    The very last thing called is LateUpdate(), which runs a single time, doing the customization data. It could be as simple as this:

    Code (csharp):
    1.  
    2.   void LateUpdate()
    3.   {
    4.   if (!doneOnce)
    5.   {
    6.   if(myCharacterName == "Carter")
    7. {
    8. GetComponent<SpriteRenderer>().color = Color.red; //whatever
    9. }
    10. else
    11. {
    12. //leave it at the default blue. You aren't a Carter!
    13. }
    14.   doneOnce = true;
    15.   }
    16.   }
    17.  

    The only problem with this is that my server instantiates all the characters great, but the Clients only see themselves. I assume this has to deal with ClientScene.AddPlayer(0) or something I'm not calling in OnServerAddPlayer(). But the syncing of variables to customize the player's color/name/whatever should all be working in this example.
     
    Last edited: Jul 3, 2015
    ArkVoid and Darhkuan like this.
  20. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    I am in a hurry and dont' have much time, so I tried to just bold the important parts.

    Sorry for not making a better example. This isn't perfect (it syncs the variable, but I'm not spawning the player correctly) so I didn't want to waste too much time on this.


    TLDR: Just use a non-network gameobject ("Client") to hold the variable, and when the playerPrefab is spawned, grab that non-network gameobject ("Client") and send a [Command] to do whatever.
     
  21. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    Another benefit to having it in LateUpdate() is that it will repeatedly try to perform the customization (spawn graphic, change sprite, color sprite, whatever) until it is Sync'd. My game apparently does not fully Sync the characterName for several iterations of LateUpdate(). Since it returns null when trying to Instantiate a gameobject with the name "" (default for characterName before it syncs), "doneOnce" is never set to true. Once it finally Syncs the name to be "Carter", it then successfully instantiates "Prefabs/Player/Carter" and then sets the doneOnce to true, ending the update loop.
     
  22. Oniromancer

    Oniromancer

    Joined:
    Aug 2, 2013
    Posts:
    6
    Man, CarterG81, I feel you. I spent too many hours trying to solve the most basic stuff (reference to the local instance of player in the client) , and Im sure that my solution is actually a hack. I appreciate Unity's effort to update the Networking system, but I really feel they completely failed to do this right. I mean, before deprecating the old system, they should have polished the new system, specially in terms of documentation. Sure, you can have your basic FPS network out of the box, but anything past that is a pain in the neck, and solutions have to be based on trial-error testing, which for multiplayer is particularly exhausting (again, documentation and web searches give very poor results). Whats worst is that the choice of a networking back end is more commitment than marriage, so at the point I realised how painful uNet can be, it was already late to change my choice (I mostly do turn based games, and there doesn't seem to be any standard alternative that can deploy to mobile... if anyone has a suggestion, please let me know). So for now, Im stuck with it, just hoping that, since it's Unity's next bet, it slowly will improve ( I insist, maybe the system is great, but that means nothing if people cant get to understand it and use it. Maybe they don't need to change code, but provide with WORKING examples that show it's potential out side the basic one player, one controller scheme). In any case, I needed to blow some steam, and if anyone out there is about to start a multiplayer project, and is wondering if uNet is a reliable solution, for no I would say that it definitely isn't: if you have any alternative, go for it.
     
    CarterG81 and Chom1czek like this.
  23. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    This is an understatement which applies to the entire (gamedev) internet.

    I can't count the amount of clutter-ware on the internet, especially related to gamedev. So many "libraries" and the like which are so horribly undocumented that their very existence is perplexing. Especially when you consistently read responses like, "It was easier to just create my own version." or "I didn't get it, so I just did the same thing from scratch."

    Bleh. If only there was a quality-focused website that curated all this content. That's a lot of work though.
     
    Last edited: Nov 8, 2015
    RoyalDio2 and dben41 like this.
  24. s26812743

    s26812743

    Joined:
    Jan 23, 2017
    Posts:
    3
    Hey carter, i know I'm like a year and a half late and I hope you haven't given up on Unity developing! I was just wondering if you still have this multiplayer project and maybe you can post it on Github or something so I can check it out, I'm trying desperately to make a similar project work.
     
  25. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    I honestly dont even remember this post, or anything about UNET except that it was horrible (at least at the time). I abandoned UNET after 60 hours of development after finding out it (at the time) was literally broken.

    I use Forge Networking now, and have ever since a bit after that post. It has its bugs and later its bug fixes as it matures, like all of them do (even my mortal enemy, UNET). But of every networking package, it was the only one that "Just works". It just released a Remastered version I believe. (I've been in beta the entire time.)

    I notice that alot of people struggle, like I did, with multiplayer networking.

    I am wary though to write any tutorials for it, as I want to make sure everything works as it should in all setups (Local, Lan, online, routers, etc.) I'd hate to write something that ended up having some hidden issue.

    In my view, the part we all struggle with is engineering the code. So if I can write something to teach how to make it work in Forge, for those who cant afford Forge it should be very similar logic in UNET. For those who can afford it, I'll just say it: Screw UNET. It blows hard. It's closed source. Forge is awesome & full source included.



    Anyway...what is it you are trying to do? What type of game?
     
    Last edited: Jan 25, 2017
    path14 likes this.
  26. s26812743

    s26812743

    Joined:
    Jan 23, 2017
    Posts:
    3
    Basically I'm trying to recreate a game called Dead Realm (essentially a hide and seek) for a school project. All I need help with is spawning in the players at the start of the game:

    For each round start, I want one player in the lobby to be randomly selected as the monster (the seeker), and the rest are humans (hiders). I want the monster to spawn in one place, and all the humans to spawn in another at the start of each round.

    It seems simple, but I dont know how to spawn in different prefabs (monster or human), nor control where they are spawned. Maybe you can help? I'm totally willing to switch to forge networking but I also need to be able to create a lobby.


    If you can help me in any way it'd be greatly appreciated.
     
  27. CarterG81

    CarterG81

    Joined:
    Jul 25, 2013
    Posts:
    1,773
    You're best off making your own thread about this. That way more people can help (quicker solution) and you could potentially find a solution using UNET.

    This sounds pretty easy to do in Forge (and thus probably UNET too) but I am pretty busy these days, so posting your own thread could get you better answers, much faster. The community here is very helpful.
     
  28. msabharwal

    msabharwal

    Joined:
    Apr 5, 2017
    Posts:
    4
    Hello, Am not very good at C# and am stuck on the multiplayer. Have a player and Two Spawn points created, everything works fine till the Local Host plays and the Client joins the first time. But if I keep the Localhost connected and disconnect the Client and reconnect client, it spawns over the Local player. The method is set to Round Robin and I need to ensure that the new player / client joining should know if there are positions available and then the player should be placed at the empty Spawn point and not on the existing Local player. Your kind help would be highly appreciated.

    Thanks and Brgds
     
  29. Deleted User

    Deleted User

    Guest

    Well, if you need some checks, you need to override your OnServerAddPlayer Method in NentworkManager, also add some triggers to your spawns in order to check if the spawn is occupied.
     
  30. SavedByZero

    SavedByZero

    Joined:
    May 23, 2013
    Posts:
    124
    Unbelievable. I’m having the same level of drama involved in simply passing the NAME of my first server spawned object to the network client side when the network client connects. Someone from Unity should really look over this thread and make an officia tutorial that explains all the simplest parts of this. There’s plenty of tutorials about how to move boxes around in a 3D world, but nothing so basic as this.
     
  31. Deleted User

    Deleted User

    Guest

    You can override the OnServerAddMessage so you could pass the name. or just simply call TargetRpc to this client.

    https://bitbucket.org/Unity-Technol...ssages.cs?at=5.3&fileviewer=file-view-default

    line 108.