gpt4 book ai didi

java - 程序中传递消息的数据结构?

转载 作者:行者123 更新时间:2023-12-02 06:21:09 26 4
gpt4 key购买 nike

我正在尝试编写一个简单的RPG。到目前为止,每次我尝试启动它都会立即变得一团糟,而且我不知道如何组织任何事情。因此,我从头开始,尝试为基本上是MVC框架的新结构建立原型(prototype)。我的应用程序开始在Controller中执行,它将在其中创建View和Model。然后它将进入游戏循环,游戏循环的第一步是收集用户输入。

用户输入将由 View 的一部分收集,因为它可能有所不同(3D View 将直接轮询用户输入,而远程 View 可能会通过telnet连接接收它,或者命令行 View 将使用System.in )。输入将被转换为消息,并且每条消息都将被提供给Controller(通过方法调用),然后该Controller可以解释该消息以修改Model数据,或者通过网络发送数据(因为我希望拥有联网选项) 。

在联网游戏的情况下,也可以使用此消息处理技术来处理网络消息。到目前为止,我是否一直秉承MVC的精神?

无论如何,我的问题是,代表这些消息的最佳方法是什么?

这是一个用例,每个消息用斜体表示:假设用户开始游戏并选择角色2。然后,用户移至坐标(5,2)。然后他对公众聊天说:“嗨!”。然后,他选择保存并退出。

View 应该如何将这些消息包装成 Controller 可以理解的内容?还是您认为我应该有单独的 Controller 方法,例如chooseCharacter(),moveCharacterTo(),publicChat()?我不确定这种简单的实现在我移至网络游戏时是否会起作用。但另一方面,我不想只是将字符串发送到Controller。这很困难,因为select-character操作采用一个整数,move-to采用两个整数,并且聊天采用字符串(和作用域(public(私有(private))global),在private(私有(private))的情况下,目标用户);并没有真正的设置数据类型。

也欢迎任何一般性建议;我担心在正确的时间吗?我是否正朝着布局合理的MVC应用程序的正确道路迈进?有什么我忘记的吗?

谢谢!

最佳答案

(免责声明:我从来没有用Java编写游戏,只用C++编写了游戏。但是总体思路也应该适用于Java。
我提出的想法不是我自己的,而是我在书中或“在互联网上”找到的解决方案的混搭,请参阅引用资料部分。
我自己动用了所有这些内容,到目前为止,它产生的设计很干净,我确切知道要添加新功能的位置。)

恐怕这将是一个很长的答案,第一次阅读时可能不清楚,因为我无法很好地描述它,所以我会来回引用,这是由于我缺乏解释能力,不是因为设计有缺陷。事后看来,我超出了范围,甚至可能偏离了话题。但是,既然我已经写了所有这些内容,我就无法把自己丢掉。请问是否有不清楚的地方。

在开始设计任何包和类之前,请先进行分析。您想在游戏中拥有哪些功能。不要计划“也许以后再添加”,因为几乎可以肯定的是,在开始认真添加此功能之前,您已经预先做出了设计决定,因此计划的存根将不足。

出于激励,我从这里的经验出发,不要将您的任务视为编写游戏引擎,编写游戏!无论您对 future 项目的吸引力有什么想法,除非您将其放在您现在正在编写的游戏中,否则请拒绝该项目。没有未经测试的无效代码,没有动机问题,因为无法解决甚至对即将进行的直接项目而言都不是问题的问题。没有完美的设计,但有一个足够好的。值得牢记这一点。

如上所述,我认为在设计游戏时MVC毫无用处。模型/ View 分离不是问题,并且 Controller 的内容非常复杂,以至于被称为“ Controller ”。
如果您希望有名为model,view,control的子包,请继续。以下内容可以集成到此包装方案中,尽管其他方面至少是明智的。

很难在我的解决方案中找到起点,因此我只是从最顶层开始:

在主程序中,我只是创建Application对象,将其初始化并启动。应用程序的init()将创建功能服务器(请参见下文)并对其进行初始化。同样,创建第一个游戏状态并将其推到顶部。 (另请参见下文)

功能服务器封装正交游戏功能。这些可以独立实现,并通过消息松散耦合。示例功能:声音,视觉表示,碰撞检测,人工智能/决策制定,物理等等。功能本身的组织方式如下所述。

输入,控制流程和游戏循环

游戏状态提供了一种组织输入控制的方法。我通常只有一个类来收集输入事件或捕获输入状态并稍后对其进行轮询(InputServer/InputManager)。如果使用基于事件的方法,则将事件赋予单个已注册的 Activity 游戏状态。

开始游戏时,这将是主菜单游戏状态。游戏状态具有init/destroyresume/suspend功能。 Init()将初始化游戏状态,如果使用主菜单,它将显示最高菜单级别。 Resume()将控制此状态,现在它从InputServer接收输入。 Suspend()将清除屏幕上的菜单 View ,并且destroy()将释放主菜单所需的任何资源。

可以堆叠游戏状态,当用户使用“新游戏”选项启动游戏时,MainMenu游戏状态将被挂起,并且PlayerControlGameState将被放入堆栈中,并现在接收输入事件。这样,您可以根据游戏状态来处理输入。在任何给定时间只有一个 Controller 处于 Activity 状态,您可以极大地简化控制流程。

输入收集由游戏循环触发。游戏循环基本上确定当前循环的帧时间,更新要素服务器,收集输入并更新游戏状态。帧时间或者被赋予这些功能的更新功能,或者由Timer单例提供。这是用于确定自上次更新调用以来的持续时间的规范时间。

游戏对象和功能

此设计的核心是游戏对象和功能的交互。
如上所示,这种意义上的功能是可以彼此独立实现的一项游戏功能。游戏对象是与玩家或任何其他游戏对象进行任何交互的任何事物。示例:玩家头像本身是游戏对象。火炬是游戏对象,NPC是游戏对象,照明区域和声源或它们的任意组合也是如此。

传统上,RPG游戏对象是某些复杂类层次结构中的顶级类,但实际上这种方法是错误的。许多正交方面不能放在层次结构中,甚至最后使用接口(interface)都必须具有具体的类。一个项目是一个游戏对象,一个可拾取的项目是一个游戏对象,一个胸部是一个容器是一个项目,但是使用这种方法使一个盒子成为可拾取或不拾取是一个或一个决定,因为您必须拥有一个层次结构。如果您想拥有一个只有在回答了一个谜语的情况下才会说话的魔术谜语箱子,事情就会变得更加复杂。没有一个完全适合的层次结构。

更好的方法是只具有一个游戏对象类,并将通常在类层次结构中表示的每个正交方面放入其自己的组件/功能类中。游戏对象可以容纳其他物品吗?然后向其添加ContainerFeature,是否可以说话,向其添加TalkTargetFeature,依此类推。

在我的设计中,GameObject仅具有固有的唯一ID,名称和location属性,其他所有内容均作为功能部件添加。可以在运行时通过GameObject接口(interface)通过调用addComponent(),removeComponent()添加组件。因此,要使其可见,请添加VisibleComponent,使其发出声音,添加AudableComponent,使其成为容器,再添加ContainerComponent。

VisibleComponent对于您的问题很重要,因为这是提供模型和 View 之间的链接的类。并非所有事物都需要古典意义上的观点。触发区域将不可见,环境声音区域也将不可见。只有具有VisibleComponent的游戏对象才可见。
当VisibleFeatureServer更新时,视觉表示将在主循环中更新。然后,它根据向其注册的VisibleComponents更新 View 。它是查询每个状态还是仅对从它们接收的消息进行排队取决于您的应用程序和基础可视化库。

就我而言,我使用Ogre3D。在这里,将VisibleComponent附加到游戏对象时,它会创建一个SceneNode,该SceneNode会附加到场景图,而场景节点会附加到Entity(3d网格的表示)。每个TransformMessage(请参阅下文)都将立即处理。然后,VisibleFeatureServer使Ogre3d将场景重绘到RenderWindow(本质上,细节一如既往地复杂)

留言内容

那么这些功能,游戏状态和游戏对象如何相互通信?
通过消息。此设计中的Message只是Message类的任何子类。每个具体的消息都可以具有自己的接口(interface),以方便其执行任务。

消息可以从一个GameObject发送到其他GameObject,也可以从GameObject发送到其组件,也可以从FeatureServer发送到它们负责的组件。

创建FeatureComponent并将其添加到游戏对象后,它会针对要接收的每个消息调用myGameObject.registerMessageHandler(this,MessageID)将自身注册到游戏对象。它还为要从那里接收的每条消息注册到其功能服务器。

如果玩家尝试与目标人物对话,则用户将以某种方式触发对话 Action 。例如:如果焦点所在的字符是友好的NPC,则可以通过按鼠标按钮触发标准交互。通过向其发送GetStandardActionMessage来查询目标游戏对象的标准 Action 。目标游戏对象接收该消息,并从第一个注册的对象开始,通知要了解该消息的功能部件。然后,此消息的第一个组件会将标准操作设置为将触发自身的操作(TalkTargetComponent会将标准操作设置为Talk,它将首先接收到该消息。),然后将消息标记为已使用。 GameObject将测试消耗情况,并查看是否确实消耗了它并返回给调用方。然后评估现在修改的消息,并调用结果操作

是的,这个例子似乎很复杂,但是已经是比较复杂的例子之一。诸如TransformMessage之类的用于通知位置和方向变化的其他对象则更易于处理。对于许多功能服务器来说,TransformMassage很有趣。 VisualisationServer需要它来更新GameObject在屏幕上的视觉表示。 SoundServer更新3d声音位置等。

使用消息而不是调用方法的优点应该很明显。组件之间的耦合较低。调用方法时,调用者需要了解被调用者。但是通过使用消息,这是完全解耦的。如果没有接收器,那就没关系。如果根本不关心接收者如何处理消息。
也许在这里代表是一个不错的选择,但是Java错过了一个干净的实现,并且在网络游戏的情况下,您需要使用某种具有较高延迟的RPC。低延迟对于交互式游戏至关重要。

持久性和编码

这使我们了解了如何通过网络传递消息。通过将GameObject/Feature交互封装到消息中,我们只需要担心如何通过网络传递消息。理想情况下,您将消息转换为通用格式,并将其放入UDP包中并发送。接收者将消息解压缩到适当类的实例,然后根据消息将其引导到接收者或广播。
我不知道Java的内置序列化是否可以完成任务。但是即使没有,也有很多库可以做到这一点。

GameObjects和组件通过属性使它们的持久状态可用(C++没有内置的序列化。)
它们具有类似于Java中的PropertyBag的接口(interface),可通过该接口(interface)检索和恢复其状态。

引用文献

  • The Brain Dump:专业游戏开发人员的博客。还是开源星云引擎(在商业上成功的游戏中使用的游戏引擎)的作者。我在这里介绍的大多数设计来自Nebula的应用程序层。
  • 上面博客中的
  • Noteworthy article,它列出了引擎的应用程序层。与我上面试图描述的另一个角度。
  • A lengthy discussion介绍如何布局游戏架构。主要是食人魔特有的,但也足够通用,对其他人也很有用。
  • Another argument for component based designs,底部有有用的引用。
  • 关于java - 程序中传递消息的数据结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1189236/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com