Search Unity

Is Object Pooling Possible?

Discussion in 'Multiplayer' started by chrisall76, Jun 18, 2015.

  1. chrisall76

    chrisall76

    Joined:
    May 19, 2012
    Posts:
    667
    I'm in the process of converting my Photon code to UNet and I'm stuck at the moment when it comes to object pooling. Does anyone know if pooling is possible with UNet or am I going to have to use regular create/destroy for now?
     
  2. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Yes! There are span handles you can override. Remove your Prefabs from the spawn registration in the editor, and override the on spawn and on despawn methods.

    On the server, get an object from the pool and call NetworkServer.Spawn. On the client, get your object and return it from your spawn handler.

    To return to pool, I recommend using custom messages as, while Network.Destroy will trigger the on despawn override on the client, it seems to also destroy the object on the server. Using custom messages you can handle returning to pool in your own way.

    Check docs for method override names. :)
     
  3. chrisall76

    chrisall76

    Joined:
    May 19, 2012
    Posts:
    667
    Alright I kinda get it, but where do I find the OnSpawn/OnDespawn method? There doesn't seem to be any in NetworkManager
    http://docs.unity3d.com/ScriptReference/Networking.NetworkManager.html
    And a quick search leads to no results.
    Here's my code btw (which is atm broken because I was in the mid process of experimenting with how to go about it).

    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using UnityEngine.Networking;
    4. using System;
    5. using System.Collections;
    6. using System.Collections.Generic;
    7.  
    8. public class GameObjectPoolManager : NetworkBehaviour {
    9.  
    10.     public static GameObjectPoolManager current;
    11.     private List<GameObject> ObjectList = new List<GameObject>();
    12.     private bool WillGrow = true;
    13.     public List<GameObject> ObjectsToReadyUp = new List<GameObject>();
    14.     public float AmountToReady = 2;
    15.  
    16.     public void Awake(){
    17.         WillGrow = true;
    18.         current = this;
    19.         CmdCreateExtra();
    20.     }
    21.  
    22.     [Command]
    23.     public void CmdCreateExtra() {
    24.         if (ObjectsToReadyUp != null) {
    25.             for (int i = 0; i < ObjectsToReadyUp.Count; i++) {
    26.                 for (int w = 1; w < AmountToReady; w++) {
    27.                     GameObject temp = null;
    28.                     temp = (GameObject)Instantiate(ObjectsToReadyUp[i], new Vector3(0, 100, 0), Quaternion.identity);
    29.                     ObjectList.Add(temp);
    30.                     temp.SetActive(false);
    31.                     NetworkServer.Spawn(temp);
    32.                 }
    33.             }
    34.         }
    35.     }
    36.  
    37.     [Command]
    38.     public GameObject CmdCreateObject(GameObject GO, Vector3 Position, Quaternion Rotation){
    39.  
    40.         for(int i=0; i < ObjectList.Count; i++){
    41.             if(ObjectList[i].name == GO.name || ObjectList[i].name == GO.name + "(Clone)"){
    42.                 if(ObjectList[i].activeSelf == false){
    43.                     ObjectList[i].SetActive(true);
    44.                     ObjectList[i].transform.position = Position;
    45.                     ObjectList[i].transform.rotation = Rotation;
    46.                     return ObjectList[i];
    47.                 }
    48.             }
    49.         }
    50.  
    51.         if (WillGrow) {
    52.             GameObject temp = null;
    53.             temp = (GameObject)Instantiate(GO, Position, Rotation);
    54.             ObjectList.Add (temp);
    55.             NetworkServer.Spawn(temp);
    56.             return temp;
    57.         }
    58.  
    59.         return null;
    60.     }
    61.  
    62.     [Command]
    63.     public void CmdDestroyObject(GameObject GO){
    64.         GO.SetActive (false);
    65.     }
    66.  
    67.     [Command]
    68.     public void CmdDestroyAfter(GameObject GO, float time){
    69.         StartCoroutine (DisableObj (GO, time));
    70.     }
    71.  
    72.     public IEnumerator DisableObj(GameObject GO, float time){
    73.         yield return new WaitForSeconds (time);
    74.         if (GO != null) {
    75.             GO.SetActive (false);
    76.         }
    77.     }
    78. }
    79.  
    80.  
     
  4. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Sorry my memory is a bit poor there. It isn't an override. You register spawn and despawn handlers via ClientScene.RegisterSpawnHandler.

    Have a read here:
    http://docs.unity3d.com/Manual/UNetSpawning.html

    I'm also implementing object pooling. The thing I don't quite understand 100% yet myself is despawning. You would normally call NetworkServer.Destroy (i think) which would trigger the despawn handler on the client. I was having null reference exceptions on the server so I believe it is destroying the object on the server. That's why I used messages instead. However I now wonder if NetworkServer.Destroy handles management of anything else... This is probably something we'll both need to find out. Is it safe to despawn without using NetworkServer.Destroy?
     
    Last edited: Jun 19, 2015
    chrisall76 likes this.
  5. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    I'm having difficulty with this myself now. I think I need to use NetworkServer.Destroy to handle the removal of network observers or something, but at the same time I don't want to destroy the object. If I can figure out more I'll let you know.

    If anyone else knows more about this, would love to hear about it.
     
    chrisall76 likes this.
  6. AlwaysBeCoding247

    AlwaysBeCoding247

    Joined:
    Jan 27, 2014
    Posts:
    41
    @Shinyclef I'm going to have a look at this myself later today as I will need to do pooling. But have you tried to just remove/add observers yourself manually along with your message that activates/deactivates(that's what your doing right?) that gameobject? Observers is just a list of NetworkConnections. You can force a set of observers to be rebuilt when you want also. I don't have the code in front of me but I feel there should be a way from what I've read to make this happen with messages.. I am going to look at it later and see if I'm understanding the internals right
     
  7. chrisall76

    chrisall76

    Joined:
    May 19, 2012
    Posts:
    667
    I would assume instead of trying to destroy the object, you'll instead disable all the components on it so that it still exist. Then when respawning would have to find a way to change which localplayer owns the object.
    For now I'll default to every player using there own pool, but I would still like to know if it's possible to have a pool handled by the server.
     
    Last edited: Jun 20, 2015
  8. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    I'd assumed you would just pool everything on every client, and the server, and have syncvars and some kind of communication for enabling disabling them across all clients... I haven't actually tried anything yet as I'm still wrapping my head around the API.
     
  9. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Yes you would pool on each client, but you still need to make sure that all active objects are connected via the network appropriately, and disconnected appropriately when returning to pool. Connecting occurs via NetworkServer.Spawn. Disconnecting would usually occur via NetworkServer.Destroy, but that destroys the object on server so is not suitable for pooling. The biggest problem is we don't actually know very clearly what NetworkServer.Destroy actually does behind the scenes, so it's difficult to replicate the network management.
     
  10. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    I found this in the docs:

    Hrmmm wonder if I can figure this out, haha
     
  11. Shinyclef

    Shinyclef

    Joined:
    Nov 20, 2013
    Posts:
    505
    Yes, I mentioned that in my second post. The rest of the information in this thread still applies.
     
  12. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Doh - totally missed that post! Anyway I have spent days digging into multiplayer but I have been hitting so many roadblocks trying to figure out the basics I haven't got around to messing with pooling yet. I'll drop back by when I get there.
     
  13. gamedevassets-sean

    gamedevassets-sean

    Joined:
    Jul 26, 2013
    Posts:
    76
    Did anyone figure this out yet? I'm currently having a play with the new networking feature and am trying to adapt my object pool to work with it.
     
  14. MD_Reptile

    MD_Reptile

    Joined:
    Jan 19, 2012
    Posts:
    2,664
    Nope not yet. Still getting used to the API really. I think I know what to do though, but haven't tried it, which is spawn across the network the whole pool on every client, and just pass around vars and changes, then destroy/instantiate more as necessary. No code sample here though haha
     
  15. Sukiyaki

    Sukiyaki

    Joined:
    Jan 26, 2015
    Posts:
    4
    I'm not quite sure how to get the object back from client while the SpawnHandler delegate only give you a Vector3 and and assetID.

    OnServer:
    - I spawn an object from the pool.
    - Call NetworkServer.Spawn(myobject);

    OnClient:
    - The SpawnHandler delegate method get called but how to get my object back? There are only a Vector3 and an assetID and it looks like there is no method to get the prefab from its assetId.
     
  16. Sukiyaki

    Sukiyaki

    Joined:
    Jan 26, 2015
    Posts:
    4
    After some minutes digging through all Unet classes in Visual Studio i just magically found out how to get prefab when you know its assetID. Simply call this:

    Code (CSharp):
    1. ClientScene.prefabs[assetId]
    and it will return the prefab associated with the assetId.
    I also tried googling "Unity Unet ClientScene" and there is no relevant result :(
     
    MD_Reptile likes this.
  17. Sukiyaki

    Sukiyaki

    Joined:
    Jan 26, 2015
    Posts:
    4
    @Shinyclef I'm so happy that in the 5.1.2p1 there is a method called "Unspawn" which does excactly what you (and me :D ) want. There is no need to implement a custom message system anymore when you want to despawn an object but not destroying it.

    Cheers!
     
    Last edited: Aug 2, 2015
    Deleted User likes this.
  18. Jay-Pavlina

    Jay-Pavlina

    Joined:
    Feb 19, 2012
    Posts:
    195
    When I try to spawn on object that was previously unspawned, I get an error that says "Object has non-zero netId." Is this a bug?
     
    Sukiyaki likes this.
  19. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    hm. that sounds like a bug. I'll look into it.
     
    Jay-Pavlina likes this.
  20. Sukiyaki

    Sukiyaki

    Joined:
    Jan 26, 2015
    Posts:
    4
    Same here!
    I'm getting crazy trying to reset the netId until i realize that i can't :(
    NetworkInstanceId (netid) is read-only and it locks developers from modifying any of its properties.

    At first i though this is by design so i tried a work-arround: When I fill the pool on server with actual clones of object I also NetworkServer.Spawn() it, so the pool is synced across network.

    But soon when i need to completely destroy the object across network, NetworkServer.Destroy() also NOT reset the netId as well.

    You could run a sample test by some code like this:
    Code (CSharp):
    1.              
    2. NetworkServer.Spawn(go);
    3. NetworkServer.Destroy(go); // You can replace this with "NetworkServer.Unspawn(go)"
    4. NetworkServer.Spawn(go); // Non-zero netId occurs
    5.  
     
  21. seanr

    seanr

    Unity Technologies

    Joined:
    Sep 22, 2014
    Posts:
    669
    bug 717122 filed on this.

    "De-spawning a server object with NetworkServer.UnSpawn() does not cleanup the object properly to allow it to be respawned with NetworkServer.Spawn(). This function should set the networkId of the object back to zero so that it can be Spawned again.

    Also calling NetworkServer.UnSpawn() on an object that was never spawned gives a nullReference exception instead of an error message."
     
    Deleted User and Jay-Pavlina like this.
  22. iagoccampos

    iagoccampos

    Joined:
    Aug 2, 2014
    Posts:
    10
    How I reset my netId for network game objects that is already in the scene, I mean, for objects that not spawned?
     
  23. isbasher

    isbasher

    Joined:
    Mar 19, 2014
    Posts:
    8
    When a player disconnects, how do i tell the server to Unspawn all objects from pools related to that player?
     
  24. justjuank

    justjuank

    Joined:
    May 30, 2013
    Posts:
    5
    Hi guys, thanks to your comments and other threads in the forums I was able to make my own implementation of a networking object pooling system.
    You can check it out here and try it out, I hope you like it:
    http://www.justjuank.com/network-object-pooling-for-unity3d/
    Sorry if this is an old thread, just wanted to share it with you.

    Juan
     
  25. svendkiloo

    svendkiloo

    Joined:
    Dec 2, 2014
    Posts:
    11
    chrisall76 likes this.
  26. MaxFrax96

    MaxFrax96

    Joined:
    Nov 18, 2012
    Posts:
    11
    How did you handle the rotations? I mean when you spawn the object your delegate gets the position to set, but no rotation.
    How can I configure the right way the object out from the pool?
    The objects I'm pooling don't have synced transform
     
  27. Griffo

    Griffo

    Joined:
    Jul 5, 2011
    Posts:
    700
    Why when using this way http://docs.unity3d.com/Manual/UNetCustomSpawning.html on the host everything works as expected the spawned bullets are put back in the pool and deactivated, but on the clients the spawned bullets are added to the scene then deactivated and left there adding up with each bullet?