GJM : Unity3D

               介绍

在本系列教程中,我们将使用的网络高级API(HLAPI)来构建一个小型的多人网络案例。即使我们的例子很简单,但也会涵盖以下关键概念,这应该可以帮助大家使用HLAPI构建大型游戏项目。

    • 在第一部分,我们将使用NetworkBehaviours,SyncVars和Commands来介绍客户端和服务器之间的通信。
    • 在第二部分,我们将实现客户端预测和服务器同步。
    • 在第三部分,我们将快速看看服务通道质量希望通过尽量保持项目精简,可以很容易的提炼并理解教程演示的网络原理,从而可以将这些概念应用到你自己的游戏中。

      预备知识

      需要熟悉基本概念。具体来说就是知道MonoBehaviour是什么,以及与GameObject的关系,并且知道里面诸如Awake、Start、Update之类的事件函数是如何及何时被调用的。不了解Unity Networking也没关系。


      我们的实现是基于Gabriel Gambetta在他的系列文章Fast-Paced Multiplayer(快节奏多人游戏)中所描述的架构。引用了他系列文章的第二篇来理解如何在项目中实现客户端预测和服务器同步。

      应该说这篇文章基本来自Unity文档《将单人游戏转换为多人游戏》的附加说明。想尽快上手HLAPI的开发者可以看看这个文档。也可将其作为HLAPI的使用指南。


      项目文件可以在文末下载。项目大部分由自定义的MonoBehaviour驱动,并且它带上空白行也只有115行。建议下载示例工程,以查看所有代码并实际运行,再学习本文就可以更好的理解整个工作原理。


      开工
      我们先创建一个Cube,可以用方向键控制它从一个网格位置移动到另一个。
      用结构体存储Cube的当前位置:

      [C#] 纯文本查看 复制代码
       
      1
      2
      3
      4
      5
      struct CubeState {
          public int x;
          public int y;
      }
        
      CubeState state;


      在Awake函数里设置Cube的初始位置:

      [C#] 纯文本查看 复制代码
       
      1
      2
      3
      4
      5
      6
      7
      8
      9
      void Awake () {
          InitState();
      }
       
      void InitState () {
          state = new CubeState {
              x = 0,
              y = 0
          };
      }


      大部分工作在Update函数中去做,这里简单检测方向键按下并做出相应动作:

      [C#] 纯文本查看 复制代码
       
      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      11
      void Update () {
          KeyCode[] arrowKeys = {KeyCode.UpArrow, KeyCode.DownArrow, KeyCode.RightArrow, KeyCode.LeftArrow};
          foreach (KeyCode arrowKey in arrowKeys) {
              if (!Input.GetKeyDown(arrowKey)) continue;
              state = Move(state, arrowKey);
          }
          SyncState();
      }
       
      void SyncState () {
          transform.position = new Vector2(state.x, state.y);
      }


      如果方向键真的被按下,就用Move函数计算Cube的新网格位置,这个函数将Cube的前一个网格位置和方向键作为参数,并返回Cube的新网格位置:

      [C#] 纯文本查看 复制代码
       
      01
      02
      03
      04
      05
      06
      07
      08
      09
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      CubeState Move(CubeState previous, KeyCode arrowKey) {
          int dx = 0;
          int dy = 0;
          switch (arrowKey) {
              case KeyCode.UpArrow:
                  dy = 1;
                  break;
              case KeyCode.DownArrow:
                  dy = -1;
                  break;
              case KeyCode.RightArrow:
                  dx = 1;
                  break;
              case KeyCode.LeftArrow:
                  dx = -1;
                  break;
          }
          return new CubeState {
              x = dx + previous.x,
              y = dy + previous.y
          };
      }


      现在只是简单的使用了Unity的非多人游戏的基础特性。希望大家都比较熟悉。否则建议大家先熟悉Unity基础,然后再回到本文学习如何使用用Unity做多人游戏。下面来添加多玩家功能。

      总览
      观察网络模拟工作方式的一种办法是,将场景作为被多位玩家共享的场景。在这种设置下,每个玩家被分配一个独有的GameOject。在本例子中,我们将为各连接上的玩家分配一个Cube。接下来就要试着解决以下问题

      1.怎样在玩家间建立链接?
      2.怎样将多个Cube实例化给在线玩家?
      3.服务器如何保持所有在线玩家的内容一致?
      4.玩家如何告知服务器Cube要移动了?


      建立链接

      HLAPI遵循CS架构,这意味着机器间不能直接相连,而是将一台机器作为服务器,所以其他机器作为客户端连接到服务器。服务器同时也能作为客户端使用,所以不需要有一个专用服务器(尽管这也可行)。我们将建立工程,它可能运行在单服务器模式下或者同时作为客户端和服务端(也被称作主机模式)。

      记住,在Unity中,功能是由GameObject结合它带有的Component决定的。我们遵循这点来实现服务器或者客户端。
      1.创建一个空的GameObject。命名为“NetworkManger”。
      2.添加NetworkManager 组件。这个组件提供了一个简单控制HLAPI的方式。
      3.添加NetworkManagerHUD 组件。这个组件提供了一个基本的控制NetworkManger的UI。

      搞定了!用HLAPI很容易将项目作为服务器启动或者作为客户端链接到服务器。

      为客户端分配玩家游戏物体

      目前为止,尽管连接已经建立,但还没有内容。当一个玩家连接时,我们想要实例化一个Cube并让玩家可以控制这个Cube。使用Player Object来实现:

      1.首先,为已经存在的Cube游戏对象并为它添加NetworkIdentity 组件。这会告诉HLAPI要处理这个游戏物体的网络方面。
      2.将这个游戏对象转换为预制件。
      3.将这个预制件赋给NetworkManger的“Player Prefab”字段。
      4.如果Cube还留在场景中,删掉它。一个新玩家链接时会自动创建一个Cube实例。因为Cube带有NetworkIdentity组件,HLAPI也会知道这是一个网络游戏对象,这将使得同样的Cube会被实例化到所有链接的客户端。

      跨机器同步MonoBehavior

      如果之前没有做过网络游戏,你可能认为给一个GameObject添加NetworkIdentity组件不会自动同步到网络中的所有其它客户端。在我看来,不自动同步真的是一件好事,因为不同游戏有不同的需求,并且控制同步和不允许的内容可以让游戏性能更好,某些情况下甚至可以让游戏设计更灵活。

      用HLAPI在网络上同步MonoBehaviour的数据也很简单:


      1.
      子类NetworkBehaviour代替了MonoBehaviour。NetworkBehaviour是基于MonoBehaviour并添加了可以在客户端和服务端传输数据的功能。做一点很简单的改变即可,将:
      [C#] 纯文本查看 复制代码
       
      public class CubePlayer : MonoBehaviour

      变为:
      [C#] 纯文本查看 复制代码
       
      public class CubePlayer : NetworkBehaviour

      通过使用NetworkBehaviour,就可以在代码里使用HLAPI的数据传输功能。


      2.
      用SyncVar 属性将成员变量标记为要被同步的。这里我们只需要将:
      [C#] 纯文本查看 复制代码
       
      CubeState state;

      变成:
      [C#] 纯文本查看 复制代码
       
      [SyncVar] CubeState state;

      通过标记变量为SyncVar,这个变量值在服务器上的任何更新都会广播到所有已链接的客户端。只要确保SyncVars 的所有改变是由服务端产生即可。


      限制输入
      到这里,如果运行工程,就会看到当按下方向键,屏幕上的所有的Cube都响应输入,这不是我们希望的,只要自己的Cube能响应输入即可。为了更正这点,只需在Update函数中做一个细微的调整,如下:
      [C#] 纯文本查看 复制代码
       
      1
      2
      3
      4
      5
      void Update () {
          if (isLocalPlayer) {
              // handle input here...
          }
          SyncState();
      }



      isLocalPlayer变量告诉我们处理的玩家物体是否是自己的。如果不是,将忽略任何输入;如果是,就移动cube。

      控制Cube

      “跨机器同步MonoBehaviour”部分的最后一句话留下了十分重要的线索,下一步应该做什么。为了让SyncVars 生效,它们应该从服务器上更新。但现在的代码不是如此:

      1.只要有方向键被按下,每个客户端直接更新被标记为SyncVar的变量。
      2.只有主机的更新传播到所有链接的客户端,实际上忽略了任何其它人的更新。

      记住SyncVar只能被服务器修改,所以我们要做的是:

      1.当客户端的方向键按下,客户端应该告诉服务器方向键被按下了。
      2.反过来,服务器应该根据收到的输入更新SyncVar。
      3.因为SyncVar由服务器更新,所有的变化都被正确的传递到所有链接的客户端上。

      HLAPI给我们提供了一个从客户端调用服务器函数的方法,使用Commands。可将已经存在的函数转换为Commands,通过如下方式标记它们(与将变量标记为SyncVar类似):

      1.添加Command属性。
      2.在函数名前添加Cmd前缀。

      例如,这个函数:
      [C#] 纯文本查看 复制代码
       
      1
      2
      void MoveOnServer (KeyCode arrowKey) {
          state = Move(state, arrowKey);
      }

      这样就能将函数变成一个Command:
      [C#] 纯文本查看 复制代码
       
      [Command] void CmdMoveOnServer (KeyCode arrowKey) { /* ... */ }

      一旦有了Command,就能像调用其它函数一样调用这个函数,但是因为它被标记为Command,所以在客户端调用函数里的代码将运行在服务器上。回到例子,只需将Update函数里的如下行:

      [C#] 纯文本查看 复制代码
       
      state = Move(state, arrowKey);

      变成:
      [C#] 纯文本查看 复制代码
       
      CmdMoveOnServer(arrowKey);


      只需做些简单的改变,就能在服务器上执行函数了,现在SyncVar现在被正确更新了。

      注意,Command只能被你自己的Player Object调用,这防止了一个客户端接管其它客户端的控制。在服务器端确保了是谁在调用函数,这将帮助我们确保多玩家网络游戏的安全。

      仅服务器调用的函数

      [Server] void InitState () { /* ... */ }

      在教程结束前,再看一下Awake 函数,只有一行代码就是调用InitState。为了让Demo理想运行,需要确保这个初始化只在服务器上运行而不能在客户端。方式是标记InitState作为一个仅服务端调用的函数。还好用HLAPI很容易做到这点。我们要做的就是标记函数为Server属性,如下:
      [C#] 纯文本查看 复制代码
       
      [Server] void InitState () { /* ... */ }

      如果在服务器/主机下调用InitState,它就能正常运行。否则,调用会被忽略。


      下一步


      运行Demo,你可能会注意到当试着控制Cube的时候,按下按键和到盒子在屏幕上移动之间有延迟。因为从客户端发送按键信息到服务器要花费一些时间,并且从服务端发送Cube的状态信息还要花费更多时间。
      Demo 下载地址 链接:http://pan.baidu.com/s/1qXYUQuS 密码:kizt
    • 原文链接:http://www.gamasutra.com/blogs/C ... ucing_the_HLAPI.php
    • 原文作者:Christian Arellano
      转载自:http://www.manew.com/forum.php?mod=viewthread&tid=45981&extra=page%3D1&page=1
    • 如遇版权问题 请联系我 993056011@qq.com
原文地址:https://www.cnblogs.com/TDou/p/6069296.html