我正在用java制作游戏,进展顺利。我想尽早实现多人游戏,所以我在它的基础上进行构建,而不是将整个游戏移植到多人游戏中,因为它具有大量不同的功能。我想让它成为客户端/服务器应用程序。现在我确定如何或如何实现多人游戏。我已经阅读了有关套接字和所有内容的 java 教程,我对它们进行了测试并成功连接(在测试项目中)。我不知道从这里去哪里。我不知道我将如何转移例如地图上不同玩家的位置,或者甚至是否有任何玩家。我不知道是使用图书馆还是自己做还是什么...
3 回答
这是一个相当广泛的问题,有多种方法可以做事,但这是我的看法。免责声明:我是一家移动多人游戏公司的服务器系统架构师。我不认为自己是这方面的专家,但由于我的工作和爱好,我确实有一些经验(我在 2004 年编写了我的第一个“MMORPG”,支持了高达 255 名玩家),并且觉得我可以戳你在正确的方向。对于这里的大多数概念,我仍然建议您使用 Google、Stackoveflow 等进行进一步研究,这只是我对游戏联网所需的“10000 英尺视图”。
根据您制作的游戏类型(想想第一人称射击游戏等实时游戏与国际象棋等回合制游戏),底层传输层协议的选择很重要。就像 Matzi 建议的那样,UDP 为您提供更低的延迟(以及更低的数据包开销,因为标头小于 TCP),但不利的一面是,永远无法保证将数据包传送到目的地,即。您永远无法确定您发送的数据是否真的到达了客户端,或者,如果您连续发送多个数据包,数据是否以正确的顺序到达。您可以通过使用单独的消息确认到达的数据来实现“可靠的 UDP”协议(尽管同样,如果确认使用 UDP,它们也可能会丢失)并通过一些额外的数据处理订单,但是您(在至少部分)失去了较低的延迟和较低的开销。另一方面,TCP 保证数据的传递和顺序保持正确,但由于数据包确认和开销(TCP 数据包具有更大的标头)而具有更高的延迟。您可以说 UDP 数据包有点像“单独的实体”,而 TCP 是一个连续的、不间断的流(您需要某种方法来区分一个消息在哪里结束,另一个消息在哪里开始)。
有些游戏同时使用两者;用于绝对必须发送到客户端的重要数据的单独 TCP 连接,例如玩家死亡等,另一个用于“即发即弃”类型数据的 UDP 连接,例如玩家的当前位置(如果位置确实没有到达另一个客户端,并且玩家正在移动,再次发送数据没有多大意义,因为它可能已经过时了,很快就会有另一个更新)。
在为传输选择了 UDP 和/或 TCP 之后,您可能仍然需要一个自定义协议来编码和解码 TCP/UDP 数据包移动的数据(“有效负载”)。对于游戏,显而易见的选择是一些二进制协议(与 HTTP 等基于文本的协议相比)。例如,一个简单的二进制协议可以标记消息中包含的总字节数,然后是数据类型、数据字段长度和字段的实际数据(重复每个消息的字段数)。这可能有点棘手,因此至少对于初学者来说,您可以使用诸如序列化和反序列化消息对象之类的方法,然后查看现有协议或自己编写协议(实际上并不难)。当您获得基本数据类型(如字符串、整数、浮点数......)的编码和解码工作以及一些数据移动时,您需要设计自己的高级协议,这实际上是您的游戏和服务器的消息用来互相交谈。这些消息是“玩家加入游戏”、“玩家离开游戏”、“
在实时游戏中,您还面临一些其他挑战,例如预测玩家的位置(请记住,客户端发送的数据很容易在数百毫秒前到达另一个玩家客户端,因此您需要“猜测”在哪里玩家到达时)。尝试使用谷歌搜索“游戏航位推算”和“游戏网络预测”等内容,Gamasutra 也有一篇很好的文章:航位推算:网络游戏的延迟隐藏,可能还有很多其他内容可以找到。
您需要考虑的另一件事是服务器端代码的并发性。很多人会告诉您,您需要使用 Java NIO 来获得良好的性能,并且每个连接使用线程是不好的,但实际上至少在 Linux 上使用 Native Posix 线程库(NPTL,几乎任何现代 linux 发行版都会将其排除在外)框),情况相反,供参考,请参见此处:Writing Java Multithreaded Servers - whats old is new。我们有服务器运行 10k+ 线程,有数千名用户并且没有阻塞(当然,在任何给定时间,这些线程中的绝大多数都将处于休眠状态,等待客户端消息或消息发送到客户端)。
最后,您需要衡量您的游戏需要多少计算能力和带宽。为此,您需要测量某个(服务器?)硬件可以为您的软件带来多少负载,以及您的游戏会带来多少流量。这对于确定您的服务器可以支持多少客户端以及您需要多快的网络连接(以及每月多少流量配额)非常重要。
希望这有助于回答您的一些问题。
首先,多人游戏使用 UDP 进行数据传输。这有很多原因,例如较低的延迟等。如果您的游戏包含密集动作,并且需要快速反应,那么您应该选择基于 UDP 的东西。
可能有网络游戏的解决方案,但编写自己的实现也不是那么难。如果您对此有疑问,您可能会在游戏的其余部分遇到问题。网络上甚至 java 中都有非面向游戏的库和解决方案,但它们大多不是为像游戏一样快的东西而设计的。例如,远程过程调用可能包含昂贵的序列化并生成比您真正需要的大得多的包。它们可以是方便的解决方案,但考虑到游戏而不是常规的商业应用程序,它们的性能很差。
例如,如果您有 20 个玩家,每个玩家都有坐标、状态,当然还有移动对象。您需要每秒至少 20 次更新才能没有太多延迟,这意味着大量流量。带有用户输入的 20*20 传入消息,以及包含大量信息的 20*20 传出消息。算一算。您必须尽可能将所有播放器和尽可能多的对象数据压缩到一个包中,以获得最佳性能。这意味着您可能必须编写可以很容易地序列化为字节流的小型数据包,并且它们必须只包含可行的信息。如果您丢失了一些数据,这不是问题,但您需要注意重要信息以确保它们到达目的地。例如,您不希望玩家错过有关他们死亡的消息。
我用C#写了一个可靠可用的网络“库”,工作量不大,但建议环顾四周,把它建好。这是一篇关于这个主题的好文章,阅读它。即使您使用外部库,最好了解它在做什么以及应该如何使用它。
对于虚拟机之间的通信,它并没有比RMI简单得多。使用 RMI,您可以在完全不同的计算机上调用对象的方法。您可以使用整个对象作为参数和返回值。因此,通知服务器您的移动可以像server.sendMove(someMoveObject, somePlayerObject, someOtherObject)
.
如果您正在寻找一个起点,这可能是一个很好的起点。