(Unity 5.1 からは UNET の使用を推奨しています。下記の情報は旧ネットワークシステムのものです。)
以下でマルチプレイヤーゲームでレベルロードする簡単なサンプルを示します。レベルロードの途中でネットワークメッセージの処理が行われないことをチェックしてます。さらにすべて準備が整った後でないかぎりメッセージ送信が行われないこともあわせてチェックしてます。最後にレベルロード完了時にすべてのスクリプトにメッセージ送信を行いレベルロードが終わってなんらかの処理を受け付けできることを示します。SetLevelPrefix 関数により新しくロードしたレベルに不要なネットワーク更新が行われないようにします。不要な更新とは例えば前のレベルでの更新です。このサンプルではグループを使用してゲームデータとグループへのレベルロード通信を分離します。グループ 0 はゲームデータのトラフィックで使用されグループ 1 はレベルロードに使用します。レベルロードの途中ではグループ 0 はブロックされますがグループ 1 は開放されていて、レベルロードの際にチャット通信も別に行うことができるよう開放されてます。
using UnityEngine;
using UnityEngine.Network;
using System.Collections;
[RequireComponent(NetworkView)]
public class ExampleScript : MonoBehaviour {
string[] supportedNetworkLevels = new[]{ "mylevel" };
string disconnectedLevel = "loader";
int lastLevelPrefix = 0;
NetworkView networkView;
void Awake ()
{
// ネットワークレベルの読み込みは、別のチャンネルで行われます
DontDestroyOnLoad(this);
networkView = new NetworkView ();
networkView.group = 1;
Application.LoadLevel(disconnectedLevel);
}
void OnGUI ()
{
if (Network.peerType != NetworkPeerType.Disconnected)
{
GUILayout.BeginArea(Rect(0, Screen.height - 30, Screen.width, 30));
GUILayout.BeginHorizontal();
foreach (var level in supportedNetworkLevels)
{
if (GUILayout.Button(level))
{
Network.RemoveRPCsInGroup(0);
Network.RemoveRPCsInGroup(1);
networkView.RPC( "LoadLevel", RPCMode.AllBuffered, level, lastLevelPrefix + 1);
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
}
[RPC]
IEnumerator LoadLevel (string level, int levelPrefix)
{
lastLevelPrefix = levelPrefix;
// デフォルトチャンネルのネットワークで、これ以上データを送信する必要はありません。
// なぜなら、今、レベルを読み込もうとしているところで、すべてのオブジェクトはどのみち削除されるからです。
Network.SetSendingEnabled(0, false);
// レベルが最初に読み込まれるべきなので、受信を停止します。
// レベルが読み込まれたら、そのレベルのオブジェクトにアタッチされたRPCと他のステートの更新を行うことができます。
Network.isMessageQueueRunning = false;
// レベルから読み込まれたすべてのネットワークビューはNetworkViewID にプレフィックスを得ます。
// これで、クライアントからの古い更新が新しく作成されたシーンに漏出することを防ぎます。
Network.SetLevelPrefix(levelPrefix);
Application.LoadLevel(level);
yield return;
// データの受信を再び可能にします。
Network.isMessageQueueRunning = true;
// レベルが読み込まれ、クライアントにデータを送信し始めることができます。
Network.SetSendingEnabled(0, true);
var gameObjects = FindObjectsOfType(GameObject) as GameObject[];
foreach (var go in gameObjects)
go.SendMessage("OnNetworkLoadedLevel", SendMessageOptions.DontRequireReceiver);
}
void OnDisconnectedFromServer ()
{
Application.LoadLevel(disconnectedLevel);
}
}
C# スクリプトの例
var supportedNetworkLevels : String[] = [ "mylevel" ];
var disconnectedLevel : String = "loader";
private var lastLevelPrefix = 0;
function Awake ()
{
//ネットワークレベルの読み込みは、別のチャンネルで行われます
DontDestroyOnLoad(this);
networkView.group = 1;
Application.LoadLevel(disconnectedLevel);
}
function OnGUI ()
{
if (Network.peerType != NetworkPeerType.Disconnected)
{
GUILayout.BeginArea(Rect(0, Screen.height - 30, Screen.width, 30));
GUILayout.BeginHorizontal();
for (var level in supportedNetworkLevels)
{
if (GUILayout.Button(level))
{
Network.RemoveRPCsInGroup(0);
Network.RemoveRPCsInGroup(1);
networkView.RPC( "LoadLevel", RPCMode.AllBuffered, level, lastLevelPrefix + 1);
}
}
GUILayout.FlexibleSpace();
GUILayout.EndHorizontal();
GUILayout.EndArea();
}
}
@RPC
function LoadLevel (level : String, levelPrefix : int)
{
lastLevelPrefix = levelPrefix;
// デフォルトチャンネルのネットワークで、これ以上データを送信する必要はありません。
// なぜなら、今、レベルを読み込もうとしているところで、すべてのオブジェクトはどのみち削除されるからです。
Network.SetSendingEnabled(0, false);
// レベルが最初に読み込まれるべきなので、受信を停止します。
// レベルが読み込まれたら、そのレベルのオブジェクトにアタッチされたRPCと他のステートの更新を行うことができます。
Network.isMessageQueueRunning = false;
// レベルから読み込まれたすべてのネットワークビューはNetworkViewID にプレフィックスを得ます。
//これで、クライアントからの古い更新が新しく作成されたシーンに漏出することを防ぎます。
Network.SetLevelPrefix(levelPrefix);
Application.LoadLevel(level);
yield;
// データの受信を再び可能にします。
Network.isMessageQueueRunning = true;
// レベルが読み込まれ、クライアントにデータを送信し始めることができます。
Network.SetSendingEnabled(0, true);
for (var go in FindObjectsOfType(GameObject))
go.SendMessage("OnNetworkLoadedLevel", SendMessageOptions.DontRequireReceiver);
}
function OnDisconnectedFromServer ()
{
Application.LoadLevel(disconnectedLevel);
}
@script RequireComponent(NetworkView)
JS スクリプトの例