我在使用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消息在缓冲区中排队的速度比消息处理程序函数重复的速度快,因此这就是发生问题的原因。

07-24 09:49
查看更多