文章目录
前言
本文开始,介绍unity网络通信组件应用,先来一个用户手册的范例。用户手册上给出了一个简单的范例,并指出可以以此为基础进行相开发,再次对范例进行精讲。(NetworkManager对使用unity的轻量级游戏开发有很大帮助,大大解决了开发时间,但是对于吃鸡、农药等游戏估计其整个消息传递机制还是自定义的(此处为猜测))。不做赘述,立马开始(完整脚本均在文末):
1. 使用流程
1.1 挂载NetworkManager
如果用过很多插件的,会知道,在使用一个插件之前肯定要挂载一个manager或者controller,NetworkManager也一样,需要关在NetworkManager进行组建数据初始化等。所以新建游戏物体,并添加NetworkManager组件并勾选Dont Destroy on Load。参数中除了network address和port以外,需要对Spawn Info进行一下简单解释。Spawn表示网路范围内产生的游戏物体(根据字面意思以及unity测试自己定义的),比如吃鸡游戏,自己控制人物在别人的场景中也存在,这就是一个Spawn。Player prefab玩家预制,即游戏玩家控制的角色,如果是聊天软件则可以是聊天窗口。RegisteredSpawnablePrefabs表示对需要对网络范围内产生的游戏物体进行注册,如子弹,你打出的子弹需要对方也可以收到,就需要在网络范围内(即所有玩游戏的人场景)产生,他跟Player prefab玩家预制本质上是一样的。
1.2 挂载NetworkManagerHUD
在NetworkManager所在的游戏物体挂在此组件,此组件为unity自定义的简单UI,运行时会有四个选项:
LANHost:局域网范围内,本机为主机(服务端),同时本机也为客户端。
LANServer:局域网范围内,本机只为主机。
LANClient:局域网范围内,本机为客户端。
Enable。。:(用户手册解释为网络端(外网),并未进行相关测试。点进去就可以进行相关外网连接设置)括号内的解释不严谨,会在下一篇中详细解释
在此说明一下:LANHost与LANServer区别在于本身是否存在一个客户端,这个对于玩儿过CS1.5/1.6的人来说好理解,因为局域网需要一个人见主机,其他人加入,同时它本身也要参与游戏,目前的FPS游戏均为局域网范围外,所以本身只是客户端。
1.3 生成玩家
添加一个3D游戏物体capsule,并生成预制体,作为一个间的游戏玩家。并拖到NetWorkManager中的PlayerPrefab槽中。对此游戏物体添加NetworkIdentity组件,此组件用来标识游戏玩家身份,只要是场景中每个客户端在交互中实时生成的并可以被广泛看到的均需要此组件。此组件有两个参数,一个人ServerOnly一个是LocalPlayer权限,表示此游戏物是本地有控制权限还是服务端有控制权限(因为服务端和客户端会均存在一个相同的游戏物体,理论上都可以进行控制,所以要对此进行一下区分,后续会讲到怎么处理)。如果需要实时更新位置信息的,则还需要NetWorkTransform组件,来试试更新位置。
1.4 添加脚本PlayerMove(控制移动开火等)
完整脚本在最后端,直接添加脚本并在脚本中添加如下代码:
float x = Input.GetAxis("Horizontal") * moveSpeed;
float z = Input.GetAxis("Vertical") * moveSpeed;
//transform.Translate(new Vector3(x, 0, z));
GetComponent<CharacterController>().Move(new Vector3(x, 0, z));
注:原程序用的transform.translate,本文通过添加角色控制器来移动
打包一个程序,运行选择客户端,editor运行选择LanHost。会发现两个场景中均存在两个游戏物体,移动游戏物体则两个游戏物体均移动。而我们需要的是只移动本客户端控制的游戏物体。所以需要作如下修改:
把MonoBehaviour换成NetworkBehaviour(后者继承在前者),然后通过NetworkBehaviour中的isLocalPlayer参数来来判断是否为本地运行,此部分代码如下:
private void Update()
{
if (isLocalPlayer)
{
PlayerMove();
}
}
private void PlayerMove()
{
float x = Input.GetAxis("Horizontal") * moveSpeed;
float z = Input.GetAxis("Vertical") * moveSpeed;
//transform.Translate(new Vector3(x, 0, z));
GetComponent<CharacterController>().Move(new Vector3(x, 0, z));
}
下面为添加子弹:
新建一个球形游戏物体,调整一下初始位置和大小,添加NetworkIdentity和NetWorkTransform组件,并生成预制体,把此预制体拖动到NetworkManager中的RegisteredSpawnablePrefabs中进行注册。当按下F键时表示开火,在update中添加如下代码:
if (Input.GetKeyDown(KeyCode.F))
{
GameObject playerBullet = Instantiate(bullet, (4*transform.forward + transform.position), Quaternion.identity);
playerBullet.GetComponent<Rigidbody>().velocity = playerBullet.transform.forward * bulletSpeed;
}
运行测试会发现,客户端的两个游戏物体均发射子弹,而且服务端无子弹生成,若增加第二个客户端,则第二个客户端也不会生成子弹。代码放在isLocalPlayer触发的模块下可解决均发射子弹的问题。第二个问题则是client产生游戏物体不会在服务端产生也不会在其他client端产生。如果自己编写消息机制,则需要发送消息告诉服务端和其他client,产生子弹,但是但是Unet为我们提供好了解决方案,即通过[Command](表示此方法客户端调用,服务端执行,对应的方法开头为Cmd;对应的[ClientRpc]表示服务端调用,客户端执行)让服务端去执行发射子弹命令,并通过NetworkServer.Spawn方法在每个客户端生成代码如下:
[Command]
private void CmdFire()
{
GameObject playerBullet = Instantiate(bullet, (4*transform.forward + transform.position), Quaternion.identity);
playerBullet.GetComponent<Rigidbody>().velocity =</