Pooling Objects in PUN

The whole answer can be found below.

Please note: The Photon forum is closed permanently. After many dedicated years of service we have made the decision to retire our forum and switch to read-only: we've saved the best to last! And we offer you support through these channels:

Try Our
Documentation

Please check if you can find an answer in our extensive documentation on PUN.

Join Us
on Discord

Meet and talk to our staff and the entire Photon-Community via Discord.

Read More on
Stack Overflow

Find more information on Stack Overflow (for Circle members only).

Write Us
an E-Mail

Feel free to send your question directly to our developers.

Pooling Objects in PUN

Vallar
2018-08-07 10:35:45

Hi,

So I am trying to create two pooling systems in Unity and so far I have been successful with one of them and failing horribly with the other.

The first pool system I have is based on having each client spawn their own objects locally instead of having the Master Client spawning the main and spawning the network copy on all clients. This I was able to achieve with no problem (so far at least).

The problem is the second; trying to pool networked objects. My PoolManager is basically a ScriptableObject (SO) so I can't run RPCs on it directly. But I can RaiseEvent on it and that I have done with the previous setup. In the new setup however, I am not quite sure how to set this up.

The problem resides in identifying the exact object I want to deactivate/activate on clients. My first reaction is to use the pool list I have that has all the objects I want to pool. So I would RaiseEvent and send over the index and the clients search for the specific object in the list. Then I remembered that the list is only kept on the Master Client.

My second reaction was that I would have the list be populated in all the clients so each client has their PoolManager with the enemiesPool list that has the objects stored. But then I thought this list since it isn't synced, it might end up messed up due to changes in it (adding, removing and so on).

My third reaction was to also sync the list but that felt like a whole other can of worms and just felt not quite the best approach anyway.

So I guess my question is, how do I achieve object pooling using my SO object in PUN?

Thanks.

Comments

S_Oliver
2018-08-07 17:41:46

Hello @Vallar ,

In my last Project i faced kind of the same Problem. Creating a Pooling System wich workes with Photon was really hard.
I needed a System wich worked like this, some Objects should be synced over Network(Position,Rotation) so i need a way to synchroinze disable/enable too and i needed a system wich gets initilized on all clients the same and i could just call a method wich stores in an different rpc to Pop an Object from Pool.

After tons of fails, i come up with a simple idea.
Each Object i needed to Sync has a photonView, so i could just send a array with with PhotonView Ids to all Clients :D
And accsessing object via ids..

https://hastebin.com/ojociyegod.cpp

https://hastebin.com/dawomijayi.cs

Vallar
2018-08-07 21:07:23

@S_Oliver wrote:

Hello @Vallar ,

In my last Project i faced kind of the same Problem. Creating a Pooling System wich workes with Photon was really hard...

Hi @S_Oliver

Thanks for your reply and sharing your code. I have taken a look at the code but I don't think I can use it given that my PoolManager is a ScriptableObject and thus has no access to PhotonView and with a bit of experimenting, I can't also use an object's PhotonView from outside of the object itself (so can't let's say use EnemyA's PhotonView from PoolManager).

But you gave me an idea where the Enemy I have has a Monobehaviour on it and so I added some methods on it to deactivate/activate itself. It is still theory as when I tested I got an error in part of it.

Right now however I am having a VERY weird NullReference error pointing to this line of code:
EnemyAI ai = PhotonNetwork.Instantiate(enemyPrefabs[i].name, Vector3.one * 100, Quaternion.identity, 1).GetComponent();

I use the same exact line for spawning players but instead of EnemyAI I have PlayerInput and instead of group 1 I use group 0 for players. It worked quite fine for players but not sure why is this triggering a null reference. I am sure enemyPrefab is populated, the prefab is also in the resources folder.

Any ideas?

Thanks

S_Oliver
2018-08-08 05:37:09

Try to put this on 2lines GameObject enemy = PhotonNetwork.Instantiate();
EnemyAI ai = enemy.GetComponent();

Vallar
2018-08-08 08:10:32

@S_Oliver wrote:

Try to put this on 2lines GameObject enemy = PhotonNetwork.Instantiate();

EnemyAI ai = enemy.GetComponent();

So been trying to resolve this for an hour now and finally reached a solution, came to post found you posted. Sorry.

So basically the problem is the groups in the PhotonNetwork.Instantiate(). If you change the group from 0 (default) to 1, it returns null for some reason. Once it is set to 0, it worked. Originally I thought group in that method is basically like a team so one can use it to separate player teams (to simulate PVP) as I recall a tutorial (can't remember official or otherwise) said it was so.

But then I hit a completely different wall related to this still. For some odd reason that line of code when executed, spawns the objects disabled and without running Awake, Start or OnEnable. I really don't know how it spawns the object disabled without running them given that even Unity when spawning an object ends up running them.

Moreover, this same line of code I posted above I have a similar one for players as mentioned and doesn't do this. So I am really not sure what is going on.

S_Oliver
2018-08-08 08:22:21

I never dealt with this but I found this https://doc.photonengine.com/en-us/pun/current/gameplay/interestgroups

The Team are set/get via Custom Player Properties.

Vallar
2018-08-08 08:25:09

@S_Oliver wrote:

I never dealt with this but I found this https://doc.photonengine.com/en-us/pun/current/gameplay/interestgroups



The Team are set/get via Custom Player Properties.

Yeah, I am now using 0 by default till I get to the point where I need to use them for networking culling.

Hopefully we get someone to comment on the fact that objects are being spawned deactivated without running Awake, Start, Enable and not asked to be deactivated.

[Deleted User]
2018-08-09 09:17:34

Hi @Vallar,

Hopefully we get someone to comment on the fact that objects are being spawned deactivated without running Awake, Start, Enable and not asked to be deactivated.

Is this about using another group in the Instantiation call? If so, this behaviour is probably correct. When you use 0 as group parameter in the Instantiation call, each client in the room will get this Instantiation call and process it. If you use another value than 0 as group parameter, the Instantiation call will only be forwarded to clients, which have subscribed to that certain group.

The group 0 is somehow a 'special' group, because all clients are subscribed to it by default and they can't unsubscribe from it.

Vallar
2018-08-09 12:36:01

@Christian_Simon wrote:

Hi @Vallar,

Hopefully we get someone to comment on the fact that objects are being spawned deactivated without running Awake, Start, Enable and not asked to be deactivated.

Is this about using another group in the Instantiation call? If so, this behaviour is probably correct. When you use 0 as group parameter in the Instantiation call, each client in the room will get this Instantiation call and process it. If you use another value than 0 as group parameter, the Instantiation call will only be forwarded to clients, which have subscribed to that certain group.

The group 0 is somehow a 'special' group, because all clients are subscribed to it by default and they can't unsubscribe from it.

Hey @Christian_Simon glad you noticed the thread.

Yeah, I discovered that the hard way. In any case, right now my problem is actually related to instantiation but I am not sure specifically what is wrong. After hours of debugging with the 0 vs another value with groups I finally got the objects to instantiate using this code:

EnemyAI ai = PhotonNetwork.Instantiate(enemyPrefabs[i].name, Vector3.one * 100, Quaternion.identity, 0).GetComponent();

This is the same code I use to instantiate players except I use PlayerInput with players instead of EnemyAI. The problem however, the enemy objects spawn deactivated unlike the players. Moreover, they don't run Awake, Start or Enable. So it isn't like they are instantiated active then get disabled but rather are disabled immediately. I didn't know that this can happen specially when the objects created don't have any parents.

Normally in Unity's Instantiate method the object is created active so there is at least a few frames where Awake, Start and OnEnable run -- even if you disable the object right away. This doesn't happen here.

Any ideas what is wrong and why is this happening?

Note: I am very aware that disabled objects don't run Awake, Start and OnEnable and I am aware that if an object has a deactivated parent, it won't run those 3 methods. The case I am speaking about is when there is no parent and no code to disable the object and the object is disabled + the 3 methods aren't running.

[Deleted User]
2018-08-09 12:57:53

Do you use the Pooling possibilities provided by PUN? For example implementing IPunPrefabPool and assigning it to PhotonNetwork.PrefabPool? If so, Awake and Start won't be called, and I guess the same happens to OnEnable. Instead of those functions, OnPhotonInstantiate() gets called.

I honestly haven't worked with object pooling before and only found the above information in the version history, when this feature has been introduced:

v1.61 (17. September 2015) Added: Option to use an Object Pool. Implement IPunPrefabPool and assign it to PhotonNetwork.PrefabPool. Note: GameObjects will not run Awake() and Start() but PUN will call OnPhotonInstantiate() as if the GameObject would be new.

Vallar
2018-08-09 13:08:43

@Christian_Simon wrote:

Do you use the Pooling possibilities provided by PUN? For example implementing IPunPrefabPool and assigning it to PhotonNetwork.PrefabPool? If so, Awake and Start won't be called, and I guess the same happens to OnEnable. Instead of those functions, OnPhotonInstantiate() gets called.

I honestly haven't worked with object pooling before and only found the above information in the version history, when this feature has been introduced:

v1.61 (17. September 2015) Added: Option to use an Object Pool. Implement IPunPrefabPool and assign it to PhotonNetwork.PrefabPool. Note: GameObjects will not run Awake() and Start() but PUN will call OnPhotonInstantiate() as if the GameObject would be new.

No I am not using anything provided by Photon. I am doing my own thing. Just instantiating object regularly (using the code I provided) then I add them to a list and manage that list as needed.

I found the stuff you mentioned but since they are old and no one practically is using them + the asset PUN used to support is deprecated I didn't bother.

But the problem isn't particularly about pooling, it is about the objects spawning in this state which is weird.

[Deleted User]
2018-08-09 13:27:20

Just instantiating object regularly (using the code I provided) then I add them to a list and manage that list as needed.

I guess I have created a similar test case, which was working for me: instantiated an object regularly, modified it's name to match the name of the prefab in the Resources folder and used that name in the network Instantiation function.

Are there any errors logged to the console? Can you share more of the source code or maybe create a repro case?

Vallar
2018-08-09 13:43:03

I can share more code, here is what I have:

public void PoolEnemies()
{
if (PhotonNetwork.isMasterClient == false)
return;

    for (int k = 0; k < enemyPrefabs.Length; k++)  
    {  
        for (int i = 0; i < initialPoolCount; i++)  
        {  
            EnemyAI ai = PhotonNetwork.Instantiate(enemyPrefabs[k].name, Vector3.one * 100, Quaternion.identity, 0).GetComponent<EnemyAI>();  
            enemiesPool.Add(ai);  
            //ai.DisableEnemy();  
        }  
    }  
}</code>

Note I am testing with Master Client only (so this isn't about other clients seeing things differently). The code is called after the scene loaded and right after the player is instantiated.

The code runs, but all enemies are spawned deactivated (despite the line ai.DisableEnemy(); is commented) and none of them ran Awake, Start or OnEnable (I know because I have code in there with Debug.Log() and it never put anything in the console).

No errors in the console whatsoever.

If you need more information let me know.

S_Oliver
2018-08-09 13:48:19

Maybe the prefabs are just disabled ?

Vallar
2018-08-09 15:40:54

@S_Oliver wrote:

Maybe the prefabs are just disabled ?

It never occurred to me that this may be the problem given that I never disable prefabs. Thanks for catching that.

Dochder
2018-08-23 17:23:22

Hey guys, I don't really understand the code behind the Network-Culling. What do I have to change to make the player just send messages into groups he is in and not send messages to other groups at all? Since I'm in Top-Down mode I don't need to synchronize players in other groups at all. I would be really happy for some help.

Greetings

Dochder
2018-08-23 17:51:36

I am using the integrated Culling which came with Pun. Or at least, how can I let PUN just send messages to the four active groups I'm in and to all others not send the position at all? If my question is too unspecific just tell me, then I will try to give out more informations.

Vallar
2018-08-23 20:34:16

@Dochder wrote:

I am using the integrated Culling which came with Pun. Or at least, how can I let PUN just send messages to the four active groups I'm in and to all others not send the position at all? If my question is too unspecific just tell me, then I will try to give out more informations.

Not an expert but the way I understand it every player is subscribed to group 0 by default. Everything sent over group 0 will be received by everyone. If you want some information to be hidden from certain players you'll need to send it over a different group (1, 2, 3, etc...) and have the players that should receive the information subscribe to the group you are sending on.

This article
should explain how to subscribe to the interest groups

Back to top