Last active
January 26, 2022 07:26
-
-
Save su10/19fc8b1b27ebbed7ef460953a289f232 to your computer and use it in GitHub Desktop.
Photonでオブジェクトをスポーンする方法の調査およびIPunPrefabPoolとUniRx.Toolkit.ObjectPoolを使ったオブジェクトプールのサンプル ref: http://qiita.com/su10/items/4bae56bc814c4e6a2bc0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public interface IPunPrefabPool | |
{ | |
// prefabIdを受け取って新しいorプールされたインスタンスを返す必要がある | |
GameObject Instantiate(string prefabId, Vector3 position, Quaternion rotation); | |
// スポーンしたオブジェクトの破棄処理を書く | |
void Destroy(GameObject gameObject); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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