Destroying scene objects - no InstantiationId error

Hello,

I have a scene with several PhotonViews in it, say items.
When a player picks up an item, I want to destroy it - but use a networked-destroy, so that other players joining after the item was picked up will not see it.

Here's my function for destroying the objects:
public static void DestroySceneObject( PhotonView photonView )
{
	if( PhotonNetwork.IsConnected )
	{
		if( !photonView.IsMine )
		{
			photonView.RequestOwnership();
		}

		PhotonNetwork.Destroy( photonView );
	}
	else
	{
		GameObject.Destroy( photonView.gameObject );
	}
}

However, this only works for objects instantiated with PhotonNetwork.Instantiate. Items that are in the scene are not removed, and I'm getting an error:
Failed to 'network-remove' GameObject because it is missing a valid InstantiationId on view: View XX on XXXXXXX (scene). Not Destroying GameObject or PhotonViews!.

Testing the view's InstnatiationId gives me 0, which kind of makes sense I suppose - but if that's the case, how am I supposed to remove these scene items?


Thanks,

Ev

Comments

  • Hello
    I'm not sure if it works but I would have tried this way using RPC
    
    PhotonView pv = GetComponent<PhotonView>();
    int g_PickedUp =  XyourGameObjectX.GetComponent<PhotonView>().ViewID;
    
    pv.RPC("DestroyRPC", RpcTarget.AllBuffered, g_PickedUp);
    
    [PunRPC]
    private void DestroyRPC(int object_id)
    {
         PhotonNetwork.Destroy(PhotonView.Find(object_id));
    }
    
    
  • Thanks!

    That seems odd - you're sending an RPC to every client, and they all call PhotonNetwork.Destroy? That would probably throw an error on all non-owner clients, and would still have the same issue as my code?
  • Yep, I thought it was a good way to achieve that ?
    I guess you can still check if the client is a owner or not before detroying it
  • But you're still doing PhotonNetwork.Destroy (inside the RPC) - which is what I'm doing in my example as well, which is in turn what throws my error - due to the objects being in the scene (not instantiated using PhotonNetwork.Instantiate).
  • JohnTube
    JohnTube ✭✭✭✭✭
    edited December 2019
    Hi @evyatron,

    Thank you for choosing Photon!

    @M4TT almost got it right
    When a PhotonView is in the scene already it does not have an instantiation ID.
    To destroy is simply sync. this yourself via buffered RPC (or cached event).
    But destroy it locally not using PUN's Destroy.

    There is another issue in your code: you call request ownership and expect it to be synchronous and happen instantly. Ownership request involves sending events to the master client via server and receiving an event from the master client via server.
    There is a callback for when the ownership transfer happens, you could implement it via IPunOwnershipCallbacks.

    Following code is not tested:
    public class OwnershipTest : MonoBehaviourPunCallbacks, IPunOwnershipCallbacks
    {
        private static List<PhotonView> photonViewsToDestroy = new List<PhotonView>();
    
        public static void DestroySceneObject(PhotonView photonView)
        {
            if (PhotonNetwork.IsConnected)
            {
                if (photonView.isRuntimeInstantiated) // instantiated at runtime
                {
                    if (photonView.IsMine)
                    {
                        PhotonNetwork.Destroy(photonView);
                    }
                    else
                    {
                        photonView.RequestOwnership();
                        photonViewsToDestroy.Add(photonView);
                    }
                }
                else // scene view loaded in the scene
                {
                    photonView.RPC("LocalSelfDestroy", RpcTarget.AllBuffered);
                    //otherPhotonView.RPC("LocalDestroy", RpcTarget.AllBuffered, photonView.ViewID); // another option
                }
            }
            else
            {
                GameObject.Destroy(photonView.gameObject); // photonView.LocalSelfDestroy();
            }
        }
    
        public void OnOwnershipRequest(PhotonView targetView, Player requestingPlayer)
        {
        }
    
        public void OnOwnershipTransfered(PhotonView targetView, Player previousOwner)
        {
            if (photonViewsToDestroy.Remove(photonView))
            {
                PhotonNetwork.Destroy(photonView);
            }
        }
    
    // on the same PhotonView (on all?)
    
        [PunRPC]
        public void LocalSelfDestroy()
        {
            GameObject.Destroy(photonView);
        }
    
    // [...] another option if you want to destroy from a single PhotonView available on all clients, similar to M4TT's DestroyRPC
    
        [PunRPC]
        private void LocalDestroy(int viewId)
        {
            GameObject.Destroy(PhotonView.Find(viewId).gameObject);
        }
    }
    
  • Thanks for the reply!

    Wouldn't using the RPC basically clog up the RPC queue though? As well as mean that whenever a new player join, they will then get sent all those "destroy" RPCs and have to do them all locally?
    My thinking is if I have a very large hand-crafted map, I'll have lots of enemies, loot containers, items, etc. And I want them all to be hand placed in the scene. This can easily get to hundreds of views - it sounds to me that if I could do a networked destroy, and these views would get automatically destroyed when the scene is loaded - that would be more efficient? Unless I'm missing something of course :smile:

    And thanks for the ownership callback tip.

    I hope you don't mind me saying, but that combined with the RPC to destroy - I think it makes the PUN UX quite convoluted :smile: I initially thought I'd just need to call PhotonNetwork.Destroy instead of GameObject.Destroy, but now I end up with 50 lines of code just for destroying a game object.
    Obviously if there are technical limitations I'm unaware of that's fine, but instinctually it feels like it should be easier to do.
    I wonder if there's no way to internally handle all of this when doing PhotonNetwork.Destroy (if there's no instantiation id - call and RPC or an internal event or some such).