我在使用Unity制作的多人射击游戏中网络延迟较低时遇到问题。我正在使用UDP将玩家位置从游戏客户端发送到我的Amazon服务器,然后再发送回另一个游戏客户端。我的游戏客户端正在以每秒8个数据包的速率向Amazon服务器发送60字节UDP数据包。
当我在两个单独的iOS设备(iPhone 7和iPad mini)上玩游戏时,网络延迟非常低,玩家可以看到彼此立即移动。但是,如果我在iPhone 7上运行游戏并与另一个使用运行较低功率设备的android三星s4的玩家对战,则从iOS设备接收玩家位置时,我在android设备上会遇到5秒钟的延迟。奇怪的是,iOS设备可以立即从android设备接收玩家位置。在iPhone 7上与Mac上的客户端进行播放时,会发生相同的问题,除了从Mac客户端接收播放器位置时,iPhone 7经历5秒的延迟。当在Samsung Galaxy S4上与Mac上的客户端进行游戏时,也会发生此问题,其中galaxy s4经历5秒的延迟。
我试图将udp接收缓冲区的大小增加到16 MB,但没有任何改变。
这是我的游戏客户端代码的示例,我在其中将玩家位置发送到亚马逊服务器:
void Update(){
// manually constrain rotation because rigidbody constraints don't work
this.transform.rotation = Quaternion.Euler(new Vector3(0, this.transform.rotation.eulerAngles.y, 0));
this.transform.position = new Vector3(this.transform.position.x, boatHeightConstant, this.transform.position.z);
// Speed Boost Handling
if(isSpeedBoosting == true){
tiltFactor = tiltModifier * (VelocityRatio() + 1.0f);
speedBoostTimer += Time.deltaTime;
}
else{
tiltFactor = VelocityRatio() + 1.0f;
}
if(speedBoostTimer >= speedBoostDuration){
isSpeedBoosting = false;
speedBoostTimer = 0f;
endSpeedBoost = true;
}
if(endSpeedBoost == true){
GetComponentInChildren<MainWeapon>().EndFireRateBoost();
endSpeedBoost = false;
}
// Attack Boost Handling
if(isAttackBoosting == true){
attackBoostTimer += Time.deltaTime;
}
if(attackBoostTimer >= attackBoostDuration){
isAttackBoosting = false;
attackBoostTimer = 0f;
endAttackBoost = true;
}
if(endAttackBoost == true){
GetComponentInChildren<MainWeapon>().ResetDamage();
GetComponentInChildren<MainWeapon>().ResetHeatUpRate();
endAttackBoost = false;
}
if (GetComponent<InputManager>().GetInputType() == 0 || GetComponent<InputManager>().GetInputType() == 1 )
{
if (isSpeedBoosting == true)
{
Move(speedModifier);
}
else
{
Move(1f);
}
if (syncTimer <= 0f) {
syncTimer = networkRefreshRate;
SyncTransform ();
} else {
syncTimer -= Time.deltaTime;
}
}
else
{
NetworkMove();
}
}
/// <summary>
/// This function is constantly called to upload the local player's position to the server so the server is
/// aware of this player's movement and can share this local players current position with other players in the game.
/// </summary>
public void SyncTransform() {
if (isLocalPlayer == true && client != null && client.IsConnected()) {
client.SendPlayerTransform(SharedData.storage["userId"], transform.position, transform.rotation, currentLife);
}
}
这是我的游戏客户端中UDP sender类的示例:
public void SendPlayerTransform(string playerId, Vector3 position, Quaternion rotation, int currentLife) {
Message.Writer writer = new Message.Writer(Message.MessageType.PlayerTransform, udpClient, remoteEndPoint);
// Write the timestamp of this message
writer.WriteLong(DateTime.UtcNow.Ticks);
// write the player id
writer.WriteString(SharedData.storage["userId"]);
// write the position vector
writer.WriteFloatArray(CommonGameFunctions.ConvertVectorToFloatArray(position));
// write the rotation vector
writer.WriteFloatArray(CommonGameFunctions.ConvertQuaternionToFloatArray(rotation));
writer.WriteInt (currentLife);
// Now send the message
writer.Send();
}
这是我在游戏客户端上接收UDP消息的示例:
public int HandleUdpMessages() {
if (udpTimerStarted == false) {
lastUdpMessageReceivedTime = DateTime.Now;
udpTimerStarted = true;
} else if (udpTimerStarted == true && udpClient.Available == 0){
TimeSpan t = DateTime.Now - lastUdpMessageReceivedTime;
if (t.Seconds >= 10f) {
// no message received for last 10 seconds then throw IO exception
//throw new SocketException();
}
}
if (udpClient.Available > 0) {
var messageReader = new Message.Reader (udpClient);
messageReader.BlockingRead (ref localEndPoint, UdpReceiveTimeout);
var messageType = messageReader.ReadMessageTypeUdp ();
lastUdpMessageReceivedTime = DateTime.Now;
Debug.Log ("Received udp message: " + messageType);
switch (messageType) {
// Player position update message
case Message.MessageType.PlayerTransform:
HandlePlayerTransform (messageReader);
break;
case Message.MessageType.PlayerScore:
HandlePlayerScore (messageReader);
break;
case Message.MessageType.RockHealth:
HandleRockHealth (messageReader);
break;
case Message.MessageType.PlayerHealth:
HandlePlayerHealth (messageReader);
break;
case Message.MessageType.ShieldHealth:
HandleShieldHealth (messageReader);
break;
default:
Debug.LogError ("Unhandled message " + messageType);
break;
}
}
return 0;
}
public void HandlePlayerTransform(Message.Reader reader)
{
long timeStamp = reader.ReadLong ();
string playerId = reader.ReadString();
if (playerMessageTimeStamps [playerId].latestPlayerTransform > timeStamp)
return;
Vector3 position = new Vector3();
Quaternion rotation = new Quaternion();
// read position
position = CommonGameFunctions.ConvertFloatArrayToVector3(reader.ReadFloatArray(3));
rotation = CommonGameFunctions.ConvertFloatArrayToQuaternion(reader.ReadFloatArray(4));
// Now update the transform of the right player
Player player = Player.playerTable[playerId];
if (player == null) {
return;
}
player.SetNetworkPostion(position);
player.SetNetworkRotation(rotation);
}
在我的服务器上,这是运行在其专用线程上的主要游戏循环。
// Now all the players are connected to the server
// We can start the main game loop
while (gameRunning == true) {
HandlePlayersWhoDroppedOut ();
if (PlayersLeftGame () == true) {
DisconnectAllPlayers ();
Debug.LogError ("Player's left match, returning from thread");
return;
} else {
foreach (NetworkPlayer p in participants) {
try {
p.HandleTcpMessages ();
p.HandleUdpMessages ();
} catch (IOException e) {
droppedPlayers.Add (p);
}
}
try {
RespawnRocksIfDestroyed ();
} catch (System.IO.IOException e) {
DisconnectAllPlayers ();
return;
Debug.LogError ("Failed to spawn rocks");
}
}
}
最佳答案
所以我想出了我的代码的问题。在udp消息处理程序函数的每次迭代中,我只读取了一条udp消息。我更改了功能以读取缓冲区中所有可用的udp消息,并将延迟减少了80%。 UDP消息在缓冲区中排队的速度比消息处理程序函数重复的速度快,因此这就是发生问题的原因。