如何组织游戏代码以适合MVC模式?


71

我是大学一年级的学生,攻读计算机科学学位。最近几年,我编写了很多程序,但是最近,我对结构代码,设计模式,语言差异等方面的理论有了更多的了解。

我有一个Java类,所以我放弃了C ++的研究/开发,而转到了Java和JOGL(Java OpenGL)。太好了!但这不是重点。

我想制作一个小型的角色扮演游戏,但是这个问题确实适用于任何类型的游戏。您如何以结构化的方式组织游戏对象,例如Model-View-Controller模式?它看起来是一种了不起的模式,已经被广泛使用并且很有道理,但是我在弄清楚如何实现它方面遇到了麻烦。

例如,我需要跟踪GL对象以绘制到屏幕上。我必须具有实现MouseListener,MouseMotionListener,MouseWheelListener和KeyListener的类(或一个类,多合一输入管理器)。而且我必须将游戏数据放在所有这些不同类都可以访问和修改的地方。如果有人按下键盘上的按钮,则输入管理类需要以某种方式执行键所映射的操作;当需要绘制框架时,图形类需要找到一种方法来遍历所有不同的“事物”并全部绘制。

而我最大的问题是GUI;它在哪里联系在一起?这有点像输入,但不完全相同,它需要从实际的游戏模拟中设置和获取数据。而且,如果我决定尝试添加网络,则更加复杂的是(类似于GUI) )还需要访问大量数据以进行修改和读取...

哦,我都很困惑。我不知道如何将所有这些以面向对象的方式一起工作……编写清楚适合模式的东西很容易,但是当您发生大量事情时,所有事情都绑在一个游戏循环上,互相修改以及游戏数据等等,……我什至不知道。也许我只是在做比实际更大的交易。

其他人有这种感觉吗?请澄清一下我的处境,这样我就可以花更少的时间去担心和不知道从哪里开始!

编辑:找到了一个不错的图表,可能会帮助我解决所有问题...来源:(请注意,PS文件!)http://www.tucs.fi/publications/attachment.php?fname=TR553.ps.gz

Edit2:我也喜欢这个人对他如何计划他的MVC游戏的解释:http : //interactivesection.wordpress.com/2007/11/19/dum-de-dum-drum-my-first-mvc-game-development/

Edit3:另一篇很棒的文章! http://dewitters.koonsolo.com/gamemvc.html

Answers:


56

可能会帮助您将模型视为一种游戏API。如果从一开始就没有针对该游戏的用户界面,那么您的游戏将沦为什么?您提到的是,您所想到的是RPG,因此在这种情况下,您可以想象拥有角色,他/她的物品,咒语,能力,NPC甚至地图和战斗规则之类的东西都属于模型。就像垄断的规则和部分一样,没有最终游戏如何显示该细节或用户如何与之交互的细节。就像《雷神之锤》一样,它是一组抽象的3D对象,它们在一个关卡中移动,并计算出诸如相交和碰撞之类的东西,但没有渲染,阴影或声音效果。

通过将所有这些都放入模型中,游戏本身现在与UI无关。它可以像Rogue游戏一样挂接到ASCII文本界面,或者类似于Zork的命令行UI,或者基于Web的3D UI。根据游戏机制的不同,其中一些用户界面可能非常糟糕,但它们都是可能的。

视图层是UI依赖层。它反映了您使用的UI的特定选择,并将非常致力于该技术。它可能负责读取模型的状态,并以3D,ASCII或网页的图像和HTML绘制模型状态。它还负责显示玩家与游戏互动所需的任何控制机制。

控制器层是两者之间的粘合剂。它永远不应包含任何实际的游戏逻辑,也不应负责驱动View层。相反,它应该将在“视图”层中执行的操作(单击按钮,单击屏幕区域,操纵杆操作等)转换为对模型执行的操作。例如,放下任何物品,攻击NPC,等等。它还负责收集数据并进行任何转换或处理,以使View图层更易于显示它。

现在,我在上面描述的方式好像是驱动游戏的事件序列非常不同,这可能仅非常适合于网络游戏。那是因为那是我最近花费的时间。在不是由用户的请求和服务器的响应驱动的游戏中(例如网络)(例如,在用户计算机上运行的游戏),您可能需要确保Model层很好地实现了Observer模式。例如,如果由于时间流逝而在模型中进行操作,那么您可能不希望View层不断轮询模型以获取更新。相反,通过使用观察者模式,模型可以在模型对象发生更改时通知任何观察者。反过来,这可以用于提示立即更新视图以反映更改。

然后,如果经过60秒导致玩家的基地进行了一些维修,则该基地可能会进行维修,并立即通知与之相连的所有观察者该基地已经更新。视图可以作为观察者附加,请注意,由于其状态已更改,因此需要重新显示基础。通知本身可能已经包含了足够的信息来更新View,或者它可能必须转过身来咨询模型以进行更新,但是结果将是相同的。


19

您正在那里相处。基本上,问自己一个问题:“如果必须更改程序的某些部分,将会更改哪些代码?”

如果它可以改变外观而不改变基本数据,那么它就在视图中。如果是可以以多种方式查看的数据,那就是模型。如果这是您的游戏方式,那么它就是控件。

因此,无论是用两个刀片还是一个刀片绘制“轴”,它都是视图。如果这是您用斧头造成多少生命值伤害,那就是模型。而且,无论是通过键入“ s”还是右键单击来挥动斧头,都可以控制。


8

我与您在一起,我记得当我第一次发现MVC时,我尝试将所有内容塞入其中。我确实做了一款利用MVC模式的游戏。不过,后来我发现我做的太过分了。在MVC中,我几乎设法使我制作的每个类别都适合一个类别。

我建议阅读由四人组成的“设计模式”。除了MVC,还有许多有用的模式。有时,完全不使用MVC毫无意义。尤其是对于游戏,我不确定MVC是否是个好主意。原因是您不想以许多不同的方式(视图)显示游戏对象,但是您想对许多不同类型的游戏对象重用绘图代码。

对于我自己的2D游戏引擎,我非常积极地使用了策略模式。游戏对象,例如玩家和我称为Sprite的怪物。我让精灵的绘制由策略模式处理。那是我打电话的时候sprite.draw()时,我会做这样的事情:

class Sprite {
  void draw() {
    this.view.draw(this.currentPosition, this.currentOrientation);
  }

  Point  currentPosition;    // Current position of this sprite
  double currentOrientation; // Facing angle of sprite
};

这种方法的好处是您可以在多个子画面之间共享视图对象。因为通常会有很多例如怪物,它们看起来相同,但是位置不同并且行为可能不同。

因此,行为我还将使用一种策略模式,该策略模式将是一个包含描述行为的代码的对象。这样,我可以将相同的行为应用于不同位置的多个怪物。因此,每帧我都会调用一个update()函数来更新位置方向和怪物做什么。

class Sprite {
  void setUpdateAction(Action action) {
    this.updateAction = action;
  }

  void update(double start_time, double delta_time)
  {
    this.prevPosition = position();  
    advance(delta_time); // Advance to next position based on current speed and orientation

    this.updateAction.execute(this, start_time, delta_time);
  }

  Action updateAction;
};

有很多这种变化。在我当前的实现中,我什至将currentPositionspeedorientationadvance()分离到一个名为MotionState的单独对象中。这样一来,我在进行路径搜索算法时就可以构建可能位置和方向的搜索树。然后,我不想随身携带有关如何进行每次更新或应如何绘制精灵的信息。


我真的很喜欢在游戏中使用策略模式的想法。我从未开发过游戏,但是从设计的角度来看,战略模式对我来说很有意义。
Hooray Im帮助

1
进一步了解这个想法,您最终将获得一个基于组件的系统
pek

2

集中用户交互逻辑的MVC概念是游戏开发的良好模型。

我已经完成了Flash游戏开发方面的一些工作。 是一篇有关Flash中对象池的文章。这个概念是跨平台的,可能会给您一些想法。

您一次关注所有正在发生的事情是正确的。根据您的游戏设计,您的游戏循环可能需要处理很多事情。在这里,您将学到所有肮脏的优化技巧,通常是很难的方法:)

有多种方法来组织代码-一种选择可能是将GameManager类编写为Singleton。所有游戏对象都与之绑定,以进行管理和用户交互。GameManager处理所有用户输入并将消息分发到其对象池。您可以使用界面来定义游戏对象和GameManager之间的通用通信模式。

就性能优化而言,线程功能非常强大。异步操作可以确保您不会浪费那些宝贵的周期。


您的链接已死。从Java版本1.4开始,对象池在大多数情况下是没有用的。我不确定使用单例是否绝对必要,并且我已经看到许多游戏项目滥用管理器,而设计不良的管理器可能是内存泄漏的一个好来源。控制对象的生命周期比将沙拉和胡萝卜放到一个管理器中更为有用。我的意思是,避免在无用的对象上保留引用将使垃圾收集器完成足以管理托管对象的工作,并且您必须自行销毁非托管对象。
gouessej 2014年

1

您所有的侦听器和处理程序都需要进入Controller类,屏幕上对象的状态(例如,位置,颜色等)应属于Model类的一部分,而任何在屏幕上绘制事物的代码都将成为其中的一部分风景。


1

我对MVC的思考方式是作为MDUC
模型
显示
用户输入控制器

该模型包含域模型对象
。显示屏在屏幕上显示域模型对象的当前状态和行为。
用户输入控制器处理所有用户输入。

这是完全相同的模式,但是名称的描述性才稍微高一点,因此我发现模式的每个部分所承担的职责更加清楚,因此模式的含义更加令人难忘。

一旦看到从用户的输入中分离显示和拆分数据和模型操作之后,就可以更轻松地看到将您的代码中的内容分组的位置。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.