Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How to initialize value on playerPrefab before the value is SyncVar'ed to the client?

Discussion in 'Multiplayer' started by soaring, Jun 23, 2016.

  1. soaring

    soaring

    Joined:
    Jun 22, 2015
    Posts:
    27
    We have a multiplayer game where we want to be able to set value on a property on the playerPrefab immediately after it is Instantiated on the Server, before SyncVar sends the values. Basically, our goal is to make sure when the OnServerStart()/OnClientStart()/Start() is called on either server or client, the new value is already set.

    According to the Object Creation Flow on: http://docs.unity3d.com/Manual/UNetSpawning.html
    • Prefab with NetworkIdentity component is registered as spawnable
    • GameObject 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)
    • NetworkServer.Spawn() is called with the instance
    • The state of the SyncVars on the instance on the server are collected by calling OnSerialize() on NetworkBehaviour components
    However, for the life of me, I can't figure out where do I put the code that can "set initial values on the instance" prior to the SynVar states are serialized.

    I've been beating my head on this for 2 days. Any help would be greatly appreciated!
     
  2. Severos

    Severos

    Joined:
    Oct 2, 2015
    Posts:
    181
    Your code would look something like:
    Code (CSharp):
    1. GameObject go = Instantiate()//create the new object from prefab on server.
    2. go.GetComponent<SomeClass>().x=5; //set the variable you want to initialize
    3. NetworkServer.Spawn(go);
    The other way I can think about is overriding the serialize function.
    But out of curiosity, why would you need an initial value before syncing vars?
     
  3. Baroni

    Baroni

    Joined:
    Aug 20, 2010
    Posts:
    3,256
    Awake() should be called before SyncVar updates - at least it does in my script.

    E.g. saving the initial health value as max health, before getting the actual networked (current) health value.
     
  4. Severos

    Severos

    Joined:
    Oct 2, 2015
    Posts:
    181
    Far to my understanding of networking, SyncVar won't be active until you spawn the object, so if you use Instantiate you just created the object but didn't spawn it over the network, so SyncVar have no effect atm.
    The other 2 options I can think of are using custom network message and overriding (de)serialize function.
     
  5. soaring

    soaring

    Joined:
    Jun 22, 2015
    Posts:
    27
    @Baroni - I can give that a try...

    @Severos - like @Baroni was mentioning, we need to do that in order to do things like setting the initial energy, hp, uniqueID or any other dynamic values that would be generated during run time.

    One approach we ended up using, though I am not sure it is the best way, is to NetworkManager.OnServerAddPlayer () to be something like this:

    Code (CSharp):
    1. public override void OnServerAddPlayer (NetworkConnection conn, short playerControllerId)
    2. {
    3.  
    4.     // If you don't call the base class, then the player doesn't get added.
    5.     base.OnServerAddPlayer (conn, playerControllerId);
    6.  
    7.     // now find the player object
    8.     PlayerController[] playerControllers = FindObjectsOfType<PlayerController> ();
    9.     foreach (PlayerController playerController in playerControllers) {
    10.         if (playerController.connectionToClient.connectionId == conn.connectionId) {
    11.             // Intialize the player Object here...
    12.         }
    13.     }
    14. }
    This way the initialization values are set on the player objects on the server prior to the SyncVars being set and all the values passed to the Client. There are some downsides to this approach, for example, playerController. OnStartServer() is called prior to this, so the initialized values that you set won't be available on the server side unless you catch it in playerController.Start()
     
  6. Severos

    Severos

    Joined:
    Oct 2, 2015
    Posts:
    181
    Actually you don't need to call base.OnServerAddPlayer you can manually spawn your player like this:
    Code (CSharp):
    1. public override void OnServerAddPlayer (NetworkConnection conn, short playerControllerId) {
    2.   GameObject go = Instantiate(playerPrefab,Vector3.Zero,Quaternion.Identity);
    3. //do your initialization on the object here.
    4. NetworkServer.Spawn(go);
    5. }
    Just don't forget to override both add player handlers (there's another one that takes an extra networkMessage argument.
     
    soaring likes this.
  7. soaring

    soaring

    Joined:
    Jun 22, 2015
    Posts:
    27
    Thanks! Yeah, that would make the code much simpler and cleaner.