- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试编写一个简单的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/destroy
和resume/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)检索和恢复其状态。
引用文献
关于java - 程序中传递消息的数据结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1189236/
我一直在读到,如果一个集合“被释放”,它也会释放它的所有对象。另一方面,我还读到,一旦集合被释放,集合就会释放它的对象。 但最后一件事可能并不总是发生,正如苹果所说。系统决定是否取消分配。在大多数情况
我有一个客户端-服务器应用程序,它使用 WCF 进行通信,并使用 NetDataContractSerializer 序列化对象图。 由于服务器和客户端之间传输了大量数据,因此我尝试通过微调数据成员的
我需要有关 JMS 队列和消息处理的帮助。 我有一个场景,需要针对特定属性组同步处理消息,但可以在不同属性组之间同时处理消息。 我了解了特定于每个属性的消息组和队列的一些知识。我的想法是,我想针对
我最近开始使用 C++,并且有一种强烈的冲动 #define print(msg) std::cout void print(T const& msg) { std::cout void
我已经为使用 JGroups 编写了简单的测试。有两个像这样的简单应用程序 import org.jgroups.*; import org.jgroups.conf.ConfiguratorFact
这个问题在这里已经有了答案: Firebase messaging is not supported in your browser how to solve this? (3 个回答) 7 个月前关
在我的 C# 控制台应用程序中,我正在尝试更新 CRM 2016 中的帐户。IsFaulted 不断返回 true。当我向下钻取时它返回的错误消息如下: EntityState must be set
我正在尝试通过 tcp 将以下 json 写入 graylog 服务器: {"facility":"GELF","file":"","full_message":"Test Message Tcp",
我正在使用 Django 的消息框架来指示成功的操作和失败的操作。 如何排除帐户登录和注销消息?目前,登录后登陆页面显示 已成功登录为“用户名”。我不希望显示此消息,但应显示所有其他成功消息。我的尝试
我通过编写禁用qDebug()消息 CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG_OUTPUT 在.pro文件中。这很好。我想知道是否可以
我正在使用 ThrottleRequest 来限制登录尝试。 在 Kendler.php 我有 'throttle' => \Illuminate\Routing\Middleware\Throttl
我有一个脚本,它通过die引发异常。捕获异常时,我想输出不附加位置信息的消息。 该脚本: #! /usr/bin/perl -w use strict; eval { die "My erro
允许的消息类型有哪些(字符串、字节、整数等)? 消息的最大大小是多少? 队列和交换器的最大数量是多少? 最佳答案 理论上任何东西都可以作为消息存储/发送。实际上您不想在队列上存储任何内容。如果队列大部
基本上,我正在尝试创建一个简单的 GUI 来与 Robocopy 一起使用。我正在使用进程打开 Robocopy 并将输出重定向到文本框,如下所示: With MyProcess.StartI
我想将进入 MQ 队列的消息记录到数据库/文件或其他日志队列,并且我无法修改现有代码。是否有任何方法可以实现某种类似于 HTTP 嗅探器的消息记录实用程序?或者也许 MQ 有一些内置的功能来记录消息?
我得到了一个带有 single_selection 数据表和一个命令按钮的页面。命令按钮调用一个 bean 方法来验证是否进行了选择。如果不是,它应该显示一条消息警告用户。如果进行了选择,它将导航到另
我知道 MSVC 可以通过 pragma 消息做到这一点 -> http://support.microsoft.com/kb/155196 gcc 是否有办法打印用户创建的警告或消息? (我找不到谷
当存在大量节点或二进制数据时, native Erlang 消息能否提供合理的性能? 情况 1:有一个大约 50-200 台机器的动态池(erlang 节点)。它在不断变化,每 10 分钟大约添加或删
我想知道如何在用户登录后显示“欢迎用户,您已登录”的问候消息,并且该消息应在 5 秒内消失。 该消息将在用户成功登录后显示一次,但在同一 session 期间连续访问主页时不会再次显示。因为我在 ho
如果我仅使用Welcome消息,我的代码可以正常工作,但是当打印p->client_name指针时,消息不居中。 所以我的问题是如何将消息和客户端名称居中,就像它是一条消息一样。为什么它目前仅将消
我是一名优秀的程序员,十分优秀!