游戏开发论坛

 找回密码
 立即注册
搜索
查看: 2204|回复: 0

Unity结合C++开发服务器实现多人游戏(八)

[复制链接]

29

主题

38

帖子

384

积分

中级会员

Rank: 3Rank: 3

积分
384
QQ
发表于 2017-2-27 16:54:09 | 显示全部楼层 |阅读模式
角色移动同步化

角色移动同步化的方式有客户端-服务器方式和P2P方式。
让我们了解客户端-服务器方式.
1.从客户端发送到服务器角色的移动信息.
2.服务器并且发送到其它的客户端.
3.客户端接收后查找相应角色并移动.
P2P方式与以上相似,只是不通过服务器. 此讲座主要讲述,不通过服务器,直接接收客户端之间的信息.

通过服务器处理和客户端之间的直接处理存在各自的优缺点. 通过服务器处理检测时,客户端被黑客攻击的话,可以提前感知并采取防御措施.
客户端之间处理检测时,客户端之间以直接通信使得通信量的时间可以最小化.

ProudNet中,客户端之间要进行P2P通信的话,首先要创建P2P组.然后,在P2P组添加设备的 HostID. P2P组或者HostID类型.
P2P组也可添加服务器.
开启服务器时,创建一个P2P组,同时添加服务器自身至P2P组.
  
class SimpleServer
  
{
  
    HostID  m_playerGroup = HostID_None;
  
    void Run()
  
    {
  
    ...
  
        m_playerGroup  = m_netServer->CreateP2PGroup();
  
        m_netServer->JoinP2PGroup(HostID_Server,  m_playerGroup);
  
...
  
客户端发送"将进入游戏"服务器接收处理. P2P组添加此客户端.
  
DEFRMI_Simple_JoinGameScene(SimpleServer)
  
{
  
...
  
    // P2P group join
  
    m_netServer->JoinP2PGroup(remote,  m_playerGroup);
  
在服务器端通过JoinP2PGroup或者CreateP2PGroup添加客户端的话, 此客户端将接受事件回调. 此事件的名字是JoinServerCompleteHandler.变量会得知,"进入哪个P2P组,还有谁进入了此P2P组". 我们在此保管P2P组的HostID.
  
class NetManger
  
{
  
    HostID  m_playerP2PGroup = HostID.None;
  
    void Start()
  
    {
  
        ...
  
            m_netClient.P2PMemberJoinHandler  =
  
            (HostID  memberHostID, HostID groupHostID,
  
                int memberCount, ByteArray customField) =>
  
        {
  
            m_playerP2PGroup  = groupHostID;
  
        };
  
  
现在, 客户端通过P2P向所有其它客户端发送"我在移动中"的信息.同时发送玩家角色的位置和速度.
在PIDL文件下定义Player_Move远程函数.
  
global Simple 1000
  
{
  
...
  
    Player_Move(
  
        [in] float x, [in] float y,  [in] float z,
  
        [in] float vx, [in] float vy,  [in] float vz,
  
        [in] float angle);
  
}
  
起初速度以(0,0,0)发送. 此内容下一章详细讲述;;
每0.1秒发送一次可根据游戏的特性缩短或者延长周期.
  
class NetManager
  
{
  
    float m_lastSendTime = -1;
  
    void Update()
  
    {
  
        if (m_netClient != null)
  
        {
  
            //  If connection online
  
            if (m_netClient.GetLocalHostID() !=  HostID.HostID_None) // (1)
  
            {
  
                //  send player move message
  
                if (m_lastSendTime < 0 || Time.time - m_lastSendTime > 0.1)
  
                {
  
                    var  sendOption = new RmiContext(); // (2)
  
                    sendOption.reliability  = MessageReliability.MessageReliability_Unreliable;
  
                    sendOption.maxDirectP2PMulticastCount  = 30;
  
                    sendOption.enableLoopback  = false;
  
  
                    var  pc = m_localPlayer.GetComponent<PlayControl>();
  
                    m_proxy.Player_Move(m_playerP2PGroup,  sendOption,
  
                        m_localPlayer.transform.position.x,
  
                        m_localPlayer.transform.position.y,
  
                        m_localPlayer.transform.position.z,
  
                        0,  0, 0,
  
                        pc.carAngle);
  
  
                    m_lastSendTime  = Time.time;
  
...
  
服务器和客户端的话, NetClient.GetLocalHostID发送自身的HostID. 在此之外发送None.
以上(1)代表检测此内容.
P2P方式玩家移动时,使用unreliable. Unreliable设定后, 内部使用UDP发送信息. 使用UDP发送的情况,发生数据包丢失,直接发送丢失的数据包.假如,设置为reliable的话, 以TCP或者Reliable UDP发送,此时发生数据包丢失的话,增加延时后再发送. 角色移动或者像机枪乱射"稍微丢失的"以unreliable发送为佳.网络环境差的国家中来说,此方法是有效的.
P2P方式发送的收信对象过多的话,客户端发生的收信通信量大幅上升. 大家想要控制通信量上限的话, "OO个直接以P2P发送,其余则用服务器代替发送" MaxDirectP2PMulticastCount来指定.
以上(2)所代表的内容是.
P2P有发送"移动信息"部分,自然需要接收部分.让我们进行添加. 调用NetClient.Connect之前进行以下处理.
  
  
        m_stub.Player_Move  = (Nettention.Proud.HostID remote,
  
            Nettention.Proud.RmiContext  rmiContext,
  
            float x, float y, float z, float vx, float vy, float vz, float angle) =>
  
        {
  
            var  g = GameObject.Find("RemotePlayer/" +  remote.ToString());
  
            if (g != null)
  
            {
  
                g.transform.rotation  = new UnityEngine.Vector3(x, y, z);
  
  
                var  rot = Quaternion.AngleAxis(angle, new UnityEngine.Vector3(0,  1, 0));
  
                g.transform.rotation  = rot;
  
            }
  
  
            return true;
  
        };
  
这样操作后,会实现角色的位置和方向更新.
服务器端接收Player_Move后,进行保管.

  
DEFRMI_Simple_Player_Move(SimpleServer)
  
{
  
   CriticalSectionLock  lock(m_critSec, true);
  
  
   // validation check
  
   auto it =  m_remoteClients.find(remote);
  
   if (it == m_remoteClients.end())
  
   {
  
       return true;
  
   }
  
   auto& rc =  it->second;
  
  
   // update player info
  
   rc->m_x = x;
  
   rc->m_y = y;
  
   rc->m_z = z;
  
   rc->m_vx = vx;
  
   rc->m_vy = vy;
  
   rc->m_vz = vz;
  
   rc->m_angle = angle;
  
  
   return true;
  
}
  
开启2个以上的客户端,进行角色移动.
但是,角色移动会有些迟钝,把"移动信息"每0.1秒改成每1/60秒即可解决此问题.
此方法在大型网游和对战游戏中不算是好方法,我们开发的游戏是即使在网络环境差的情况下也可以正常运作.下一篇进行详细讲述

第九篇:Unity结合C++开发服务器实现多人游戏(九)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

作品发布|文章投稿|广告合作|关于本站|游戏开发论坛 ( 闽ICP备17032699号-3 )

GMT+8, 2025-2-25 00:22

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表