用功能语言编写游戏有哪些挑战和好处?


81

虽然我知道功能语言并不是游戏编写中最常用的语言,但与它们相关的许多好处似乎使它们在任何编程环境中都非常有趣。特别是当我将注意力集中在越来越多的处理器上时,我认为并行的简易性将非常有用。

另外,将F#作为.NET系列的新成员,它可以直接与XNA一起使用,与使用LISP,Haskell,Erlang等相反,它可以大大降低阈值。

如果有人有使用功能代码编写游戏的经验,那么正面和负面是什么呢?它适合什么,不适合什么?

编辑:很难确定对此有一个好的答案,因此它可能更适合作为社区Wiki帖子。


3
我曾经用LISP编写过我最喜欢的游戏之一(《无赖》),这个问题(及其潜在答案)对我来说很有趣。
贾斯汀·L.2010年

不知道Rogue是用LISP编写的,但是觉得很有趣。经典游戏。:)
McMuttons 2010年


2
我敢肯定您对使用C语言的原始Rogue是正确的,因为它是在Unix上编写的,但是Lisp 中肯定有几个Rogue 克隆cl-user.net/asp/root-dir(不是直接的Game链接) ,它有太多特殊字符,似乎链接已断开)。
独眼巨人

Answers:


57

我目前正在Haskell从事游戏。我不能说一般的函数式编程,但是特别是对于Haskell:

善良

这些都是让Haskell真正脱颖而出的好东西。

  • 纯粹意味着对代码的推理要容易得多。您不必担心变量的“当前”值,也不必担心使用给定函数是否会在某种程度上与全局状态冲突。这也意味着并行性和并发性要容易得多(在许多情况下是微不足道的)。这些东西可能是游戏开发者的天赐之物。
  • Haskell使您可以非常轻松地使用您自己创建的抽象来进行高级编写。与Haskell中的专用代码相比,编写非常通用的代码通常要容易得多,而这样做的好处体现在更少的开发时间和更容易的代码理解上。在Haskell中,这比我所知道的任何其他语言都更为真实,使用接口就像使用全新的语言一样(同时仍保留了Haskell生态系统其余部分的优势)。大多数语言都称其为语言功能,Haskell则称其为库,并且感觉并不强迫。如果您发现自己在psuedocode中对游戏进行推理,则可以编写一个简单的界面并将其制作为真实代码,这样做通常是值得的。
  • 这可能与其他答案冲突,但是Haskell在处理命令性代码方面比我所知道的任何其他语言都更好。纯度的约束意味着Haskell在“评估”(减少表达式)和“执行”(执行副作用)的概念之间有分隔。您可以轻松控制何时以及如何执行副作用,这是所谓的“命令式”语言所无法比拟的。尽管我之前提到过纯度,但是某些C绑定仍然是非常必要的(OpenGL,我在看着你),因此非常欢迎对命令式代码进行这种细粒度的控制。一旦习惯了,即使是最老练的C程序员也可能会喜欢它。
  • GHC生成非常快速的二进制文件。它们的速度不如普通C编译器生成的二进制文件快,而是在一个数量级之内,并且比使用解释语言所实现的速度快得多。即使由于速度太慢而不能用Python等其他高级语言编写游戏,也很有可能在Haskell中进行游戏。
  • 尽管Haskell中没有很多与游戏相关的库,如下面的“错误”所述,但Haskell外部函数接口是我使用过的最简单的函数。您可以通过几乎完全用Haskell编写来绑定到C。

坏人

这些都是不好的事情,但无需过多努力即可解决。

  • 如果您非常习惯使用非功能语言,则学习Haskell所需的时间和精力可能会相当多。语言本身并不是很困难,但是当您考虑公共语言扩展和大量的库时(请记住,您可能会考虑其他语言的语言特性的很多东西都是Haskell中的库),它看起来更加不祥。我认为这是值得的,但其他人可能会不同意。
  • 有些人抱怨懒惰。如果您知道自己在做什么,就不会造成难以跟踪的空间泄漏(我几年来还没有造成空间泄漏),但是初学者在这方面遇到了更多麻烦。击落这些人并声称这不是问题,这对我来说是愚蠢的,但我会断言,凭经验可以轻松解决这个问题。
  • 在Haskell中,没有很多优秀的游戏制作库,而且Haskellers也不在其中编写游戏,因此在这方面很难找到资源和提供帮助。

丑陋的

这些都是需要大量努力才能克服的事情。

  • 如果您正在编写占用大量资源的游戏,GHC的垃圾收集器可能会非常咬您。这是一个世代相传的GC,因此有时您可能会丢掉几帧。对于某些游戏而言,这是可以的,但对于其他游戏而言,这是一个主要的问题。我和其他使用Haskell的游戏开发人员希望看到为GHC实现的软实时GC,但据我所知,目前还没有为此付出任何努力。目前,我不担心解决此问题(并且尚未从中看到任何丢帧的情况),但是至少另一个团队已将其主要渲染循环放入C中。

2
您如何看待玻璃钢?
胡虎2010年

4
@Wei Hu:我是FRP概念的忠实拥护者,而且我本人也对此进行了广泛的研究,包括创建一些不同的语义并编写了一些实现。最佳实现仍然存在严重的语义和/或实现错误。我想说它们对于游戏开发是可行的,但相对于传统方法还没有很多优势。
杰克·麦克阿瑟

1
一位匿名用户试图编辑此答案,以消除“丑陋”的部分。“从2011年开始,Haskell中的垃圾收集器现在是并发的,并行的和世代的(ghc.haskell.org/trac/ghc/blog/new-gc-preview)”
Jari Komppa

1
不幸的是,事实并非如此。并发GC被认为是过于复杂,证明小整体改善,到现在也没有被合并英寸
杰克麦克阿瑟

1
@ Hi-Angel有一个旧的GHC后端生成C,但是它与本机代码生成器或LLVM后端没有什么不同。它仍然需要GHC运行时。而且,C后端没有什么特别之处,它可以使避免垃圾回收器变得更加容易。如果消除GC非常容易且便宜,那么其他后端也可能会这样做。
杰克·麦克阿瑟

19

去年,我花了很多时间在Haskell上开发了一款商业游戏引擎,对我们来说,这种体验是非常积极的。我们的游戏世界非常复杂,Haskell使建模从编辑器格式到游戏引擎格式的转换过程变得容易。我不愿意想到命令式语言中的代码是什么样的。

有时会出现空间泄漏,虽然它们引起了一些麻烦,但在一般方案中,泄漏量很小(例如,与在类似大小的Java项目中发现死锁相比),并且一旦修复,他们保持不变。

我们正在使用类似于Yampa的FRP,并且肯定有一个学习曲线,但是一旦结束,体验就会非常积极。图书馆对我们来说不是问题-我们所需要的一切都已经可用。垃圾收集延迟是一个特别的问题,因为它是针对嵌入式平台的。我们使用了一些C ++来管理动画。作为嵌入式平台(=慢速处理器),性​​能也是一个问题。我们已经完成了一些C的工作,并且还在研究新兴的Haskell技术,例如加速。C ++动画师很早就做出了设计决定,代码太慢的地方只有很小的一部分。从长远来看,我们希望将所有C转换为Haskell。

Haskell使困难的工作变得容易,与我们所产生的大量清晰,可维护且几乎坚不可摧的复杂代码相比,我刚才提到的所有困难都很小。并行性将很快成为游戏开发中的一个问题,我们非常有能力解决它。我说过的某些内容可能不适用于小型项目,因为从长远来看,这是我们的职责所在,因此,学习曲线,库支持等启动成本就不再是问题。


7
自您发布此答案以来已经快5年了;您愿意更新吗?商业游戏引擎如何运作?哪些作品被证明是有价值的?您是否能够像希望的那样利用并行性?
维·莫里森

17

戴夫提到了一些出色的观点,尽管我必须指出,哈斯克尔已经解决了他的两个问题。可以使用State monad来绕过无状态(编辑:并非如此,有关详细信息请参见下文),可以使用IO monad来处理排序(就此而言,可以使用EDIT:或任何其他monad ...)

您所面临的挑战(以及我曾尝试学习游戏编程和Haskell的挑战)更多地沿着这些思路。(这些都是特定于Haskell的,因为我还没有深入了解任何其他FP语言。)

  • FP学习曲线: FP需要从迭代编程中彻底转变观念。如果您不习惯使用地图和折叠而非环圈来思考,则需要进行心理锻炼。
  • 数学学习曲线: Haskell对其设计人员的数学技巧既有福又受诅咒,因为在学习FP的基础知识之后,您会陷入单子,同构,箭头和其他许多注意事项的考虑,这些因素虽然不难本身是非常抽象的。
  • Monads:这些幼犬并不是特别难缠,尤其是当您接受Eric Raymond的声明时,即它们是“将功能组合转变为强制函数调用排序的一种手段”。不幸的是(尽管随着Haskell的流行而变得越来越好),对monad的许多解释要么以大多数程序员的头(“它们是endofunctors的范畴中的类人动物!”)为目标,要么是完全无益的类比(例如,布伦特·约热(Brent Yorgey)令人痛苦的准确例子:“ 单子就像墨西哥卷饼!”您认为他在开玩笑吗?没人会在教程中提出如此愚蠢的比喻吗?再想一想。)
  • 生态系统:如果您已经成功地克服了道路上的其他所有障碍,那么您将面对与使用仍然主要是实验性语言的日常实际情况的争执。当您到达这里时,上帝会帮助您。这里就是我开始认真jonesing斯卡拉,或F#,或其他一些语言,其中至少有一些与其他图书馆的互操作性的保证。从前,我让Haskell链接并在Windows中加载OpenGL库。那让我感觉很好。然后OpenGL绑定重新发布,破坏了我所做的一切。那就是Haskell-land的生活。老实说,这可能会很痛苦。您想要子弹引擎的包装器吗?在黑客中-作为废弃软件从去年11月开始。你喜欢Ogre3d吗?黑客绑定了一部分库。最重要的是,如果您坚持不遵循常规的语言进行游戏开发,那么您将花费更多的时间来进行基本的管道开发工作,这要比仅仅跟随一群人并坚持使用C ++花费的时间更多。

不利的一面是,事情正在迅速改善。这实际上取决于您想要从体验中获得什么。您想开发一款游戏并将其放在您的网站上以寻求名利吗?坚持使用C ++或Python。但是,如果您想学习一些需要创新的新知识,请尝试使用一种功能语言。在学习时,要对自己有很大的耐心。

Antti Salonen有一个有趣的博客,详细介绍了他与Haskell游戏编程的断断续续的关系。值得一读。

编辑(一年后):现在,我对State monad进行了更多研究,我意识到对于打算保留在特定功能之外的State来说,这不是一个特别好的解决方案。真正的无状态解决方案可在IOVar,ST,MVar(用于线程安全)的Haskell中找到,或通过类似Yampa的方法找到,Yampa使用Arrows和FRP管理仍然对开发人员隐藏的内部状态。(此列表是按难度顺序排列的,尽管一旦您理解了monad,前三个并不是特别困难。)


1
即使是Haskell特有的,这里也有一些好的想法,我认为这些想法适用于大多数附带语言。正如您简要提到的那样,我认为这是F#之类的语言具有潜力的地方。处理状态/ UI用C#,例如,然后在F#逻辑模块等潜在路径寻找,AI等算法
McMuttons

5

客观的骆驼!

在大多数类型的软件开发中,使用功能语言可能是一个巨大的优势,主要是因为它们大大缩短了开发时间。我可以看到用功能性语言将服务器后端写入游戏或客户端上的AI和逻辑层的巨大潜力。众所周知,LISP已用于NPC脚本编写。

如果我想用功能语言编写游戏的前端,那么我肯定会选择Objective Caml,它是一种混合语言。它是ML的后代,可以混合使用迭代式和函数式样式,并具有带有状态对象的客观系统。(Caml是F#建模的语言。)

OpenGL绑定似乎完美无缺,人们已经使用OCaml制作游戏很长时间了。该语言可以编译,而且速度可能非常快(很久以前,它在某些基准测试中胜过C语言,现在就不行了)。

也有大量的图书馆。从计算机科学到绑定各种库的一切。



3

至于好处,请查看Clean Game Library(http://cleangl.sourceforge.net/)并检查平台游戏。一旦您对语法有所了解(我鼓励您尝试),您将看到结构的纯粹优雅,以及源代码与它们所表达的概念的映射程度。此外,状态的抽象以及显式传递状态的必要性使您的代码对多核更加友好。

另一方面,它需要重大的范式转换。过去常常没有想到的很多事情已经不复存在了,您会困惑于如何解决它,只是因为您以错误的方式思考。


2

我认为最大的挑战将是:

  • 功能语言的无状态性:游戏中的想法是,每个实体都有一个状态,并且此状态正在逐帧修改。有一些机制可以模仿某些语言中的行为(例如,在plt方案中带有“!”的功能),但是感觉有点不自然。
  • 执行顺序:在游戏中,某些代码部分依赖于其他代码部分才能在执行之前完成。函数语言通常不保证函数参数的执行顺序。有多种方法可以模仿此行为(例如(begin...plt方案中的“ ”)。

随意扩展此列表(并添加一些积极点^^)。


1
在非严格的语言(例如Haskell)中,执行顺序可能会有所不同,但这不会导致依赖于其他代码部分的代码部分产生创建问题。您可以将其视为按需评估。直到需要结果时才执行计算。我认为,可能导致您悲伤的唯一方法是表现。例如,在某些情况下,可能很难获得良好的缓存,因为除了指定计算之间的依赖关系之外,您无法控制评估的顺序。
詹森·达吉特

1

您可以用C / C ++编写代码,并使用嵌入式语言(例如Embedded Common Lisp)进行脚本编写。尽管要使它们在不同的平台上进行编译可能很困难。您可以查看Lisp In Small Pieces,以了解如何编写自己的脚本语言。如果有时间,这实际上并不难。


0

我用LISP编写了简单的游戏,这很有趣,但是我不推荐这样做。函数式编程是关于最终结果的全部。因此,这对于处理数据和制定决策非常方便,但是我发现很难像基本控制循环那样执行简单的干净代码。

我还没有学习F#,所以使用它可能会容易得多。


我最初的想法可能是用命令式语言编写脚手架和状态处理,然后使用功能位进行游戏逻辑等。例如,使用.NET和C#/ F#,这将非常容易,因为它们使用相同的库,并且可以相互通信而不会造成我所知道的实际损失。
McMuttons

-1

正面将是性能可移植性,而负面将是管理复杂性,今天的游戏非常复杂,需要能够更好地管理复杂性的工具或编程语言,例如面向对象的方法论或难以在函数式编程中实现的通用编程语言。


11
你在跟我开玩笑吗?FP是类固醇的通用编程。能够概括数据类型功能使FP拥有强大的管理复杂性的能力。
rtperson

我同意,FP的最大优势之一就是它能够执行通用代码。
McMuttons
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.