角色移动同步化
角色移动同步化的方式有客户端-服务器方式和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++开发服务器实现多人游戏(九) |