unity网络通信

2015年01月17日 11:01 0 点赞 0 评论 更新于 2017-05-08 18:20

在Unity开发中,我们需要掌握多方面的知识,不仅仅局限于游戏开发本身,网络通信也是重要的一环。今天,我们就来深入了解Unity网络通信。

Unity网络功能概述

首先要明确的是,Unity自带的网络功能不太适合开发MMO类型的游戏。若要将Unity用作MMO游戏的客户端,通常会在C#中通过socket建立自定义的网络通信。Unity自身的网络功能是为多人游戏设计的,其常见模式是一个玩家创建游戏(该玩家兼具服务器和客户端的身份),其他玩家连接进来。从带宽角度考虑,一般同时支持的玩家数量少于64个(具体数量取决于游戏设计)。这些玩家通常在局域网内互联,但借助MasterServer进行配对,也能在互联网上相互连接。许多所谓的单机且支持多人连线的游戏基本采用这种网络模式。此外,Unity的网络功能具备跨平台特性,可在PC、IOS、Android等平台运行,并且支持不同平台的客户端之间进行通信。

网络协议

Unity网络协议是一种相对高层的网络协议,它并非独立存在,而是与引擎的游戏对象紧密结合,为游戏提供网络功能支持。要理解Unity的网络协议,需先了解网络模式下游戏对象的管理机制。

网络游戏对象管理

在Unity网络模式下,创建游戏对象实例通过Network.Instantiate()函数实现。此函数与Object.Instantiate()类似,会在本地创建游戏对象实例,同时在所有互联的主机上创建相同的游戏对象实例,这些游戏对象拥有相同的NetworkView ID。每个创建的游戏对象使用NetworkView.isMine来标识自身身份。若isMinetrue,表示该游戏对象由本机创建;若为false,则表示由其他主机创建并同步到本机。游戏对象的isMine身份与网络底层的服务器和客户端身份无关,服务器和客户端上均可存在不同身份的游戏对象,这主要取决于游戏对象的创建者。若要销毁游戏对象,应调用Network.Destroy()替代Object.Destroy(),这样会在所有主机上销毁该对象。

基于游戏对象的网络协议

在Unity中,不存在单纯的“发送给服务器”或“发送给客户端”的网络消息。Unity的网络协议是基于具体游戏对象的通信协议,这意味着所有通信上下文都与特定的游戏对象相关。一旦在网络环境下创建了游戏对象,各个主机上创建的游戏对象之间就建立了通信关系,任何一个主机上的游戏对象都能与其他主机上的“自己”进行通信。不同游戏对象之间的通信相互独立,如同每个游戏对象都有独立的通信管道。

游戏对象之间的通信形式主要分为以下两种:

  • 状态同步:指游戏对象中经常变化且可描述为对象属性的数据。状态同步的方向只能从isMine到非isMine。例如,玩家角色对象的hp属性,若要让其他玩家看到,可使用状态同步实现。当isMine的玩家角色的hp发生变化时,会自动将该属性更新到其他主机上的同一玩家角色对象。状态同步还支持保证和非保证模式。
  • RPC(远程过程调用):用于在其他主机的相同游戏对象上执行远程函数调用。RPC通常用于通过网络通知一次性的游戏事件,如“游戏对象A受到了30点伤害”等。RPC的传输方向非常灵活,可以在一个主机向任何其他主机(包括自身)发送RPC调用。

网络游戏对象管理、状态同步和RPC共同构成了Unity的网络协议,所有多人游戏的网络功能都构建在这套协议基础之上。

使用Unity构建多人游戏

传递游戏对象引用

在构建多人游戏时,除了通过网络传递基本数据类型(如intstring等),有时还需要传递游戏对象的引用。例如,游戏对象A通过RPC通知其他主机它消灭了游戏对象B,这里的B就是游戏对象的引用。要传递这种引用,可利用NetworkView.viewIDNetworkView.viewIDNetworkViewID类型的对象,RPC本身支持传输该数据类型。发送方只需将B的NetworkView.viewID作为RPC的参数传递给接收方,接收方再通过NetworkView.Find()在本地将viewID转换为对应的游戏对象引用即可。

全局对象

在单机游戏中,场景通常会创建一个全局对象来处理整体游戏状态,如关卡完成情况、全局事件处理等。在多人游戏中,同样需要这样的功能。一般来说,可在服务器端将全局对象创建为网络游戏对象,并复制给其他客户端。全局对象主要用于处理以下功能:

  • 新连接的处理:当客户端连接到服务器后,全局对象会自动创建到该客户端。客户端可使用基于该全局对象的一个RPC作为第一条发送给服务器的消息,此消息通常包含自身身份和游戏信息。服务器处理该RPC以实现游戏层面的玩家登录。
  • 同步场景切换:当游戏需要所有玩家同时切换场景时,由全局对象进行通知和同步。
  • 游戏状态管理和同步:全局游戏对象通常在服务器端运行游戏逻辑,并将客户端需要知晓的游戏状态和事件通知给所有客户端。

融合单人游戏

多人游戏通常具备单人模式,且很多游戏逻辑相同。在编写游戏逻辑代码时,除必要的网络功能代码外,应尽量模糊单人与多人游戏的区别,以提高代码的可理解性和可维护性。我们可将单人游戏视为只有一个本地客户端的多人游戏,并屏蔽网络功能。

以下是实现相关功能的代码示例:

使用NetworkViewWrapper代替直接使用NetworkView

using UnityEngine;
using System.Collections;

/** 包装NetworkView,使之能够兼容非网络模式. */
[RequireComponent(typeof(NetworkView))]
public class NetworkViewWrapper : MonoBehaviour
{
public int group
{
get { return networkView.group; }
set { networkView.group = value; }
}

public bool isMine
{
get
{
if(Network.peerType == NetworkPeerType.Disconnected)
return true;
return networkView.isMine;
}
}

public Component observed
{
get { return networkView.observed; }
set { networkView.observed = value; }
}

public NetworkPlayer owner
{
get { return networkView.owner; }
}

public NetworkStateSynchronization stateSynchronization
{
get { return networkView.stateSynchronization; }
set { networkView.stateSynchronization = value; }
}

public NetworkViewID viewID
{
get { return networkView.viewID; }
set { networkView.viewID = value; }
}

public static NetworkViewWrapper Find(NetworkViewID viewID)
{
NetworkView nvw = NetworkView.Find(viewID);
return (nvw == null) ? null : nvw.GetComponent<NetworkViewWrapper>();
}

public void RPC(string name, NetworkPlayer target, params object[] args)
{
if (Network.peerType == NetworkPeerType.Disconnected)
return;
networkView.RPC(name, target, args);
}

public void RPC(string name, RPCMode mode, params object[] args)
{
if (Network.peerType == NetworkPeerType.Disconnected)
return;
networkView.RPC(name, mode, args);
}
}

使用NetObject封装对象的创建和销毁

using UnityEngine;
using System.Collections;

public static class NetObject
{
public static Object instantiate(Object prefab, Vector3 position, Quaternion rotation)
{
if (Network.peerType == NetworkPeerType.Disconnected)
return Object.Instantiate(prefab, position, rotation);
else
return Network.Instantiate(prefab, position, rotation, 0);
}

public static void destroy(GameObject gameObject)
{
if (Network.peerType == NetworkPeerType.Disconnected)
Object.Destroy(gameObject);
else
Network.Destroy(gameObject);
}
}

作者信息

feifeila

feifeila

共发布了 3994 篇文章