How to despawn if Object is manually instantiated?
The whole answer can be found below.
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).
How to despawn if Object is manually instantiated?
vinibeg
2021-12-02 16:03:11
Hello,
I am following the Manual Instantiation from the doc here:
And it works. Now, how do I destroy the gameobject? Do i need to call Photon.Destroy? Or manual destroy, i.e remove PhotonView component, then call Unity Destroy function?
Comments
More information:
I use the manual instantiation because I use PoolManager game assets:
So for example, I want to spawn a vfx:
var obj = PoolManager.Pools[pool].Spawn("VFX", pos, rot);
So, when I want to despawn, by right using the PoolManager would be:
PoolManager.Pools[pool].Despawn(gameObject);
However, the gameobject still have photonView component attached to it. How should I do?
Pardon my english, as its not my first language.
Hi @vinibeg,
Thank you for choosing Photon!
You need a manual destruction event like manual instantiation:
call raise event with custom event code for manual network destruction, payload should be network object's ViewId.
remove manual network instantiation event from cache.
on receiving clients find network object using ViewId and Unity destroy it locally.
As a side note, did you consider implementing support for PoolManager as custom prefab pool? Read more here.
Hello JohnTube,
Thanks for your answer. Following your steps:
call raise event with custom event code for manual network destruction, payload should be network object's ViewId. [ done ]
remove manual network instantiation event from cache.
[How do I manually remove the instantiation event from cache? Do i need to remove the photonView component?]
on receiving clients find network object using ViewId and Unity destroy it locally.
[ sure, i will use my current PoolManager to despawn it]
Regarding the PrefabPool, do you mean, I should extend my current PoolManager to implement "IPunPrefabPool"?
Because my project has 2 modes, single player and multiplayer mode. Do you think i should:
Add photonView component to all single player prefabs?
Add and remove photonView component on spawn and despawn method?
Sorry, how do i manual remove instantiate from cache?
RaiseEventOptions options = new RaiseEventOptions() { CachingOption = EventCaching.RemoveFromRoomCache };
PhotonNetwork.RaiseEventInternal(MPEvent.Remove, photonView.id, options, SendOptions.SendReliable);
Then on the clients side, i do:
GameObject g = PhotonView.Find(photonViewID).gameObject;
Destroy(g)?
Hi @vinibeg,
The network destroy is three steps, I think you already got two working.
Remove the cached manual instantiation event from the events cache of the room, so no new actor or rejoining actor will receive it, caching option is correct yes (EventCaching.RemoveFromRoomCache) (event code == same manual instantiation event code, payload or data should be a filter for event cache removal if what you sent initially is a Hashtable or same structure which hopefully the server accepts as filter so you will remove only the exact event occurrence related to a specific ViewId and not all instantiations of ALL ViewIDs, raiseEventOptions.TargetActors should be set to the actor number of the instantiator). Read more here about Cached Events here
. If you're stuck, share code snippet for both manual instantiation and destruction you used/tried/tested.
Send manual network destroy event (custom event code + ViewId as payload).
When receiving manual network destroy event, destroy or despawn object locally.
About IPunPrefabPool, it was just a suggestion and a totally alternative way of using manual network instantiation and destruction which allows you to keep using default built-in methods PhotonNetwork.Instantiate & PhotonNetwork.Destroy BUT use your own custom object pooling.
Hello @JohnTube ,
Thanks for your reply. Here is my codes.
In my NetworkSpawner.cs, send manual Instantiate / Remove instantiate event codes:
public void SpawnBallObject(Transform obj, string prefabName, string pool, E_SpawnAction action, int ownerID)
{
if(action == E_SpawnAction .Spawn)
{
PhotonView photonView = obj.GetComponent<PhotonView>();
if(photonView)
{
// remove it first, and reattach
Destroy(photonView);
}
photonView = obj.GetComponent<PhotonView>();
photonView.OwnershipTransfer = OwnershipOption.Takeover;
photonView.ObservedComponents = new List<Component>();
if (!obj.GetComponent<PhotonTransformView>())
{
obj.gameObject.AddComponent<PhotonTransformView>();
}
photonView.ObservedComponents.Add(obj.GetComponent<PhotonTransformView>());
if (PhotonNetwork.AllocateViewID(photonView))
{
Hashtable ht = new Hashtable();
ht.Add("prefabName", prefabName);
ht.Add("pos", obj.transform.position);
ht.Add("rot", obj.transform.rotation);
ht.Add("viewID", photonView.ViewID);
ht.Add("pool", pool);
ht.Add("ownerID", ownerID);
ht.Add("action", action);
RaiseEventOptions raiseEventOptions = new RaiseEventOptions
{
Receivers = ReceiverGroup.Others,
CachingOption = EventCaching.AddToRoomCache
};
SendOptions sendOptions = new SendOptions
{
Reliability = true
};
PhotonNetwork.RaiseEvent(MP_EventCode.SpawnNetworkObject, ht, raiseEventOptions, sendOptions);
}
else
{
Debug.LogError("Failed to allocate a ViewId.");
Destroy(obj);
}
}
// Remove instantiate cache from room
else
{
Hashtable ht = new Hashtable();
ht.Add("prefabName", prefabName); // should i still send this if not in used?
ht.Add("pos", obj.transform.position); // should i still send this if not in used?
ht.Add("rot", obj.transform.rotation); // should i still send this if not in used?
ht.Add("viewID", obj.GetComponent<PhotonView>().ViewID);
ht.Add("pool", pool);
ht.Add("ownerID", ownerID); // should i still send this if not in used?
ht.Add("action", action);
int[] actors = { ownerID };
RaiseEventOptions raiseEventOptions = new RaiseEventOptions
{
TargetActors = actors,
CachingOption = EventCaching.RemoveFromRoomCache
};
SendOptions sendOptions = new SendOptions
{
Reliability = true
};
PhotonNetwork.RaiseEvent(MP_EventCode.SpawnNetworkObject, ht, raiseEventOptions, sendOptions);
}
}
Then in my EventManager.cs that handles the event:
...
case MP_EventCode.SpawnNetworkObject:
{
Hashtable evTable = content as Hashtable;
E_SpawnAction action = (E_SpawnAction)evTable["action"];
if(action == E_SpawnAction.Spawn)
{
// Dont spawn multiple
if (senderId == PhotonNetwork.LocalPlayer.ActorNumber) break;
string prefabName = (string)evTable["prefabName"];
Vector3 pos = (Vector3)evTable["pos"];
string pool = (string)evTable["pool"];
int ownerID = (int)evTable["ownerID"];
Transform prefab = PoolManager.Pools[pool].Spawn(prefabName);
PhotonView photonView = null;
if (!prefab.GetComponent<PhotonView>())
photonView = prefab.gameObject.AddComponent<PhotonView>();
photonView.ViewID = (int)evTable["viewID"];
photonView.OwnershipTransfer = OwnershipOption.Takeover;
photonView.ObservedComponents = new List<Component>();
if (!prefab.GetComponent<PhotonTransformView>())
{
prefab.gameObject.AddComponent<PhotonTransformView>();
}
photonView.ObservedComponents.Add(prefab.GetComponent<PhotonTransformView>());
}
else
{
// This is for RemoveCacheFromRoom
// Currently do nothing...
}
}
break;
....
In my Ball.cs script, where I want to destroy the ball:
public void DespawnObject()
{
if(NetworkManager.IsSinglePlayer)
{
PoolSpawner.Despawn(gameObject, PoolId.Prefabs);
}
else
{
// First event: remove cache from room
NetwoskSpawner.Instance.SpawnBallObject(transform, transform.name, PoolId.Prefabs, E_SpawnAction.Despawn, ownerID);
// Second event: destroy game object
NetworkSpawner.Instance.DespawnBaseObject(pv.ViewID, PoolId.Prefabs);
}
}
Are above codes correct?
When I try to spawn the ball for second time, I got this:
AllocateViewID() can't be used for PhotonViews that already have a viewID. This view is: View 1001 on MP_Ball(Clone)001 Spawned By Ball Controller
Hi @vinibeg,
Code looks fine but it's missing DespawnBaseObject method and OnEvent handling.
This part is a bit suspicious from a Unity point of view unless I'm missing something.
"remove", "reattach"?
PhotonView photonView = obj.GetComponent<PhotonView>();
if(photonView)
{
// remove it first, and reattach
Destroy(photonView);
}
photonView = obj.GetComponent<PhotonView>();
I would suggest minor optimizations or changes (NOT DIRECTLY RELATED TO THE ISSUE IN QUESTION AND ARE OPTIONAL):
I understand why you're doing this from the comment, you're spawning locally and sending event to Others to spawn. In this case you won't get the live event but you may get this event if you leave the room and rejoin it (PlayerTTL ! = 0 & RejoinRoom) or join it again (AutoCleanUp / CleanUpCacheOnLeave == false).
if (senderId == PhotonNetwork.LocalPlayer.ActorNumber) break;
No need for "action" key/value as you should not receive RemoveFromRoomCache events, those are only meant for the server.
No need for "ownerID" if by that you mean ActorNumber (or UserID). ActorNumber can be guessed from ViewID, UserId is exchanged if PublishUserId is enabled.