Search Unity

Question Spawning an object with parameters

Discussion in 'Netcode for GameObjects' started by Horst6667, Jul 20, 2022.

  1. Horst6667

    Horst6667

    Joined:
    Feb 5, 2018
    Posts:
    17
    Hi!

    What is the best way to spawn an object with certain parameters?

    I have tried to send a ClientRPC from within OnNetworkSpawn of a NetworkBehaviour on the server-side. This did not work. The RPC did not reach the clients.

    Is the following code reliable?

    networkObject.Spawn();
    networkObject.GetComponent<SomeNetworkBehaviour>().SomeClientRPC(some parameters);

    I'm missing something like networkObject.SpawnWithParameters(...)
     
    Last edited: Jul 20, 2022
  2. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    You can give network variables initial values when they're declared, but if you want to set them dynamically before spawning instantiate the prefab first, set the values and then spawn the object.

    Example:
    Code (CSharp):
    1.             Stack stack = GameObject.Instantiate(prefabObject).GetComponent<Stack>();
    2.             stack.IsControlled = false;
    3.             stack.Side = side;
    4.             stack.Units = 1;
    5.             stack.StackState = StackState.Waiting;
    6.             stack.MovePoints = GameConstants.TIME_ONE_DAY;
    7.             stack.Position = position;
    8.             stack.transform.position = new Vector3(position.x, position.y);
    9.  
    10.             stack.NetworkObject.Spawn();
     
  3. Horst6667

    Horst6667

    Joined:
    Feb 5, 2018
    Posts:
    17
    Thank you!
    I read somewhere, that NetworkVariables which have values assigned _before_ the NetworkObject is spawned, are not replicated correctly to the client.
    That is no longer the case?
     
  4. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    I haven't noticed that. I tend to spawn objects with visibility disabled so they're not spawned on the client with the .Spawn() call but when .NetworkShow(clientId) is called.
     
  5. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    I'm looking into similar and all the information I am finding to include other forum post regarding NetworkVariable say you shouldn't set the variable until after it has been spawned.

    in our case we have a rather generic NetworkBehaviour that needs to be configured before it spawns this works fine on Mirror, FishNetworking and similar and seems a common use case for a game so I have to imagine there is some system for this in NetCode for GameObjects

    e.g.

    Code (CSharp):
    1. var GO = Instantiate(prefab);
    2. var behaviour = GO.GetCommponent<MyBehaviour>();
    3. behaviour.someNetworkVariable.Value = 42f;
    4. behaviour.NetworkObject.Spawn();
    My understanding from the documents and other forum posts is that this will not work or will work sometimes but not others. So what is the "correct" way to dynamically configure a network object after instantiation but before spawn?

    Relevant quote from the documentation
    Where they say "first initializing" is what I find confusing, are they referring to variable initialization e.g.
    Code (CSharp):
    1. public NetworkVariable<int> myInt = new(42);
    is okay but no where else can you do that until the object is spawned.

    This gets more concerning if your using Pooled Spawning ... since the instance has been used previously and its internal values will be non-default you obviously need to set those after getting the network object but before spawning it again.
     
    Last edited: Sep 27, 2022
  6. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    This is true since one of Netcode's recent updates, at least it was throwing an exception last time I tried. I changed my above code to set the values after spawning and It's not making any difference in my case which may be due to the objects not being visible to any client on spawn. There's some discussion on the subject on Github here and here.

    This is something I'm in the dark about as network values are sync'ed on each server tick it shouldn't matter if you spawn first and configure afterwards as long as it happens before the next server tick. What I don't understand is how and when the server tick occurs.

    I'm not having to worry too much about this but where I need to clear unwanted values I do it before returning objects to the pool.
     
    Loden_Heathen likes this.
  7. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    Assuming the Spawn() method doesn't do anything right away this would be fine but cant really know that.
    Its important in our case that when OnNetworkSpawn() is called that the variables are set correctly rather thats on the server in the same stack as where we actually call NetworkObject.Spawn() or if its on a Client some time later doesn't matter ... we need assurance that the variables are set correctly

    I'll fumble with it and see what I can get ... would love to hear from Unity on this .. seems like a rather fundamental thing
     
  8. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    There was an issue with network lists as I was finding in OnNetworkSpawn the list was sometimes only partially complete on the client but that was fixed. I've not come across anything similar with network variables so far when they're set in the same frame as the Spawn() call.
     
  9. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    Have played with this a lot and really find NetCode to be far behind Mirror and similar options

    OnNetworkSpawn() is called before network variables are set, they network variables invoke their on change events as they are being applied ... this creates A LOT of issues and difficultly that could easily be corrected

    For example we use a pool system and we have a common network behaviour that gets used for a few spawned "actors"

    We on spawn set several values such as the actor type, health, etc.

    We have found that OnNetworkSpawn for the client the variables are default
    We also find that each gets set one by one and invokes its callback so we have no reliable way to know when the state is up to date with some manual logic

    What we are going to do is set a bool `dirty` when ever any variable updates and then on LateUpdate handle if there are any dirty values ... this is a major pain we simply dont have with Mirror and other options

    With other options the OnClientStart is called only after the object has been synced for the first time thus you know that all of its inital values set by server are set before this is ran

    Next indication of change is only invoked after all variables have been synced that frame thus you know if any is called that all have already been updated for that frame

    For example if you have 2 variables A and B and monitor change on each and react to both for each change then in NetCode if A and B are changed in the same update each's event will be raised but the first raised will be before the second is set

    This is again an issue that can be easily solved with better design .. we can work around it but its really a shame to see this as Unity's replacement for uNET when Mirror, FishNet, etc. derived from uNET are so far ahead in so many ways

    O ya and we have found issues with NetworkTransform as well ... NetCode really seems like its not yet fit for purpose.

    We so far are planning to stick with it and work around its issues but it really does not seem to be in a great place.
     
    Last edited: Nov 17, 2022
  10. cerestorm

    cerestorm

    Joined:
    Apr 16, 2020
    Posts:
    660
    What you're doing sounds like a major hassle so I took a look at all the network objects that I spawn on the client. Those that have their values set in the same frame of spawning have those values on OnNetworkSpawn. Of those one is spawned from a pool (hidden), has values set, then NetworkShow'n to client in the same frame. Two are newly instantiated, spawned hidden, values set, then NetworkShow'n to the client in the same frame. The rest are as the previous two but NetworkShow isn't called until some frames afterwards. Incidentally any value set after NetworkShow is not part of spawn and triggers OnValueChanged.

    Are you using the latest version of Netcode? How it used to work was OnValueChanged was always called when an object spawned but now it seems those values that are set on spawn don't trigger it.

    Something that's a bit of a bugbear with me is that object field changes on the server don't happen on the client in the same order. I raised this with the Netcode developers and they don't see it as an issue, for me if you're only setting a value once I don't see why the changes can't be kept in order. Apparently FishNet works in the same way as Netcode.

    Most of the time the ordering doesn't matter for what I'm doing as the values are used to reflect changes in UI which each UI value updated independently. Where I need to have multiple values appear on the client at the same time I've been making use of structs.
     
  11. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    Yep, on the latest version

    Ya I find NetCode's sync to be annoying :)

    I have used Mirror in the past and could rely on that when OnStartClient was called that ya all the sync vars where up to date regardless of how it was spawned.

    Its been a bit since I looked but I also seem to recall that each frame it updated all values first then raised events as to what was updated which is a MUCH better approach than updating a value raising its change event then updating the next as your obviously going to have use cases where multiple fields are dependent so the dev doesn't need to know the millisecond that the value changed rather the dev needs to know if the object's state changed that frame (all the value changes that happened for that frame)

    So what we have done is wrapped things up to work that way :)

    We simply set a simple (isDirty) bool when any field that we are tracking changes to indicate that the object is dirty. Then on late update if isDirty we process. When we process an object we need to know that all of its data is up to date for that frame. I really cant think of a use case where I would want to process a vars change when I dont know what the final state that frame will be for other variables.

    I also find NetCode has issues with its NetworkTransform, I finally gave up and just sync what I need my self through sync vars but ... ya NetCode seems very much WIP