OGeek|极客世界-中国程序员成长平台

标题: android - 如何在移动设备上使用 UDP 解决高延迟问题 [打印本页]

作者: 菜鸟教程小白    时间: 2022-12-11 20:13
标题: android - 如何在移动设备上使用 UDP 解决高延迟问题

我在使用 Unity 制作的多人射击游戏时遇到网络延迟较低的问题。我正在使用 UDP 将玩家位置从游戏客户端发送到我的亚马逊服务器并返回到另一个游戏客户端。我的游戏客户端以每秒 8 个数据包的速率向亚马逊服务器发送 60 字节的 UDP 数据包。

当我在两台不同的 iOS 设备(iPhone 7 和 iPad mini)上玩游戏时,网络延迟非常低,玩家可以立即看到彼此的 Action 。但是,如果我在我的 iPhone 7 上运行游戏并与另一个使用运行 android 的 samsung Galaxy s4 的玩家对战,这是一个低功耗设备,当从 iOS 设备接收玩家位置时,我会在 android 设备上遇到 5 秒延迟.奇怪的是,iOS 设备可以立即从 android 设备接收玩家位置。在 iPhone 7 上与 mac 上的客户端玩游戏时会发生同样的问题,除了 iPhone 7 在从 mac 客户端接收玩家位置时会遇到 5 秒的延迟。在三星 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 发送器类的示例:

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 ("layer'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");
            }
        }
    }



Best Answer-推荐答案


我的代码的问题在于,在 UDP 消息处理函数的每次迭代中,我只读取了一条 UDP 消息。我更改了函数以读取缓冲区中所有可用的 UDP 消息,并且延迟减少了 80%。 UDP 消息在缓冲区中排队的速度比消息处理函数重复的速度要快,所以这就是问题发生的原因。

关于android - 如何在移动设备上使用 UDP 解决高延迟问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50658421/






欢迎光临 OGeek|极客世界-中国程序员成长平台 (http://ogeek.cn/) Powered by Discuz! X3.4