ノート: UNet は非推奨となり、今後 Unity から削除される予定です。新しいシステムが開発中です。詳細は ブログ と FAQ を参照してください。 |
クライアント上でプレハブからゲームオブジェクトをスポーンするときに、スポーンハンドラー関数を使用してデフォルトの挙動をカスタマイズできます。スポーンハンドラー関数を使うと、ゲームオブジェクトの破棄だけでなく、スポーンの方法を完全に制御することができます。
ClientScene.RegisterSpawnHandler を使って、クライアントゲームオブジェクトをスポーン、および破棄する関数を登録します。サーバーは直接ゲームオブジェクトを作成し、この機能を通じてクライアント上にスポーンします。この関数は、ゲームブジェクトのアセット ID を取り、2 つの関数にデリゲートします。1 つはクライアント上でのゲームオブジェクトの生成を処理し、1 つはクライアント上でのゲームオブジェクトの破棄を扱います。アセット ID は動的か、(もしあるなら) スポーンしたいプレハブゲームオブジェクトのアセット ID にすることができます。
スポーン/アンスポーン (un-spawn) する関数は、ゲームオブジェクトのシグネチャを持つ必要があり、高レベルAPI で定義されています。
// クライアント上でオブジェクトをスポーンするリクエストを処理します
public delegate GameObject SpawnDelegate(Vector3 position, NetworkHash128 assetId);
// クライアント上でオブジェクトをアンスポーンするリクエストを処理します
public delegate void UnSpawnDelegate(GameObject spawned);
スポーン関数に渡すアセット ID は、プレハブの場合は NetworkIdentity.assetId を使って見つけることができます。これは、自動的に表示されます。動的なアセット ID は次のように処理します。
// 一意の新しい assetId を生成
NetworkHash128 creatureAssetId = NetworkHash128.Parse("e2656f");
// 新しい assetId のハンドラーを設定
ClientScene.RegisterSpawnHandler(creatureAssetId, SpawnCreature, UnSpawnCreature);
// 既存のプレハブ上で assetId を取得
NetworkHash128 coinAssetId = coinPrefab.GetComponent<NetworkIdentity>().assetId;
// カスタムでスポーンしたい既存のプレハブのハンドラーを設定
ClientScene.RegisterSpawnHandler(coinAssetId, SpawnCoin, UnSpawnCoin);
// コインをスポーン - SpawnCoin がクライアント上で呼び出されます
NetworkServer.Spawn(gameObject, coinAssetId);
スポーン関数自身は、デリゲートのシグネチャで実装されています。これはコインのスポーンを行う関数 (coin spawner) です。SpawnCreature は同じように見えますが、スポーンのロジックは異なります。
public GameObject SpawnCoin(Vector3 position, NetworkHash128 assetId)
{
return (GameObject)Instantiate(m_CoinPrefab, position, Quaternion.identity);
}
public void UnSpawnCoin(GameObject spawned)
{
Destroy(spawned);
}
カスタムのスポーン関数を使用するときに、ゲームオブジェクトを破棄せずにゲームオブジェクトのアンスポーン (スポーンの破棄) ができるのは便利なことがあります。これは、NetworkServer.UnSpawn を呼び出すことによって行います。この関数によってメッセージがクライアントに送信され、ゲームオブジェクトをアンスポーンし、それで、カスタムのアンスポーンの関数がクライアント上で呼び出されます。この関数が呼び出されたときにオブジェクトは破棄されません。
ホスト上では、ゲームオブジェクトはローカルクライアントのためにスポーンされません。なぜなら、すでにサーバー上に存在するからです。つまり、スポーンハンドラー関数は、ホスト上では一切呼び出されません。
ここでは、カスタムのスポーンハンドラーを持つ、とてもシンプルなゲームオブジェクトプールの設定例を紹介します。スポーン/アンスポーンしてから、ゲームオブジェクトをプールに出し入れすることができます。
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 GameObject " + obj.name + " at " + position);
obj.transform.position = position;
obj.SetActive (true);
return obj;
}
}
Debug.LogError ("Could not grab GameObject 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 GameObject " + spawned.name);
spawned.SetActive (false);
}
}
このマネージャーを使うために、新しい空のゲームオブジェクトを作成し「SpawnManager」と名付けます。SpawnManager という新しいスクリプトを作成し、上のコードサンプルにコピーします。そして、それを新しい SpawnManager ゲームオブジェクトにアタッチします。次に、複数回スポーンしたいプレハブを Prefab フィールドへドラッグし、 Object Pool Size を設定します (デフォルトは 5)。
最後に、プレイヤーの移動に使用するスクリプトで SpawnManager への参照を設定します。
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()
{
// サーバー上でコインを設定
var coin = spawnManager.GetFromPool(transform.position + transform.forward);
coin.GetComponent<Rigidbody>().velocity = transform.forward*4;
// クライアント上でコインをスポーン。カスタムのスポーンハンドラーが呼び出されます。
NetworkServer.Spawn(coin, spawnManager.assetId);
// コインがサーバーで破棄されるとき、クライアント側でも自動的に破棄されます。
StartCoroutine (Destroy (coin, 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.