在游戏编程中全局变量不好吗?

Joe*_*e.F 24 c++ global-variables

我知道我对全局变量的直觉反应是"糟糕的!" 但是在我的大学全球比赛中使用的两个游戏开发课程被广泛使用,现在我正在使用的DirectX 9游戏编程教程(www.directxtutorial.com)我被告知全局游戏编程是可以的. ..?该网站还建议仅使用结构,如果您可以在进行游戏编程时帮助保持简单.

我在这个问题上真的很困惑,我一直在努力做的所有研究都很混乱.我意识到使用全局变量存在问题(线程问题,它们使代码难以维护,它们的状态难以跟踪等)但是还有与不使用全局变量相关的成本,我必须通过一个loooot虽然我觉得指针会加快这个过程(这是我第一次用C++编写游戏.)无论如何,我意识到可能没有"正确"或"错误"的答案在这里,因为两种方式都有效,但我希望我的代码尽可能正确,所以任何输入都会很好,非常感谢你!

Aid*_*ell 37

游戏和全局游戏的问题在于游戏(现在)在引擎级别进行了线程化.使用引擎的游戏开发人员使用引擎的抽象而不是直接编程并发(IIRC).在许多高级语言(如C++)中,线程共享状态很复杂.当许多并发进程共享一个共同资源时,他们必须确保它们不会踩在彼此的脚趾上.

要解决这个问题,可以使用并发控制,例如互斥锁和各种锁.这实际上使得代码的异步关键部分以同步方式访问共享状态以进行写入.并发控制的主题太多了,无法在此完全解释.

可以说,如果线程使用全局变量运行,它会使调试变得非常困难,因为并发错误是一场噩梦(想想,"哪个线程写了这个?谁持有那个锁?").

游戏编程API中有例外,例如OpenGL和DX.如果您的共享数据/全局变量是指向DX或OpenGL图形上下文的指针,那么通常这会映射到GPU操作,这些操作不会遭受同样的麻烦.

小心点 保持代表"玩家"或"僵尸"或其他任何东西的对象,并在线程之间共享它们可能会很棘手.产生'玩家'线程和'僵尸组'线程,并且基于消息传递在它们之间具有强大的并发抽象,而不是在线程/临界区边界上访问那些对象的状态.

说了这么多,我同意下面的"对全球化说不"的观点.

有关线程和共享状态的复杂性的更多信息,请参阅:

1 POSIX Threads API - 我知道它是POSIX,但提供了一个好主意,转换为其他API
2 Wikipedia关于并发控制机制的优秀文章
3 The Dining Philosopher的问题(以及许多其他)
4 ThreadMentor关于线程的教程和文章
5 另一个英特尔的文章,但更多的是营销的东西.
6 一篇关于构建多线程游戏引擎的ACM文章


Sha*_*rog 24

曾经参与AAA游戏,我可以告诉你,全球化应该在它们像癌症一样传播之前立即根除.我已经看到它们完全损坏了I/O子系统,因此必须完全抛弃它才能被重写.

对全局变量说不.总是.

  • +1"对全局说不" - 那里有一件T恤. (12认同)
  • @John,我在这里看不到相关性 - 你不需要OO来删除全局变量.您可以编写一个C应用程序,其中包含函数和结构中的所有内容,而不是全局内容. (3认同)
  • @Necrolis:在单例中包装全局变量只是假装你修复了问题.强迫它OO不会让它变得更好. (3认同)
  • 哼......但单身人士和全球人之间的区别是什么?全局变量并不坏,因为它们被标记为"全局变量"它们很糟糕,因为它们引入了全局共享状态,我无法看到Singleton为该特定问题带来了什么(忽略初始化/破坏问题). (3认同)
  • @Matthieu M.两者之间没有太大区别.一个人只是有一个奇特的名字. (3认同)
  • @John我同意,单身人士至少和全球人一样糟糕. (2认同)
  • @Necrolis 没错,单身人士*是*全球性的。 (2认同)

Eli*_*sky 7

在这方面,游戏和其他程序之间没有区别.虽然在小学课程中给出的小例子中可以说是好的,但在实际课程中强烈反对全球变量.

因此,如果您想编写正确,可读和可维护的代码,请尽可能远离全局变量.


Dav*_*eas 6

到目前为止所有答案都处理全局/线程问题,但我会将2美分加到struct/class(理解为所有公共属性/私有属性+方法)讨论中.由于更简单,更喜欢使用汇编语言而不是C++,因此优先选择基于类的结构,这是因为它是一种更简单的语言.

您必须考虑如何使用实体并为其提供方法这一事实使得具体实体稍微复杂一些,但却极大地简化了代码的其余部分和可维护性.封装的重点在于它通过提供清晰的方式简化程序,以便在维护对象不变量的同时修改数据.您可以控制入口点以及可能发生的情况.公开所有属性意味着代码的任何部分都可能有一个小的无辜错误(忘记检查条件X)并完全打破你的不变量(健康低于0,但没有'死'处理被触发)

另一个常见的讨论是性能:如果我只需要更新数据,那么必须调用方法将影响我的性能.并不是的.如果方法很简单,并且您在标题中将它们作为内联提供(在类主体内部或在外部使用inline关键字),编译器将能够将这些指令复制到每个使用位置.您可以保证编译器不会错误地遗漏任何检查,并且不会影响性能.

  • 使用内联仍然会影响性能.如果你内联到代码的关键部分不再适合缓存,你可能会发现由于缓存未命中而造成的速度损失. (3认同)
  • @David结构和类在C++中基本相同 (2认同)
  • @Shaggy Frog:如果你是手动复制方法而不是让编译器内联它,你就不会在性能方面获得任何收益,但在维护方面却有一大堆伤害......无论如何`inline`是一个暗示,甚至VS`__forceinline`是暗示;) (2认同)