如何避免GameManager神物?


51

我刚刚阅读了有关构建游戏代码的问题的答案。这让我想知道普遍存在的GameManager类,以及它在生产环境中如何经常成为问题。让我描述一下。

首先,有原型。没有人会在乎编写出色的代码,我们只是尝试运行一些东西,以查看游戏玩法是否合计。

然后是一个绿灯,为了清理事物,有人写了一个GameManager。可能真的要抱一堆GameStates,也许要存储一些GameObjects,没什么大不了的。可爱的小经理。

在预生产的和平领域中,游戏的状态很好。编码人员有适当的睡眠时间,并且有很多想法可以使用出色的设计模式来构建事物。

然后开始生产,很快就会有紧要关头。均衡饮食早已一去不复返了,错误跟踪器正在解决问题,人们感到压力很大,游戏必须在昨天发布。

在那个时候,通常,这GameManager是一个很大的混乱(保持礼貌)。

原因很简单。毕竟,编写游戏时,嗯......所有的源代码实际上是在这里管理游戏。只需在中已添加了GameManager所有其他内容的位置添加此额外功能或错误修正,就很容易了。当时间成为问题时,就无法编写单独的类,也不能将此巨型管理器拆分为子管理器。

当然,这是一个经典的反模式:上帝对象。这是一件坏事,要合并的痛苦,要维持的痛苦,要理解的痛苦,要转变的痛苦。

您有什么建议可以防止这种情况发生?

编辑

我知道怪罪命名。当然,创建一个GameManager类并不是一个好主意。但同样的事情可以发生在一个干净的GameApplication或者GameStateMachineGameSystem那最终被管道胶带喜爱的一个项目的结束。不管命名如何,您都可以在游戏代码中的某个位置找到该类,但现在它只是一个雏形:您还不知道它将变成什么样的怪物。所以我不期待“怪罪命名”的答案。我想要一种防止这种情况发生的方法,一种编码架构和/或一个团队可以遵循的流程,因为他们知道这将在生产中的某个时刻发生。放弃一个月的错误修正和最新功能真是太糟糕了,因为所有这些现在都在一个庞大的无法维护的经理中。


无论是被称为GameManager还是ControllerOfTheGame,还是您想调用的任何名称,我的意思是避免使用一个类来控制整个游戏的逻辑。您知道无论执行什么计划调用它,都在执行操作。我认为我的上一个游戏有一个关卡控制器,刚开始只是为了保存关卡的状态而已,但是如果我停下来想一想,很明显,该对象本应处理更多的细节。
brandon '04年

@brandon让我为您重新回答一个问题:如何控制整个游戏的逻辑,而又不让自己暴露于GameManager上文所述的效果?
Laurent Couvidou 2012年

取决于逻辑。显而易见的东西,例如玩家,敌人,结构等,显然具有其自己的逻辑,被放入各自的类中。您似乎最想问的是处理游戏的状态。通常会有一个类来跟踪整个游戏的状态,但是根据我的经验,这是原型制作时您需要担心的最后一件事,其目的应该非常明确。基本上,您可以在原型制作之前进行一些初步计划,或者在开始生产时从头开始,以避免使用仅用于测试的垃圾类。
brandon'4

有趣的是,XNA为您造成了这种潜在的灾难。默认情况下,会创建一个Game类来管理所有内容。它包含主要的update,draw,initialize和load方法。实际上,我正在迁移到一个新项目,因为我(不太复杂)的游戏变得太难管理了,所有的东西都塞满了那个班级。此外,Microsoft教程似乎鼓励这样做。
Fibericon

Answers:


23

然后是一个绿灯,为了清理环境,有人编写了GameManager。可能要保留一堆GameState,也许要存储一些GameObject,实际上并没有什么大的。可爱的小经理。

你知道,当我读这篇文章的时候,我的脑海里几乎没有警报声。名称为“ GameManager”的对象永远不会变得可爱或很小。有人这样做是为了清理代码?以前看起来像什么?好吧,开个玩笑:一个班级的名称应该清楚地表明班级的工作,这应该是一回事(又名:单一责任原则)。

另外,您可能最终仍会得到像GameManager这样的对象,但是显然,它存在于非常高的层次上,它应该与高层次的任务有关。维护游戏对象目录?也许。促进游戏对象之间的交流?当然。计算对象之间的碰撞?没有!这也就是为什么经理这个名字不被接受的原因-它太宽泛了,并且在该旗帜下允许大量滥用。

关于类大小的快速经验法则:如果每个类遇到数百行代码,则开始出现问题。不必过于狂热,任何超过300 LOC的代码对我来说都是一种代码气味,如果您超过1000,警告铃应该响起。通过相信某种方式比4种结构良好的类(每类250条)更容易理解1000行代码,您正在自欺欺人。

当时间成为问题时,就无法编写单独的类,也不能将此巨型管理器拆分为子管理器。

我认为是这种情况,只是因为问题被允许传播到一切都是一团糟。重构的实践确实是您要寻找的- 您需要以很小的增量不断改进代码的设计

您有什么建议可以防止这种情况发生?

问题不是技术问题,因此您不应该为此寻求技术修复。问题是这样的:您的团队中有一种趋势是创建整体的代码段,并且相信这样的代码在中期/长期内会以某种方式受益。似乎团队也缺乏强大的架构领导力,他们无法控制游戏的架构(或者至少,这个人太忙于执行此任务)。基本上,唯一的出路是让团队成员认识到这种想法是错误的。它没有人支持。产品的质量会变差,团队只会花更多的时间来修理东西。

好消息是,编写干净的代码所带来的直接,实在的好处是如此之大,以至于几乎所有开发人员都很快意识到了自己的好处。说服团队以这种方式工作一段时间,结果将完成。

困难的部分是,要对构成错误代码的内容(以及快速提出更好的设计的才能)产生一种感觉,这是在开发中要学习的较难技能之一。我的建议取决于希望您的团队中有足够高级的人可以做到这一点-这样说服人们会容易得多。

编辑-更多信息:

总的来说,我认为您的问题不仅限于游戏开发。从本质上讲,这是一个软件工程问题,因此,我对此有何评论。我不确定游戏开发行业的性质,它是否比其他类型的开发更具结果性和针对最终期限。

但是,专门针对游戏开发,在StackOverflow上有关“特别是游戏架构”技巧的问题的公认答案说:

遵循面向对象设计坚实原则 ....

这基本上就是我所说的。当我承受压力时,我也发现自己在编写大量代码,但是我已经把它钻到脑海中,那就是技术债务。对我来说,行之有效的方法是花一天的前半部分(或四分之三)来制作大量中等质量的代码,然后坐下来思考一会儿。在我的头上或在纸/白板上做一些关于如何改善代码的设计。通常,我会注意到重复的代码,并且能够通过分解代码来减少代码的总行数,同时还能提高可读性。这次投资很快就收回了投资,以至于称其为“投资”听起来很愚蠢-如果我允许它继续下去,我经常会捡拾可能浪费了我半天(一周后)的错误。

  • 在编写代码的同一天修复问题。
  • 您会很高兴在几个小时内完成。

真正相信上述困难;我之所以能够为自己的工作做这件事,只是因为我一遍又一遍地体验到了效果。即便如此,当我可能会提出更多建议时,我仍然很难为修正代码辩解...所以我绝对知道您来自哪里。不幸的是,这个建议可能太笼统了,不容易做到。我对此深信不疑!:)

要回答您的特定示例:

Bob叔叔的Clean Code总结了高质量的代码是多么出色。我碰巧同意几乎所有内容。因此,当我想到您的30000 LOC经理班级示例时,我真的不同意“充分理由”部分。我不想冒犯他人,但以这种方式会导致问题。没有充分的理由在一个文件中包含这么多的代码-这几乎是1000页文本!局域性的任何好处(执行速度或设计“简单性”)将立即被开发人员完全束之高阁,他们试图解决这种怪异,甚至在我们讨论合并之前,都将使其无效。

如果您不相信,我最好的建议是拿一本上面的书,然后仔细阅读。将这种类型的思想应用到人们自愿创建干净,易于解释的代码中,该代码结构良好。


这不是我曾经见过的缺陷。我已经在几个团队的多个项目中看到了这一点。我见过很多有才华,有条理的人为此奋斗。在压力之下,300行代码限制不是您的生产者所关心的。玩家也不会。当然,这很不错,但是细节决定成败。GameManager到目前为止,我所看到的最坏的情况是大约30.000行代码,我可以肯定其中的大部分是有充分理由的。当然这是极端的,但是那些事情发生在现实世界中。我正在寻求一种限制这种GameManager影响的方法。
Laurent Couvidou 2012年

@lorancou-我在帖子中添加了很多额外的信息,评论太多了,希望它能给您更多的想法,即使我无法将您指向任何具体的体系结构例子。
Daniel B

啊,我没看那本书,所以绝对是个好建议。哦,我并不是说有充分的理由在数千行的类中添加代码。我的意思是,那种上帝对象中的所有代码都会做些有用的事情。我可能写得太快了;)
Laurent Couvidou 2012年

@lorancou Hehe,太好了...那就是精神错乱了:)
Daniel B

“重构的实践确实是您想要的-您需要以很小的增量不断改进代码的设计。” 我认为您在这里有很强的优势。混乱会吸引混乱,这是造成混乱的真正原因,因此必须长期应对混乱。我将接受这个答案,但这并不意味着其他都没有价值!
Laurent Couvidou 2012年

7

这实际上不是游戏编程的问题,而是一般的编程问题。

所有的源代码实际上都在这里管理游戏。

这就是为什么您不应该对任何建议使用名称中带有“ Manager”的类的面向对象编码器感到不满意的原因。公平地讲,我认为在游戏中避免“半身像”对象几乎是不可能的,但我们仅将它们称为“系统”。

截止日期临近时,任何软件都容易变脏。那是工作的一部分。“风管类型编程 ” 并没有天生的错误,特别是当您必须在几个小时内发货时。您不能完全摆脱这个问题,您可以减少它。

虽然很明显,如果您很想在GameManager中放置东西,这意味着您没有一个非常健康的代码库。可能有两个问题:

  • 您的代码太复杂。模式太多,过早的优化,再也没有人了解流程了。如果可以的话,尝试在开发时使用更多的TDD,因为这是解决复杂性的很好的解决方案。

  • 您的代码结构不正确。您正在(ab)使用globals变量,这些类之间没有松散耦合,没有任何接口,没有事件系统,依此类推。

如果代码看起来不错,您将不得不记住,对于每个项目,“出了什么问题”并在下次避免使用。

最后,这也可能是团队管理的问题:时间是否足够?大家都知道您要使用的架构吗?到底谁允许提交带有单词“ Manager”的类的提交?


胶带编程没有错,我给你。但是我非常确定-Manager类在游戏引擎中无处不在,至少我已经看过很多。只要它们不会变得太大,它们就很有用。a SceneManager或a NetworkManager怎么了?我不明白为什么我们应该阻止它们,或者用-System代替-Manager会有什么帮助。我正在寻找一种替代架构的建议,以取代通常GameManager不易发生代码膨胀的东西。
洛朗·库维杜

SceneManager有什么问题?好的,Scene和SceneManager之间有什么区别?网络和网络管理员?另一方面:我不认为这是体系结构问题,但是如果您可以给出GameManager中存在但不应存在的具体示例,则可以更轻松地提出解决方案。
Raveline

OK,所以就不用管-Manager了。假设您有一个处理游戏状态的类,可以称之为GameStatesCollection。刚开始时,您只有几种游戏状态以及在它们之间进行切换的方式。然后就是加载屏幕,这使所有这些事情变得复杂。然后,当您从Level_A切换到Level_B时用户弹出光盘时,某些程序员必须处理一个错误,而无需花费半天的重构就可以修复光盘的唯一方法是in GameStatesCollection。景气,神的对象。
Laurent Couvidou 2012年

5

因此,从更多的Enterprise-Y世界引入一种可能的解决方案:您是否检查了控制/依赖注入的反转?

基本上,您会获得一个框架该框架是游戏中其他所有内容的容器。它并且只有它知道如何将所有内容连接在一起,以及对象之间的关系。您的任何对象都没有实例化其自己的构造函数中的任何内容。他们没有从IoC框架中寻求所需的东西,而没有创建所需的东西。创建后,IoC框架“知道”需要什么对象来满足依赖关系并填充它。FTW耦合松动。

当您的EnemyTreasure类向容器询问GameLevel对象时,框架就会知道要为其提供哪个实例。怎么知道 也许它更早地实例化了它,也许它问了附近的GameLevelFactory。重点是,EnemyTreasure不知道GameLevel实例来自何处。它只知道它的依赖关系现在已经满足,并且可以继续生活。

哦,我看到您的PlayerSprite类实现了IUpdateable。很好,因为框架会自动将每个IUpdateable对象订阅到Timer,这是主要游戏循环所在的位置。每个Timer :: tick()都会自动调用所有IUpdatedable :: update()方法。容易吧?

实际上,您不需要使用bighugeohmygod框架即可为您执行此操作:您可以自己完成其中的重要工作。关键是,如果发现自己制作了-Manager类型的对象,那么很可能是,您没有在各个类之间正确分配职责,或者缺少某些类。


有趣的是,我第一次听说IoC。对于游戏编程而言,这可能太大了,但我会检查一下!
Laurent Couvidou 2012年

IoC,难道我们不只是用常识来称呼它吗?
brice 2012年

只要有一半的机会,工程师就可以从任何东西中构建一个框架。(=另外,我不主张用,我崇尚架构模式的框架。
drhayes

3

过去,如果执行以下操作,则通常会获得神职课程:

  • 在我写下类图(或只是主要对象的草图)之前,先坐在游戏编辑器/ IDE /记事本上
  • 从一个非常模糊的游戏构想开始,并立即对其进行编码,然后在出现新构想时开始对其进行迭代
  • 当我不制作基于状态的游戏(如基于回合的策略,其中GameManager实际上是域对象)时,请创建一个名为GameManager的类文件
  • 不在意早期的代码组织

这可能会引起争议,如果确实如此,我会大笑,但是即使在进行原型设计时,我也无法想到您在编写可玩的原型之前不应该编写非常基本的类图。我确实倾向于在计划之前测试技术创意,但是仅此而已。因此,如果我想制作门户网站,我会写一些非常基本的代码来使门户网站正常工作,然后说好的时间进行一些较小的计划,然后我就可以开发可玩的原型了。


1

我知道这个问题现在已经很老了,但我想我要加2美分。

在设计游戏时,我几乎总是有一个Game对象。但是,这是一个很高的级别,它本身并没有真正“做任何事情”。

例如,它告诉Graphics类为Render,但与渲染过程无关。它可能会要求LevelManager加载新的关卡,但与加载过程无关。

简而言之,我使用一个半神对象来协调其他类的使用。


2
这根本不是很像上帝的东西-这称为抽象。:)
沃恩(Vaughan)击中了
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.