(For new projects, you should use the new networking system introduced in 5.1. This information is for legacy projects using the old networking system.)
Ниже представлен простой пример способа, как загрузить уровень в многопользовательской игре. Он написан таким образом, что можно быть уверенным, что никакие сетевые сообщения не будут обрабатываться, пока уровень загружается. Также можно гарантировать, что никакие сообщения не будут переданы, пока всё не будет готовы. И, наконец, когда уровень загрузится, он отправит сообщение всем скриптам, чтобы они знали, что уровень загрузился и могли отреагировать на это. Функция SetLevelPrefix помогает сдерживать нежелательные обновления для нового уровня. Нежелательными обновлениями называют, например, обновления из предыдущего уровня. Пример также использует группы, чтобы отделить игровые данные и данные о загрузке уровня в разные группы. Группа Group 0 используется для игрового трафика, а группа group 1 для загрузки уровня. Группа Group 0 блокируется, пока уровень загружается, а группа group 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 ()
{
// Network level loading is done in a separate channel.
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;
// There is no reason to send any more data over the network on the default channel,
// because we are about to load the level, thus all those objects will get deleted anyway
Network.SetSendingEnabled(0, false);
// We need to stop receiving because first the level must be loaded first.
// Once the level is loaded, rpc's and other state update attached to objects in the level are allowed to fire
Network.isMessageQueueRunning = false;
// All network views loaded from a level will get a prefix into their NetworkViewID.
// This will prevent old updates from clients leaking into a newly created scene.
Network.SetLevelPrefix(levelPrefix);
Application.LoadLevel(level);
yield return;
// Allow receiving data again
Network.isMessageQueueRunning = true;
// Now the level has been loaded and we can start sending out data to clients
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# example script
var supportedNetworkLevels : String[] = [ "mylevel" ];
var disconnectedLevel : String = "loader";
private var lastLevelPrefix = 0;
function Awake ()
{
// Network level loading is done in a separate channel.
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;
// There is no reason to send any more data over the network on the default channel,
// because we are about to load the level, thus all those objects will get deleted anyway
Network.SetSendingEnabled(0, false);
// We need to stop receiving because first the level must be loaded first.
// Once the level is loaded, rpc's and other state update attached to objects in the level are allowed to fire
Network.isMessageQueueRunning = false;
// All network views loaded from a level will get a prefix into their NetworkViewID.
// This will prevent old updates from clients leaking into a newly created scene.
Network.SetLevelPrefix(levelPrefix);
Application.LoadLevel(level);
yield;
// Allow receiving data again
Network.isMessageQueueRunning = true;
// Now the level has been loaded and we can start sending out data to clients
Network.SetSendingEnabled(0, true);
for (var go in FindObjectsOfType(GameObject))
go.SendMessage("OnNetworkLoadedLevel", SendMessageOptions.DontRequireReceiver);
}
function OnDisconnectedFromServer ()
{
Application.LoadLevel(disconnectedLevel);
}
@script RequireComponent(NetworkView)
JS example script