NetworkManager
NetworkManager是一个组件,用来管理网络多人游戏的状态。它实际上是完全使用HLAPI实现,所以它所做的一切程序员可以使用其他方式实现。然而,NetworkManager封装好了很多有用的功能到一个地方,使创建、运行和调试多人游戏尽可能简单。
NetworkManager可以使用使其完全没有脚本。在编辑器的检视面板,允许配置它的所有特性。NetworkManagerHUD供应一个简单的、默认的用户界面,允许网络游戏在运行时由用户控制。更先进的用法,开发人员可以从派生一个类继承自NetworkManager,并且自定义其行为通过重写任何它所提供的虚拟函数。
NetworkManager所具备的属性包括如下:
- 游戏状态管理
- 派生管理
- 场景管理
- Debugging信息
- 比赛选择(Matchmaker)
- 用户自定义
开始使用NetworkManager
NetworkManager可以作为核心控制组件在多人在线游戏中。开始,创建一个空的游戏对象在你的场景开始,或选择一个方便管理的对象。然后加入NetworkManager组件。
在编辑器的检视面板允许您配置和控制很多事情与网络有关。
NetworkManagerHUD是另一个组件,它需要跟NetworkManager一起工作。它给你提供了一个简单的用户界面,显示游戏运行控制网络状态。这是好的开始使用一个网络项目,但不是作为一个游戏的最终的UI。
游戏状态管理
网络多人游戏可以运行在三种模式——作为一个客户,作为一个专用的服务器,或者作为“主机”(同时是客户端和服务器)。连网是为了实现同样的游戏代码和资产在所有这些情况下工作。发展为单人的游戏版本和多人的游戏版本应该是一样的。
NetworkManager提供了方法进入每一个模式。
- NetworkManager.StartClient()
- NetworkManager.StartServer()
- NetworkManager.StartHost()
都可用于脚本代码,所以他们可以从键盘输入调用处理程序或自定义的用户界面。默认运行时控件还可以显示调用这些函数。也有按钮可用NetworkManagerHUD检查员在播放模式,调用相同的功能。
Whatever method is used to change the game state, the properties networkAddress and networkPort are used. When a server or host is started, networkPort becomes the listen port. When a client is started, networkAddress is the address to connect to, and networkPort is the port to connect to.
无论使用何种方法来改变游戏状态,使用networkAddress和networkPort属性。当服务器或主机启动时,networkPort变得监听端口。当客户端启动,networkAddress地址连接,networkPort端口连接。
派生(网络实例化)管理
In addition to the player prefab, the prefabs of other objects that will be dynamically spawned must be registered with the ClientScene. This can be done with the ClientScene.RegisterPrefab() functions, or it can be done by the NetworkManager automatically. Adding prefabs to the spawn list will make them be auto-registered. The spawn configuration section of the NetworkManager inspector looks like:
NetworkManager可以用于管理从预制实例化网络对象。大多数游戏都有一个预设体作为主角对象,所以NetworkManager有一个位置使其拖动玩家预设体。当一个设置了一个玩家预设体,玩家对象将自动网络实例化物体在每个用户在游戏中。这适用于主机上的本地玩家,以及在远程客户端的远程玩家。注意,玩家预设体必须添加NetworkIdentity组件。
除了玩家预设体之外,其他的预设体对象想动态地产生必须在ClientScene中注册。这可以用ClientScene.RegisterPrefab()函数,也可以是由NetworkManager自动完成的。将预制体添加到列表中会让他们被自动注册。
一旦玩家预设体设置之后,应该能够在开始游戏主机中看到玩家对象了。停止游戏应该看到玩家对象被销毁。运行游戏的另一个副本和作为一个客户端连接到localhost应该让另一个玩家对象出现,和停止,客户应客户的player对象被销毁。
玩家对象产生是调用了NetworkManager的默认方法:OnServerAddPlayer。如果你想定制玩家创建对象的方式,您可以重写虚函数。默认实现是这样的:
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
注意NetworkServer.AddPlayerForConnection()函数必须用于创建新的玩家对象,这样就建立了与客户端的联系。这将生成对象,所以NetworkServer.Spawn 不需要在玩家物体上调用。
开始位置
控制玩家在哪里产生,你可以使用NetworkStartPosition组件。NetworkManager寻找场景中NetworkStartPosition对象,如果它发现任何一个,那么它将在该位置产生玩家并且应用其中一个。自定义代码可以访问的可用NetworkStartPositions使列表通过NetworkManager.startPositions,还有一个帮助函数GetStartPosition()在NetworkManager中可用于实现OnServerAddPlayer找到一个起始位置。
场景管理
大多数游戏有多个场景。通常在屏幕至少有一个标题或开始菜单场景添加到实际的游戏场景中。NetworkManager设置了自动管理场景状态和场景切换方式功能适用于一个多人游戏。NetworkManager有两个槽,offlineScene和onlineScene。将场景拖到槽中可以激活网络场景管理功能。
当服务器或主机启动时,onlineScene场景将被加载。这将成为当前网络场景。任何连接到服务器的客户端也会加载这个场景。这个场景的名称存储在networkSceneName属性中。
当网络停止时(服务器或主机停止了),或由客户端断开,离线场景将被加载。这使得游戏断开时自动回到菜单场景。
我们还可以在游戏中更改场景,通过调用方法NetworkManager.ServerChangeScene()。这将使所有当前连接的客户端的变化场景,并将更新networkSceneName这样新客户也会加载到新场景。
当网络场景管理激活时,任何调用游戏状态管理功能会导致场景的变化,例如:NetworkManager.StartHost()或NetworkManager.StopClient(),这适用于运行时控制UI。通过设置场景和调用这些函数很容易控制多人在线游戏的场景同步。
注意,场景变化导致所有场景中的对象被销毁。这可能包括NetworkManager!如果你想使其在场景切换过程中不销毁,确保勾选“Dont Destroy On Load”设置为true。在简单的情况下,这是最好的配置。但是,可以使其在每一个场景都有不同的NetworkManager来控制增量加载预制,或不同的场景转换。
调试信息
NetworkManagerHUD属性面板显示网络在运行时的状态额外的信息。这包括:
- 网络连接
- 服务器端:NetworkIdentity激活的对象
- 客户端:NetworkIdentity激活的对象
- 同时在线客户端
比赛安排(Matchmaking)
NetworkManager运行时UI和NetworkManager检视面板UI允许与matchmaker server交互。函数NetworkManager.StartMatchmaker()使连接,并保存在NetworkManager.matchmaker属性中。一旦被激活时,会改变默认的UI并执行回调函数使其简单的完成matchmaking。
可以继承NetworkManger重写虚方法,实现自定义回调。
用户自定义(Customization)
There are virtual functions on NetworkManager that derived classes can use to customize behaviour. When implementing these functions, be sure to take care of the functionality that the default implementations provide. For example, in OnServerAddPlayer(), the function NetworkServer.AddPlayer must be called to active the player object for the connection.
Functions invoked on the Server/Host:
继承NetworkManager之后用户可以根据需要重写很多虚方法。重写这些方法时,一定要注意方法原本实现的内容。例如,OnServerAddPlayer()函数, 必须调用方法NetworkServer。AddPlayer实现玩家对象的连接。
服务器/主机上调用的方法有:
// 当有客户端连接时调用
public virtual void OnServerConnect(NetworkConnection conn);
// 当有客户端断开时调用
public virtual void OnServerDisconnect(NetworkConnection conn)
{
NetworkServer.DestroyPlayersForConnection(conn);
}
// 当有客户端准备(Ready)时调用
public virtual void OnServerReady(NetworkConnection conn)
{
NetworkServer.SetClientReady(conn);
}
// 当生成对象到客户端时调用
public virtual void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
var player = (GameObject)GameObject.Instantiate(playerPrefab, playerSpawnPos, Quaternion.identity);
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
// 当玩家从客户端销毁时调用
public virtual void OnServerRemovePlayer(NetworkConnection conn, short playerControllerId)
{
PlayerController player;
if (conn.GetPlayer(playerControllerId, out player))
{
if (player.NetworkIdentity != null && player.NetworkIdentity.gameObject != null)
NetworkServer.Destroy(player.NetworkIdentity.gameObject);
}
}
// 当服务器发生错误时调用
public virtual void OnServerError(NetworkConnection conn, int errorCode);
在客户端调用的方法:
// 当连接到服务器时调用
public virtual void OnClientConnect(NetworkConnection conn)
{
ClientScene.Ready(conn);
ClientScene.AddPlayer(0);
}
// 当断开服务器时调用
public virtual void OnClientDisconnect(NetworkConnection conn)
{
StopClient();
}
// 当网络问题时调用
public virtual void OnClientError(NetworkConnection conn, int errorCode);
// 告诉服务器没准备好时调用
public virtual void OnClientNotReady(NetworkConnection conn);
Matchmaker会调用的方法:
//当有一个比赛创建
public virtual void OnMatchCreate(CreateMatchResponse matchInfo)
//当接收到比赛列表时调用
public virtual void OnMatchList(ListMatchResponse matchList)
//当假如比赛时调用
public void OnMatchJoined(JoinMatchResponse matchInfo)
===================================================================