【问题标题】:Linear impulse and incorrect positions in multiplayer多人游戏中的线性脉冲和不正确的位置
【发布时间】:2020-12-17 08:53:25
【问题描述】:

我正在 LibGDX 中创建游戏。我最初是在 youtube 上关注超级马里奥兄弟游戏的教程,但是,我已经更改了游戏以包含我自己的角色和图形。我正在使用 Kryonet 作为在我的游戏中包含多人游戏功能的一种方式。这是我遇到几个问题的地方。

首先,我使用线性脉冲来移动,所以当我的角色击中能量提升时,会施加线性脉冲,并赋予他们更快前进的力量。在您同时查看两个屏幕之前,这可以正常工作,角色 A 在一个玩家的屏幕上的位置不同,而在另一个玩家的屏幕上的位置不同。我得到的两个角色的线性脉冲值也不同。我正常的右、左、上运动也是用线性脉冲完成的。由于线性脉冲值的随机性,我一直得到这个错误,我说这就像滑动冰错误,将冰块推过表面并不总是保证相同的位置。下面是我的多人客户端类的一些代码。

client.addListener(new ThreadedListener(new Listener() {
            // What to do with the packets.
            public void connected(Connection connection) {

            }

            public void received(Connection connection, Object object) {

                if (object instanceof MovementJump) {
                    MovementJump packet = (MovementJump) object;
                    
                    PlayScreen.player.b2body.applyLinearImpulse(new Vector2(0, 4f),
                            PlayScreen.player.b2body.getWorldCenter(), true);
                }

                if (object instanceof MovementRight) {
                    MovementRight packet = (MovementRight) object;
                    
                    PlayScreen.player.b2body.applyLinearImpulse(new Vector2(packet.impulse, 0),
                            PlayScreen.player.b2body.getWorldCenter(), true);
                }

                if (object instanceof MovementLeft) {
                    MovementLeft packet = (MovementLeft) object;
                    
                    PlayScreen.player.b2body.applyLinearImpulse(new Vector2(-packet.impulse, 0),
                            PlayScreen.player.b2body.getWorldCenter(), true);
                }

                if (object instanceof MovementP2Jump) {
                    MovementP2Jump packet = (MovementP2Jump) object;

                    PlayScreen.player2.b2body.applyLinearImpulse(new Vector2(0, 4f),
                            PlayScreen.player2.b2body.getWorldCenter(), true);
                }

                if (object instanceof MovementP2Right) {
                    MovementP2Right packet = (MovementP2Right) object;
                    
                    PlayScreen.player2.b2body.applyLinearImpulse(new Vector2(packet.impulse, 0),
                            PlayScreen.player2.b2body.getWorldCenter(), true);
                }

                if (object instanceof MovementP2Left) {
                    MovementP2Left packet = (MovementP2Left) object;
                    
                    PlayScreen.player2.b2body.applyLinearImpulse(new Vector2(-packet.impulse, 0),
                            PlayScreen.player2.b2body.getWorldCenter(), true);
                }
            }

        }));
    }


这是我 playScreen 中的一些代码

            if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)
                        && player.b2body.getLinearVelocity().x <= player.getMinSpeed()) {
                    
                    
                    MovementRight pos = new MovementRight();
                    MPClient.client.sendTCP(pos);
                }

                if (Gdx.input.isKeyPressed(Input.Keys.LEFT)
                        && player.b2body.getLinearVelocity().x >= -player.getMinSpeed()) {
                    
                    MovementLeft pos = new MovementLeft();
                    MPClient.client.sendTCP(pos);
                }

【问题讨论】:

    标签: java android libgdx game-physics kryonet


    【解决方案1】:

    您应该发送每个角色的坐标,而不是发送部队。

    为什么?如果你应用一个脉冲并发送一个数据包通知其他客户端这个脉冲,它们的脉冲将在 ping 延迟之后首先添加,它永远不会为 0。这意味着它们的脉冲将在之后添加,因此它们的字符将始终落后。

    我可以想到两种结构,但可能还有很多其他方法!第一个是客户端,第二个是服务器端,两者各有利弊。我个人推荐服务器端的结构,因为被黑的 MP 游戏真的让玩家望而却步。

    客户端结构:

    优点(延迟更少,反应迟钝的玩家控制更灵敏)缺点:(易于破解)

    1. 每当您的客户角色的位置发生变化时,使用您当前的坐标向服务器发送PositionUpdatePacket。此数据包只需包含 (float x, float y, byte characterID)
    2. 服务器将数据包发送给所有其他连接的客户端。
    3. 每个客户端都会根据角色 ID 更新角色。

    服务器端结构:

    优点(难以破解)缺点:(对于迟钝的玩家来说响应速度较慢)

    1. 当玩家进行输入(例如向左移动)时,您会向服务器发送带有所需方向的MoveRequestPacket。只需在方向改变或玩家停止时通过 TCP 发送一次。
    2. 服务器更新此播放器并朝所需方向移动,即更新方法行为。这将应用脉冲。这样做的好处是,如果玩家想穿过墙壁等,服务器可以进行各种碰撞箱检查并防止移动,以防止黑客入侵。
    3. 只要角色移动,服务器就会不断向所有连接的客户端(包括发送请求的客户端)发送PositionUpdatePacket,并使用当前字符位置。
    4. 每个客户端都会更新角色。

    在这两种结构中,PositionUpdatePacket 必须不断地发送到所有连接的客户端,只要角色移动,这意味着这个数据包会被发送很多。为了减少对性能的影响,您应该通过 UDP 发送它,并且每秒只发送 x 次。 Minecraft 以 20 吨/秒的速度发送位置。

    如果您发送PositionUpdatePacket 的频率低于#update#step 方法,那么每个字符都会卡顿和滞后。为了解决这个问题,您可以使用插值来平滑运动。

    我希望这会有所帮助。祝你好运!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多