Hi all.Recently I was developing a multiplayer shooting game with unity standard networking system.I found that NetworkView component would destroy the clone game object in others cilent when I turn off a game obejct in server ( gameObject.SetActive(false) ).
It makes me confused because I don’t know how to handle game objects with ‘object pool’.For example, a bullet in server side recycled by pool and turn to inactive for running out of life time.Then the clone bullet in client side destroyed.At this moment, if I reused the bullet and turn it to active in server side, I get an error in editor which says ‘Received state update for view id’ AllocatedID: XXX’ but the NetworkView doesn’t exist’.(Of course, the NetworkView in client was destroyed by it self.)
So my question is, how do I handle bullets efficiently and correctly with NetworkView and ‘object pool’?
Any help would be appreciated.
Normally you would handle the bullets locally and dont have NetworkViews on them at all.
What I do, is i use RPCs to send data back and forth, and handle the objects as I’d like. For bullets, as BF games said… you don’t put network views on the bullets. You send via an RPC to the client, shoot bullet in this direction at this speed, and you send that data to all the clients so everyone is in sync. You just need to think creatively how to send data back and forth from the server to the client, and tell the client what to do. You aren’t restricted to network views.
Thanks to BFGames and Gambinolnd!
I removed network views on the bullets and turn to send ‘spawn bullets here!’ command via RPC instead.Everything works perfectly now 
here is an example of a masterspawner with pooling i use for enemy, could be used with anything else, it work with photon, and will manage safely host migration when a masterclient switch
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class MasterSpawner : Photon.MonoBehaviour {
[HideInInspector]
public List<int> SpawnedObjects = new List<int> ();
[HideInInspector]
public List<GameObject> PooledObjects = new List<GameObject> ();
void OnJoinedRoom()
{
if(isMaster)
BuildPool ();
}
public virtual void BuildPool()
{
}
void Awake()
{
GetInstance ();
}
public static MasterSpawner instance;
/// <summary>
/// Gets the instance.
/// </summary>
/// <value>The instance.</value>
public static MasterSpawner Instance
{
get
{
return instance;
}
}
void GetInstance()
{
instance = this;
}
void SendPlayerCurrentSpawnedAndPooled(PhotonPlayer playerTarget)
{
photonView.RPC ("ReceiveSpawnedObjectList", playerTarget, SpawnedObjects.ToArray ());
List<int> pooledIDs = new List<int> ();
foreach (GameObject obj in PooledObjects)
{
pooledIDs.Add (FindView (obj));
}
photonView.RPC ("ReceivePooledObjectList", playerTarget, pooledIDs.ToArray ());
}
[RPC]
public void ReceiveSpawnedObjectList(int[] toSpawn)
{
Debug.Log ("From Master Server: <color=green>"+toSpawn.Length+" Units spawned</color>");
foreach (int activeThis in toSpawn)
{
FindGO (activeThis).SetActive (true);
}
}
[RPC]
public void ReceivePooledObjectList(int[] pooled)
{
foreach(int obj in pooled)
{
FindGO (obj).transform.parent = transform;
PooledObjects.Add (FindGO (obj));
}
}
/// <summary>
/// Despawns the object. Must be called only from master server
/// </summary>
/// <param name="obj">Object.</param>
public virtual void DespawnObject(GameObject obj)
{
if (!isMaster)
return;
int objectID = obj.GetPhotonView ().viewID;
photonView.RPC ("RPC_DespawnObject", PhotonTargets.AllBufferedViaServer, objectID);
//[[Updating the list]]
if(SpawnedObjects.Contains (objectID))
SpawnedObjects.Remove (objectID);
}
public virtual void SpawnObject(GameObject obj, Vector3 spawnPosition)
{
if (!isMaster)
return;
GameObject NextObjectFound = NextToSpawn (obj); // check null?
if (!NextObjectFound)
{
Debug.Log ("<color=red>Not Enough Object in the Pool</color>");
return;
}
int objectID = NextObjectFound.GetPhotonView ().viewID;
photonView.RPC ("RPC_SpawnObject", PhotonTargets.AllBufferedViaServer, spawnPosition, objectID);
//[[Updating the list]]
SpawnedObjects.Add (objectID);
}
[RPC]
public void RPC_DespawnObject(int ourObjectID)
{
FindGO (ourObjectID).SetActive (false);
FindGO (ourObjectID).transform.position = Vector3.zero;
}
[RPC]
public void RPC_SpawnObject(Vector3 spawnPos, int ourObjectID)
{
FindGO (ourObjectID).transform.position = spawnPos;
FindGO (ourObjectID).SetActive (true);
}
/// <summary>
/// create unit as scene object, add it to pooled list, and immediatly disable it.
/// </summary>
/// <returns>The unit.</returns>
public GameObject PoolUnit(string UnitName)
{
GameObject newUnit = PhotonNetwork.InstantiateSceneObject
(
UnitName,
Vector3.zero,
Quaternion.identity,
0,
null
);
newUnit.transform.parent = transform;
PooledObjects.Add (newUnit);
return newUnit;
}
#region PhotonEvents
void OnPhotonPlayerConnected(PhotonPlayer newPlayer)
{
if (isMaster)
SendPlayerCurrentSpawnedAndPooled (newPlayer);
}
void OnMasterClientSwitched()
{
if (isMaster)
{Debug.Log ("you are now MasterClient");
SpawnedObjects.Clear ();
for(int i =0; i < PooledObjects.Count; i++)
{
if(PooledObjects[i].activeSelf)
SpawnedObjects.Add (PooledObjects[i].GetPhotonView().viewID);
}
}
}
#endregion
#region helper
private GameObject FindGO(int view_ID)
{
PhotonView v = PhotonView.Find (view_ID);
if (v.viewID != 0)
return v.gameObject;
else
return null;
}
private int FindView(GameObject go)
{
return go.GetPhotonView ().viewID;
}
public bool isMine
{
get
{
return photonView.isMine;
}
}
public bool isMaster
{
get
{
return PhotonNetwork.isMasterClient;
}
}
public bool IsSpawned(GameObject _o)
{
return SpawnedObjects.Contains (FindView (_o));
}
public bool SpawnedLeft()
{
return SpawnedObjects.Count > 0;
}
private GameObject NextToSpawn(GameObject _type)
{Debug.Log (PooledObjects.Count);
int i = 0;
for (i = 0; i < PooledObjects.Count; i++)
{
if(PooledObjects[i].name == _type.name+"(Clone)" && !PooledObjects[i].activeSelf)
return PooledObjects[i];
}
//TODO Case: we dont have enough units in our pool
return null;
}
private GameObject NextToDespawn(GameObject _type)
{
GameObject go;
for (int i = 0; i < SpawnedObjects.Count; i++)
{
go = FindGO(SpawnedObjects[i]);
if(go.name == _type.name+"(Clone)" && !go.activeSelf)
return go;
}
return null;
}
#endregion