【问题标题】:Making a multi-player game playable over a network or on the internet使多人游戏可通过网络或互联网玩
【发布时间】:2017-06-09 04:30:06
【问题描述】:

您好,我用 Java 编写了一个多人游戏,我想知道我需要学习什么和/或应该使用什么才能使游戏可以通过网络或互联网由多台计算机玩。我真的不知道从哪里开始,所以任何建议都会有所帮助,谢谢。

【问题讨论】:

  • 游戏在当前状态下如何支持多人游戏功能?是否使用套接字与同一台机器上的多个实例进行通信?
  • 不,它只是基于轮流,但我想这是我需要做的事情,然后才能让它通过网络播放?
  • 多人游戏是指多个用户在玩同一个游戏,并且游戏中玩家之间有一些互动。你说你的游戏是“基于轮流”。是多人游戏吗?
  • 国际象棋是多人游戏,我猜它是多人游戏
  • @Bob:如果不了解游戏架构的更多信息,就很难提出建议。不过,基本上,您可能需要学习如何使用套接字。 “java socket tutorial”的第一个谷歌点击出现了this link,这可能不是一个糟糕的起点。

标签: java network-programming


【解决方案1】:

其他答案都是相当高级的,这很好,但你不想要高级,你想要低级,比如“我如何让它真正发送数据,这意味着什么?我要发送什么,等等。”这是你要做的:

首先,TCP 还是 UDP?如果您不知道其中任何一个是什么,请阅读它们,因为我没有空间在这里对两者进行详细介绍,但您可以选择了解以下内容:

  1. TCP 适用于回合制游戏或高延迟通常可以接受的游戏,因为 TCP 保证数据包传送,因此重新传送丢弃的数据包可能需要一些时间。这对于国际象棋之类的东西或其他任何轮流的东西都有好处。
  2. UDP 适用于那些您不一定关心消息的可靠性并且希望数据只是不断发送的游戏,如果您错过了什么,哦,好吧。这适用于基于实时动作的游戏,例如《光环:致远星》或《使命召唤》。在那些情况下,如果您发送一个对象的位置,而该对象永远不会到达那里,则发送一个新位置比重新发送一个旧位置(现在更旧)更好,因此始终保证可靠性并不重要。也就是说,您必须让某些东西 100% 可靠,因此您仍然需要某些东西来保证交付,例如对象创建和对象销毁。这意味着您需要在 UDP 之上实现自己的半可靠、基于优先级的协议。这很难。

所以,问问自己什么是重要的,了解 TCP 和 UDP 的工作原理,然后做出明智的选择。

也就是说,现在您必须通过网络同步对象状态。这意味着您的对象需要序列化为可以在字节流中表示并写入套接字的东西。写入套接字很容易;如果您可以写入文件,您可以写入套接字,这真的不难。重要的是确保您能够将对象表示为缓冲区,因此如果您的对象具有指向其他对象的引用/指针,您将无法仅发送这些指针,因为它们在其他客户端上是不同的,因此您必须将它们转换为所有主机通用的东西。这意味着 ID,尽管一个对象的 ID 在所有主机中必须是唯一的,因此您必须有一种方法在主机之间进行协调,这样没有两个主机会创建具有相同 ID 的不同对象。有一些方法可以处理主机这样做,但我们不会在这里担心(提示:在主机 ID 和网络 ID 之间使用某种映射。更大的提示:如果不需要,请不要这样做)。

所以现在你可以发送数据了,太好了,现在怎么办?每次游戏状态发生变化时,您都必须以某种方式向其他机器发送更新。这就是客户端-服务器架构的用武之地,如果您愿意,也可以是对等架构。客户端-服务器更容易实现。此外,一台“充当”服务器的主机仍然是客户端-服务器,任何说不同的人都是错误的。

因此,服务器的责任是“拥有”所有游戏状态。只有服务器可以明确说明对象处于什么状态。如果要移动对象,请告诉服务器您要移动,但是服务器随后会告诉您应该移动对象,您不只是去做(尽管某种客户端预测通常很有用)。然后服务器将更新后的对象状态发送给所有其他主机。

那么,您提到了一款回合制游戏,对吧?很简单:

  1. 您将解决当前轮到客户端的完整轮到问题。一旦该客户端完成了他们想做的事情,将该轮次的结果发送到服务器。然后服务器验证客户端的动作(不要只信任客户端,作弊就是这样发生的)并将它们应用于其对象状态。
  2. 一旦服务器更新到最新状态,它就会向所有其他客户端发送消息,其中包含新的世界状态,并且这些客户端会应用这些更新。这包括刚刚轮到他们的客户;该客户端应该只在服务器告诉它时更新它的世界状态,因为你想确保与其他主机的一致性,并且你想防止主机作弊。
  3. 然后服务器发出一条消息,指示轮到谁了。您可以在上一步中与世界状态更新同时发送它,这很好。请注意客户试图让他们的轮到他们的秩序。这就是为什么服务器在世界范围内拥有权威的原因;如果客户端试图作弊,服务器可以将其击倒。

这就是回合制游戏所需要做的一切。提示:使用 TCP 更大的提示:TCP 实现了一种称为“Nagle 算法”的东西,它将您的消息组合成一个数据包。这意味着,如果您通过两次单独调用“Send”发送两条单独的消息,则其他主机可能会在一次调用“Receive”时仅收到一个数据包,但该数据包将包含发送的数据包。因此,如果您通过两次发送调用发送两个 100 字节的数据包,您可能会在一次接收调用中获得一个 200 字节的数据包。这是正常的,所以你需要能够以某种方式处理这个问题。一个技巧是使每个数据包的大小相同,然后每次检查输入时从套接字读取那么多字节。请记住,您也可能会收到部分消息。例如,如果您发送两条 100 字节的消息,它们可以组合成一条 200 字节的消息。接下来,如果您从另一端的套接字读取,但您读取的缓冲区大小为 150 字节,您将有 150 字节,其中包含第一个数据包和第二个数据包的一部分。您必须拨打第二个电话才能收到第二条消息的其余部分,因此请跟踪您收到的数据量,以免在某处丢失部分数据包。这就是为什么保持数据包大小相同的原因。

还有许多其他有用的技巧可以减少消息的大小和频率,以及跟踪非回合制和实时行动的游戏,但如果您有回合制游戏,那么正确的做法可能是使用 TCP 而不必担心其他任何东西。以下是一些有用的网站和文章的链接,它们将为您提供有关如何完成游戏网络编程的更多信息:

  • Glenn Fiedler's site,这里有一些很棒的信息。
  • 1500 archers,一篇很棒的论文,介绍了如何实现一种称为确定性锁步的技术,该技术对许多类型的游戏都很有用。

如果您想了解有关这些内容的更多详细信息,或者您有更具体的问题,请告诉我。

【讨论】:

    【解决方案2】:

    一种可能的架构方法是让游戏的一个实例充当主机(例如,第一个开始)。它将协调游戏并发送游戏状态信息并转向其他每个玩家。

    当玩家移动时,它会将移动信息发送给主机,主机会更新游戏状态(并检查移动的有效性等)。然后它将新的游戏状态发送给每个玩家,并发送(可能作为单独的通信)下一回合的通知给相应的客户端并等待其响应。

    在某种意义上,主机在这种情况下充当游戏服务器,但使用/玩游戏可能更简单,因为玩游戏不需要运行单独的进程。

    【讨论】:

      【解决方案3】:

      如果您想通过网络添加多人游戏功能,查看Netty project 构建通信基础设施可能对您有用。

      但在您这样做之前,您需要确保您的游戏具有正确的“架构”。你需要有大模块:客户端和服务器。

      服务器负责所有游戏逻辑。从某种意义上说,它是游戏引擎。客户端负责向服务器查询游戏状态,显示给玩家,获取玩家输入并向服务器发送命令。

      解耦我们的客户端和服务器代码的一种方法是让两个不同的程序从 CLI 中运行,从而避免学习网络编程所涉及的麻烦。执行顺序可以是这样的:

      1. 服务器运行初始化游戏状态,游戏状态保存在一个文件中。
      2. 客户端运行,它读取游戏状态,以某种方式显示它并从玩家那里获得一些输入。它将输入保存到文件中。
      3. 服务器运行,它从客户端读取命令,更改游戏状态并更新状态文件。

      冲洗并重复。这基本上是 PBEM 服务器所做的。

      为了帮助解耦客户端和服务器,定义一种“语言”来表示游戏状态的变化以及服务器要执行的命令是有意义的。如果客户端缓存状态并应用从服务器发送的更改,这会有所帮助。

      一旦您的客户端和服务器代码完全解耦,并且完全定义了“语言”,您就可以将通信机制从基于文件的方式更改为基于套接字的方式。

      【讨论】:

        猜你喜欢
        • 2011-09-09
        • 1970-01-01
        • 1970-01-01
        • 2012-01-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-24
        相关资源
        最近更新 更多