Skip to content

Instantly share code, notes, and snippets.

@su10
Last active January 26, 2022 07:26
Show Gist options
  • Save su10/19fc8b1b27ebbed7ef460953a289f232 to your computer and use it in GitHub Desktop.
Save su10/19fc8b1b27ebbed7ef460953a289f232 to your computer and use it in GitHub Desktop.
Photonでオブジェクトをスポーンする方法の調査およびIPunPrefabPoolとUniRx.Toolkit.ObjectPoolを使ったオブジェクトプールのサンプル ref: http://qiita.com/su10/items/4bae56bc814c4e6a2bc0
public interface IPunPrefabPool
{
// prefabIdを受け取って新しいorプールされたインスタンスを返す必要がある
GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation);
// スポーンしたオブジェクトの破棄処理を書く
void Destroy(GameObject gameObject);
}
using System.Collections.Generic;
using UnityEngine;
using System.Collections;
/// <summary>
/// Pun smart pool bridge.
/// Common Pitfalls:
/// -- even when using a pool manager, you need to store your prefab inside a Resources Folder,
/// it's because PUN needs to load the prefab and assign viewIDs to all PhotonViews. Note: It's not instantiated by pun at all, just loaded for analyzing.
/// -- on the instanciated prefab, use OnPhotonInstantiate() to catch info on initializating if you must, OnEnable and Start aren't suitable due to the network initializaion processes.
/// </summary>
public class PunSmartPoolBridge : MonoBehaviour, IPunPrefabPool
{
public void Start ()
{
PhotonNetwork.PrefabPool = this;
}
public GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation)
{
Debug.LogWarning("Instantiate Prefab: " + prefabId);
GameObject go = SmartPool.Spawn(prefabId);
go.transform.position = position;
go.transform.rotation = rotation;
return go;
}
public void Destroy(GameObject gameObject)
{
SmartPool.Despawn(gameObject);
}
}
void SpawnPlayerEverywhere()
{
// You must be in a Room already
// Marco Polo tutorial shows how to connect and join room
// See: http://doc.photonengine.com/en/pun/current/tutorials/tutorial-marco-polo
// Manually allocate PhotonViewID
int id1 = PhotonNetwork.AllocateViewID();
PhotonView photonView = this.GetComponent<PhotonView>();
photonView.RPC("SpawnOnNetwork", PhotonTargets.AllBuffered, transform.position, transform.rotation, id1, PhotonNetwork.player);
}
public Transform playerPrefab; //set this in the inspector
[RPC]
void SpawnOnNetwork(Vector3 pos, Quaternion rot, int id1, PhotonPlayer np)
{
Transform newPlayer = Instantiate(playerPrefab, pos, rot) as Transform;
// Set player's PhotonView
PhotonView[] nViews = newPlayer.GetComponentsInChildren<PhotonView>();
nViews[0].viewID = id1;
}
using Photon;
using PhotonRx;
using System.Linq;
using UniRx;
using UniRx.Toolkit;
using UniRx.Triggers;
using UnityEngine;
namespace Jagapippi.Network
{
public abstract class NetworkSpawner<T> : SingletonMonoBehaviour<NetworkSpawner<T>> where T : PunBehaviour
{
protected ObjectPoolAdapter _objectPoolAdapter;
protected virtual ObjectPoolAdapter objectPoolAdapter
{
get
{
if (_objectPoolAdapter == null)
{
_objectPoolAdapter = CreateObjectPoolAdapter();
_objectPoolAdapter.OnPhotonInstantiateAsObservable()
.Subscribe(instance => _ComponentCollection.Add(instance))
.AddTo(this);
_objectPoolAdapter.OnReturnAsObservable()
.Subscribe(instance => _ComponentCollection.Remove(instance))
.AddTo(this);
}
return _objectPoolAdapter;
}
}
protected abstract ObjectPoolAdapter CreateObjectPoolAdapter();
#region properties
[SerializeField] protected GameObject _prefab;
[SerializeField] protected bool _usePool = true;
protected readonly ReactiveCollection<T> _ComponentCollection = new ReactiveCollection<T>();
public IReadOnlyReactiveCollection<T> ComponentCollection
{
get { return _ComponentCollection; }
}
#endregion
protected virtual void Awake()
{
if (PhotonNetwork.PrefabPool == null)
{
PhotonNetwork.PrefabPool = new ObjectPoolManager();
}
var objectPoolManager = (ObjectPoolManager) PhotonNetwork.PrefabPool;
objectPoolManager.AddObjectPool(this.objectPoolAdapter);
this.OnDestroyAsObservable()
.Subscribe(_ =>
{
objectPoolManager.RemoveObjectPool(this.objectPoolAdapter);
this.objectPoolAdapter.Clear();
});
}
#region Spawn methods
public static void Spawn()
{
instance.Spawn(Vector3.zero, Quaternion.identity);
}
public static void Spawn(Transform pos)
{
instance.Spawn(pos.position, pos.rotation);
}
public static void Spawn(Vector3 position)
{
instance.Spawn(position, Quaternion.identity);
}
public static void Spawn(Quaternion rotation)
{
instance.Spawn(Vector3.zero, rotation);
}
public virtual void Spawn(Vector3 position, Quaternion rotation, params object[] args)
{
PhotonNetwork.Instantiate(_prefab.name, position, rotation, 0, args);
}
public static void SpawnSceneObject()
{
instance.SpawnSceneObject(Vector3.zero, Quaternion.identity);
}
public static void SpawnSceneObject(Transform pos)
{
instance.SpawnSceneObject(pos.position, pos.rotation);
}
public static void SpawnSceneObject(Vector3 position)
{
instance.SpawnSceneObject(position, Quaternion.identity);
}
public static void SpawnSceneObject(Quaternion rotation)
{
instance.SpawnSceneObject(Vector3.zero, rotation);
}
public virtual void SpawnSceneObject(Vector3 position, Quaternion rotation, params object[] args)
{
PhotonNetwork.InstantiateSceneObject(_prefab.name, position, rotation, 0, args);
}
#endregion
public static void Destroy(PhotonView photonView)
{
if (photonView == null
|| photonView.isMine == false
|| instance.ComponentCollection.Contains(photonView.gameObject.GetComponent<T>()) == false
)
{
Debug.LogWarning("tried to destroy invalid PhotonView!");
return;
}
PhotonNetwork.Destroy(photonView);
}
protected abstract class ObjectPool : ObjectPool<T>
{
protected ObjectPool(GameObject prefab, bool usePool)
{
_prefab = prefab;
_usePool = usePool;
}
private readonly GameObject _prefab;
private readonly bool _usePool = true;
protected GameObject prefab
{
get { return _prefab; }
}
protected bool usePool
{
get { return _usePool; }
}
public virtual bool HasPrefab(string prefabId)
{
// TODO: 名前以外の衝突しないIDで照合する
return _prefab.name == prefabId;
}
protected override T CreateInstance()
{
// TODO: Awake時点でのPhotonViewの初期化を保証する
return Instantiate(this.prefab).GetComponent<T>();
}
public new virtual void Return(T instance)
{
base.Return(instance);
if (this.usePool == false)
{
this.Clear();
}
}
}
protected class ObjectPoolAdapter : ObjectPoolManager.ObjectPool
{
private readonly ObjectPool _objectPool;
public ObjectPoolAdapter(ObjectPool objectPool)
{
_objectPool = objectPool;
}
private readonly Subject<T> _onPhotonInstantiate = new Subject<T>();
public IObservable<T> OnPhotonInstantiateAsObservable()
{
return _onPhotonInstantiate;
}
private readonly Subject<T> _onReturn = new Subject<T>();
public IObservable<T> OnReturnAsObservable()
{
return _onReturn;
}
public override Component Rent()
{
var instance = _objectPool.Rent();
// TODO: すべてのOnPhotonInstantiateが呼ばれてからOnNext()する
instance.OnPhotonInstantiateAsObservable()
.Take(1)
.TakeUntil(instance.OnDisableAsObservable())
.Subscribe(_ => _onPhotonInstantiate.OnNext(instance))
.AddTo(instance);
return instance;
}
public override bool Return(GameObject gameObject)
{
var instance = gameObject.GetComponent<T>();
if (instance && instance.GetType().IsSubclassOf(typeof(T)) == false)
{
_onReturn.OnNext(instance);
_objectPool.Return(instance);
return true;
}
return false;
}
public override bool HasPrefab(string prefabId)
{
return _objectPool.HasPrefab(prefabId);
}
public virtual void Clear()
{
_objectPool.Clear();
}
}
}
}
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
namespace Jagapippi.Network
{
public class ObjectPoolManager : IPunPrefabPool
{
private readonly List<ObjectPool> _objectPoolList = new List<ObjectPool>();
public void AddObjectPool(ObjectPool _objectPool)
{
if (_objectPoolList.Contains(_objectPool) == false)
{
_objectPoolList.Add(_objectPool);
}
}
public void RemoveObjectPool(ObjectPool _objectPool)
{
_objectPoolList.Remove(_objectPool);
}
GameObject IPunPrefabPool.Instantiate(string prefabId, Vector3 position, Quaternion rotation)
{
var objectPool = _objectPoolList.First(pool => pool.HasPrefab(prefabId));
var go = objectPool.Rent().gameObject;
go.transform.SetPositionAndRotation(position, rotation);
return go;
}
void IPunPrefabPool.Destroy(GameObject gameObject)
{
foreach (var objectPool in _objectPoolList)
{
if (objectPool.Return(gameObject))
{
return;
}
}
}
public abstract class ObjectPool
{
public abstract bool HasPrefab(string prefabId);
public abstract Component Rent();
public abstract bool Return(GameObject gameObject);
}
}
}
using UniRx;
using UnityEngine;
namespace Jagapippi.Network.Game
{
public class PlayerSpawner : NetworkSpawner<Player>
{
private readonly ReactiveProperty<Player> _LocalPlayer = new ReactiveProperty<Player>();
public ReadOnlyReactiveProperty<Player> LocalPlayer
{
get { return _LocalPlayer.ToReadOnlyReactiveProperty(); }
}
public IReadOnlyReactiveCollection<Player> PlayerList
{
get { return _ComponentCollection; }
}
protected override void Awake()
{
base.Awake();
this.PlayerList.ObserveAdd()
.Select(e => e.Value)
.Subscribe(player => PhotonPlayer.Find(player.photonView.ownerId).TagObject = player)
.AddTo(this);
this.PlayerList.ObserveAdd()
.Select(e => e.Value)
.Where(player => player.photonView.isMine)
.Subscribe(player => _LocalPlayer.Value = player)
.AddTo(this);
this.PlayerList.ObserveRemove()
.Select(e => e.Value)
.Where(player => player.photonView.isMine)
.Subscribe(player => _LocalPlayer.Value = null)
.AddTo(this);
}
protected override ObjectPoolAdapter CreateObjectPoolAdapter()
{
return new ObjectPoolAdapter(new PlayerObjectPool(_prefab, _usePool));
}
protected class PlayerObjectPool : ObjectPool
{
public PlayerObjectPool(GameObject prefab, bool usePrefab) : base(prefab, usePrefab)
{
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment