Java 游戏网络与 Kryonet : Bare-bones packet transfering

标签 java opengl networking jbox2d kryonet

我正在使用 Opengl 和 Jbox2d 用 Ja​​va 编写实时 2d 游戏。

我想开始编写网络组件。

虽然它使用 box2d,但我的游戏非常小,我想使用 Kryonet 库创建一个基本架构。

程序本身是一个像国际象棋一样的“比赛游戏”。我能想到的最合乎逻辑的系统是拥有存储所有玩家数据的专用服务器。

PlayerA 和 PlayerB 将连接到专用服务器,这将促进他们计算机之间的 TCP 链接。

比赛结束后,双方玩家会将结果数据传回专用服务器,服务器将进行身份验证,然后保存各自的玩家数据。

对于那些熟悉的人来说,暗黑破坏神 2 实现了类似的设置。

我希望此 TCP 连接仅将形状坐标 vector 数据从主机(比如玩家 A)发送到客户端(玩家 B),然后客户端将自行呈现。

然后我希望客户端将鼠标/键盘数据发送回主机。所有处理都将在主机的计算机上运行。

我的第一个问题:这个网络逻辑有什么缺陷吗?

我的第二个问题:如何使用 Kryonet 实现准系统服务器/客户端数据包传输(如所述)?

注意:我已经使用不同的库在 C++ 中完成了这种类型的数据包传输。我为 Kryonet 找到的文档/教程很糟糕。建议另一个具有良好支持的库是可以接受的答案。

最佳答案

我知道这是一个老问题,我确信 OP 已经以某种方式得到了他们的答案,但为了好玩,我想我还是会回答。自从我最近一直在使用 Kryonet 进行游戏开发以来,我一直在想这个问题。

一些早期的网络游戏,比如Bungie's Marathon (1994)看起来他们确实是这样做的:每个玩家的事件都将使用 UDP 发送给其他玩家。因此,如果玩家移动或射击,玩家的移动或射击的方向、速度等将被发送给其他玩家。这种方法存在一些问题。如果玩家的某个 Action 在网络上暂时丢失,则玩家或玩家似乎与其他人不同步。在这种情况下,游戏状态没有“真相”或“和解”。

另一种方法是让玩家在客户端计算他们的移动和 Action ,并将更新后的位置发送到专用服务器。通过服务器接收所有玩家状态更新,就有机会协调它们。如果某些数据在网络上丢失,它们也不会永久不同步。

与前面的例子相比,这相当于每个玩家将他们的位置发送到服务器,然后让服务器将每个玩家的位置发送给所有其他玩家。如果这些更新之一由于某种原因丢失,后续更新将对其进行更正。但是,如果仅发送按键,则丢失一次按键会使游戏不同步,因为所有客户端都在分别计算其他客户端的位置。

对于 Action 游戏,您可以使用混合方法来最大程度地减少明显的延迟。我一直以这种方式成功地将 Kryonet 用于 Action 游戏。每个玩家在每次渲染时都会将他们的状态发送到服务器(尽管这可能是过度的并且应该被优化)。状态包括位置、剩余投篮次数、生命值等。玩家还发送他们的投篮(起始速度和位置)。

服务器只是将这些内容回显给客户端。每当客户端收到射门时,都会在客户端进行计算,包括射门是否击中 catch 球员。由于接收玩家只计算他们自己的状态,所以从他们自己的角度来看,一切似乎都保持同步。当他们被击中时,他们会感觉到被击中。当他们击中另一名球员时,他们认为自己击中了另一名球员。由玩家“接收”一次射击来更新他们的健康状况并将该信息发送回服务器。

这确实意味着投篮在理论上可能会滞后或“丢失”,并且玩家可能认为他们的投篮击中了另一名玩家,而在另一名玩家的屏幕上却没有击中。但在实践中,我发现这种方法很有效。

这是一个例子(伪代码,不要指望它能编译):

class Client {
    final Array<Shot> shots;
    final HashMap<String, PlayerState> players; // map of player name to state
    final String playerName;
    void render() {
        // handle player input

        // compute shot movement
        // for shot in shot, shot.position = shot.position + shot.velociy * delta_t

        // if one of these shots hits another player, make it appear as though they've been hit, but wait for an update in their state before we know what really happened

        // if an update from another player says they died, then render their death

        // if one of these shots overlaps _me_, and only if it overlaps me, deduct health from my state (other players are doing their own hit detection)

        // only send _my own_ game state to server
        server.sendTCP(players.get(playerName));
    }

    void listener(Object receivedObject) {
 
        if(o instanceOf PlayerState) {
            // update everyone else's state for me
            // but ignore my own state update (since I computed it.)
            PlayerState p = (PlayerState)o;
            if(!p.name.equals(playerName) {
                players.add(p.name, p);
            }
        } else if (o instanceof Shot) {
            // update everyone else's shots for me
            // but ignore my own shot updates (since I computed them.)
            Shot s = (Shot)o;
            if(!s.firedBy.equals(playerName) {
                shots.add(s);
            }
        }
    }
}

class Server {
    final HashMap<String, PlayerState> players; // map of player name to 

    void listener(Object receivedObject) {

        // compute whether anybody won based on most recent player state

        // send any updates to all players
        for(Connection otherPlayerCon : server.getConnections()) {
            otherPlayerCon.sendTCP(o);
        }
    }
}

我确信这种方法也存在缺陷,并且可以通过多种方式对其进行改进。 (例如,它很容易让“被黑”的客户端占据主导地位,因为他们总是可以发送不考虑任何损坏等的更新。但我认为这个问题超出了问题的范围。)

关于Java 游戏网络与 Kryonet : Bare-bones packet transfering,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32020436/

相关文章:

c - OpenGL 缓冲区使用

java - GWT 客户端和桌面运行服务器之间的客户端/服务器通信

java - JCheckBox不触发isselect

java - 如何让 Tomcat 停止缓存我的 servlet 响应?

opengl - 为什么 glGetUniformBlockIndex() 返回 INVALID_INDEX?

c - openGL 3.1 中的三角形但 3.2 中没有

android 无法以编程方式连接到 wifi 网络(几秒钟后掉线并返回到上一个)

azure - 通过另一个 Azure 虚拟网络直接连接到 Azure 虚拟网络中的资源

java - RxJava : if/else operation in flatMap

java - Java 中的线程运行一段时间后能否获得实际使用的堆栈大小?