クライアント上でスポーンされるオブジェクトをプレハブから生成するデフォルトの挙動は、スポーンハンドラー関数を使ってカスタマイズできます。この方法により、スポーンするオブジェクトをスポーンしないオブジェクトと同様に、完全に制御できます。クライアントのスポーンする/しないオブジェクトに、 ClientScene.RegisterSpawnHandler を使って、関数を登録します。この機能を通じて、サーバーは直接オブジェクトを生成し、クライアント上にスポーンします。この関数は、オブジェクトのアセット ID を取得し、 2 つの関数に委譲します。 1 つはクライアント上でのオブジェクトの生成を扱い、 1 つはクライアント上でのオブジェクトの削除を扱います。アセット ID は動的なものにするか、(もしあるなら)スポーンさせたいプレハブオブジェクトから選んだアセット ID にすることができます。
スポーン/アンスポーン は、オブジェクトのシグネチャを持つ必要があり、高レベルAPIで定義されています。
// クライアント上でオブジェクトを生成する (Spawn) ために、リクエストを処理します
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
// クライアント上でオブジェクトを破棄する (unspawn) ために、リクエストを処理します
public delegate void UnSpawnDelegate(GameObject spawned);
アセットIDをスポーン関数に渡し、複数のプレハブの中から NetworkIdentity.assetId を使って自動で対象のプレハブを取得することができます。動的なアセットIDは次のように処理します。
// 一意の新しい assetId を生成
NetworkHash128 creatureAssetId = NetworkHash128.Parse("e2656f");
// 新しい assetId のハンドラーを設定
ClientScene.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);
// 既存のプレハブ上で assetId を取得
NetworkHash128 bulletAssetId = bulletPrefab.GetComponent<NetworkIdentity>().assetId;
// カスタム生成したい既存のプレハブのハンドラーを設定
ClientScene.RegisterSpawnHandler(bulletAssetId, SpawnBullet, UnSpawnBullet);
// 弾丸を生成 - SpawnBullet はクライアント上で呼び出されます
NetworkServer.Spawn(gameObject, creatureAssetId);
スポーン関数自身は、デリゲートのシグネチャで実装されています。ここでは、弾の生成について記述し、 SpawnCreature は同じように見えますが、生成ロジックは異なります。
public GameObject SpawnBullet(Vector3 position, NetworkHash128 assetId)
{
return (GameObject)Instantiate(m_BulletPrefab, position, Quaternion.identity);
}
public void UnSpawnBullet(GameObject spawned)
{
Destroy(spawned);
}
カスタムのスポーン関数を使用するときに、オブジェクトを破棄せずにオブジェクトのアンスポーンができるのはときどき便利なことがあります。これは、NetworkServer.UnSpawn を呼び出すことでアンスポーンが行えます。この関数はクライアントに送信され、クライアントのアンスポーンするオブジェクトへとメッセージを送信します。そしてアンスポーンの関数はクライアント上で呼び出されます。関数が呼び出されたときにオブジェクトは破棄されません。
(注)ホスト上では、オブジェクトはローカルクライアント用に Spawn されません。すでにサーバー上に存在するからです。したがって、Spawn ハンドラー関数は一切呼び出されません。
ここでは、カスタムのスポーンハンドラーとして、とてもシンプルなオブジェクトプールを実装する例を紹介します。スポーンした後、アンスポーンするだけで、プールにオブジェクトを出し入れすることができます。
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
public class SpawnManager : MonoBehaviour
{
public int m_ObjectPoolSize = 5;
public GameObject m_Prefab;
public GameObject[] m_Pool;
public NetworkHash128 assetId { get; set; }
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
public delegate void UnSpawnDelegate(GameObject spawned);
void Start()
{
assetId = m_Prefab.GetComponent<NetworkIdentity> ().assetId;
m_Pool = new GameObject[m_ObjectPoolSize];
for (int i = 0; i < m_ObjectPoolSize; ++i)
{
m_Pool[i] = (GameObject)Instantiate(m_Prefab, Vector3.zero, Quaternion.identity);
m_Pool[i].name = "PoolObject" + i;
m_Pool[i].SetActive(false);
}
ClientScene.RegisterSpawnHandler(assetId, SpawnObject, UnSpawnObject);
}
public GameObject GetFromPool(Vector3 position)
{
foreach (var obj in m_Pool)
{
if (!obj.activeInHierarchy)
{
Debug.Log("Activating object " + obj.name + " at " + position);
obj.transform.position = position;
obj.SetActive (true);
return obj;
}
}
Debug.LogError ("Could not grab object from pool, nothing available");
return null;
}
public GameObject SpawnObject(Vector3 position, NetworkHash128 assetId)
{
return GetFromPool(position);
}
public void UnSpawnObject(GameObject spawned)
{
Debug.Log ("Re-pooling object " + spawned.name);
spawned.SetActive (false);
}
}
このマネージャーを使用するには、
SpawnManager spawnManager;
void Start()
{
spawnManager = GameObject.Find("SpawnManager").GetComponent<SpawnManager> ();
}
void Update()
{
if (!isLocalPlayer)
return;
var x = Input.GetAxis("Horizontal")*0.1f;
var z = Input.GetAxis("Vertical")*0.1f;
transform.Translate(x, 0, z);
if (Input.GetKeyDown(KeyCode.Space))
{
// コマンド関数はクライアント上で呼び出されますがサーバー上で発生します
CmdFire();
}
}
[Command]
void CmdFire()
{
// Set up bullet on server
var bullet = spawnManager.GetFromPool(transform.position + transform.forward);
bullet.GetComponent<Rigidbody>().velocity = transform.forward*4;
// spawn bullet on client, custom spawn handler will be called
NetworkServer.Spawn(bullet, spawnManager.assetId);
// when the bullet is destroyed on the server it wil automatically be destroyed on clients
StartCoroutine (Destroy (bullet, 2.0f));
}
public IEnumerator Destroy(GameObject go, float timer)
{
yield return new WaitForSeconds (timer);
spawnManager.UnSpawnObject(go);
NetworkServer.UnSpawn(go);
}
上記のコードは、オブジェクトが破棄されるタイミングで破棄をキャンセルし、再使用するためにオブジェクトプールが行われます。
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.