实现自己的脚本语言是否可行?


21

我正在用C ++编写节拍游戏,现在是时候为事件,触发器,过场动画等实现脚本了。我已经在互联网上阅读并获得了很多信息。我的首选解决方案是实施自己的脚本语言,例如Cave Story中的脚本语言。我已经看到了这个建议,但是大多数人都建议使用lua,但这似乎与我的编程类型不符。

您是否制作了自己的脚本语言?您为什么选择自己滚动而不使用现有的?您在开发过程中咨询了哪些资源?


43
我个人的经验法则是:如果您需要问别人,滚动自己的_____是否是个好主意,那么这不是一个好主意。
sam hocevar

2
请注意,如果您实现自己的语言,则人们必须学习它,并希望您的解释器中没有错误,而诸如Lua之类的语言则更受欢迎,并且很少有错误。
Nick Caplinger 2013年

2
@NickCaplinger将其击中头部。自己动手就意味着没有文档,没有社区,没有第三方教程,没有StackExchange等。无论您在语法或集成方面取得的任何小进步,都将由于缺乏支持而被抵消,除非您是一个真正的知名开发人员。数百万的粉丝。
肖恩·米德迪奇

4
还请记住工具。这是我关于D,Rust,Dart等的问题。语法本身毫无意义。您需要适当的编辑器,突出显示,“ Intellisense”(代码完成),良好的诊断,重构支持,内联文档支持等,才能真正物有所值。更不用说所有实际的语言功能,集成度,效率等等。
肖恩·米德迪奇

1
如果您是C ++程序员,并且不喜欢LUA,请改用JavaScript。
zeel

Answers:


42

至少,可能不是。

在游戏开发中,这是非常常见的重塑方向盘的情况,这种错误仍然非常普遍。

如果您问这个问题,那么您很可能会受到其他人的影响,因此,请看看Epic Games对虚幻引擎所做的事情:

  • UE3有一个自定义的,怪异的,未经优化的,难以调试的UnrealScript内容,
  • 如果谣言属实,那么它将在UE4删除其支持,而改为支持可热重载的C ++ DLL。

您认为您可以比Epic做得更好吗?

创建编程语言属于编程语言创建者,而不是游戏工程师。

语言要完全成熟需要花很多年的时间,并且其随附的工具集(编译器,链接器,解释器,调试器..)才可用。如今,您手头上许多可用的解决方案,因此,绝对没有真正的理由从头开始做新的事情,至少在目标只是为了制作游戏的情况下如此。期。

回答您的问题,不,由于这些原因,我从未实现过自己的脚本语言。但是我受了一些半熟的苦。因为创建它们时考虑的是非常狭窄的功能,所以它们总是有这种疯狂的小怪癖,使您发疯。通常,您会发现自己花费大量时间只是在尝试解决语言的局限性,而不仅仅是制作游戏。

如果您想创建一种语言的原因是因为它是供不太了解编程的人使用的,或者您认为自己想要它的原因是因为想要一些非常特定于领域的内容,那么让我告诉您这些也是不好的理由。您可以使用,编写一些非常高级的API do_what_they_say_and_say_what_they_do()以及一些非常简单的样板代码,以展示其基本用法。您的技术不是那么熟练的用户将很乐于学习一些编程知识,并且您将很高兴不受限于某些实施不当的语言的束缚。

因此,这听起来可能会有些突然,甚至有些刺耳,所以我会说在一种情况下它可能是有道理的:如果您想学习脚本语言的制作方式。但是,请这样做,如果您这样做,请:不要强迫他人使用它。

编辑

我只是看了一下您链接的Cave Story命令列表。哎哟:

<ECJx:y      [EC?] Jump           @ Jump to event Y if any npc with ID X is present

我不想对Cave Story背后的开发人员表示不敬,但这是一个简单的命令列表的完美示例,该命令列表以无法控制的自定义脚本语言进行了变异。这对于单个开发人员或很小的团队可能仍然可用,但是在此阶段,我建议切换到适当的图灵完备且经过充分尝试的语言(例如Lua),您可以在其中进行以下操作:

if (npc.id == x) then
    jump_to_event(y)
end

例如,当您需要更复杂的条件时,这将使事情变得如此简单:

if (npc.id == x) or (npc.type == "enemy") then
    jump_to_event(y)
end

21
+1“创建编程语言属于编程语言的创建者,而不是游戏工程师。” 此报价再正确不过了。这些人参加过编程语言概念课程,该课程由一位精通编程语言的家伙教过,这些人是不同的人。一堂课让我意识到创建真正的编程语言所需要的CS理论和数学知识。这使我更加欣赏这些人及其技能。他们让我创造游戏,我远离他们,让他们创造语言。
院长奈特

+1,我不知道lua或自定义脚本语言,但是很明显,lua到底发生了什么,这就是可用性的重要所在
RhysW 2013年

1
彼此之间没有什么神奇之处,因此无法学习两者。Unreal可能正在删除UnrealScript,但它们正在极大地增强和填充Kismet,使其成为一种功能强大且完整的可视化脚本语言。因为它是失败的,因为它是由过时的,他们并没有消除虚幻,他们取出显著更困难的特点(热重装C ++,更新Kismit)。不要认为这意味着我不同意你的说法:写你自己的语言是一个巨大的浪费时间,错误的来源,以及大学习曲线的原因,等等
肖恩Middleditch

2
@ ott--我也不害怕,我的观点是扩展这种表示法将使其变得越来越复杂,而从长远来看,首先使用适当的语言将有所帮助。与这种情况下的易用性相比,间接费用无关紧要。
Laurent Couvidou 2013年

1
请注意,UnrealScript已由蓝图代替(但我敢打赌我的同事会回来的)。热加载DLL是一种正交功能。
sam hocevar

9

您是否制作了自己的脚本语言,为什么选择了自己的脚本语言而不使用现有的脚本语言?

我有,尽管我从其他语言借来了语法。总而言之,这是一次很棒的学习经历,在我看来,这并不难,因为该语言很简单。

我这样做主要是因为我想对脚本使用常规的C样式语法,因为团队中的每个人都熟悉它。

我还对学习一些有关实现编程语言的兴趣感兴趣,由于我只需要一个非常简单的功能子集(变量,算术,条件,循环以及调用游戏中函数或协程),因此我决定尝试一下。

您在开发过程中咨询了哪些资源?

我的主要资源是这本书:

在此处输入图片说明

我从这本书中学到了足够的知识来实施:

  • 词法分析器:使用正则表达式几乎是微不足道的,仅使用Regex.Match即可识别和拆分标记。
  • 递归下降解析器:基本上是语法中每个规则的一个功能以及一些帮助和使用令牌的辅助方法。我查看了C#语法规范以获取灵感。最困难的部分是在不进行无限递归的情况下处理算术和关系运算符的优先级。
  • AST解释器:我使用访问者设计模式遍历解析器生成的树,并递归执行每个指令节点。

我会停在那里,因为几乎我需要的所有东西都已经在工作,除了一件事-从递归解释器内部深入调用并产生Unity3D协程。为了解决这个问题,我不得不放弃递归,而再次转向这本书。这次我最终添加:

  • 编译器:另一位访问者,但没有执行代码,而是从AST的每个节点(即简单的基于堆栈的自定义汇编语言)生成一系列小的原子指令。while循环或if条件之类的操作将转换为标签和goto类型的指令。此时,我还将已编译的脚本作为二进制文件写入磁盘。
  • 字节码解释器:仅在平面指令列表上进行迭代。无需递归,现在可以轻松地与Unity3D协程集成。

从零知识开始,整个过程大约花了2周的时间,这非常有趣:)

PS:最后,我还想在我们的工具上为我的自定义语言添加语法突出显示和智能感知。在这方面,Scintilla可以挽救生命。我使用了以下包装器:

http://scintillanet.codeplex.com/


5

我已经看到了这个建议,但是大多数人都建议使用lua,但这似乎与我的编程类型不符。

好的,让我们从另一个角度尝试:您不喜欢Lua的什么?它是容易修复的东西,还是基本的东西?

例如,使用诸如then/do/end表示代码块之类的关键字,而不要使用良好的C / C ++样式花括号。如果那是您的问题...那是您可以解决的问题。您需要做的就是定义自己的Lua小方言,并编写一个简单的转换工具,将您的花括号语法转换为实际的Lua。

您是否想要+ =某种形式?您也可以轻松地在预处理系统中执行此操作。只需将表单的语句expr1 += expr2转换为即可expr1 = expr1 + expr2

当然,您将必须找到一种方法来检测一对花括号代表一个表还是一do/end对。而且你必须通过覆盖到您预先处理系统挂接到Lua中dofileloadstring和其他标准的Lua库函数。但这最终都是可行的。

如果您关心这样的小问题,并且您太习惯于仅采用一种编程样式而不能更改编码方式(请注意:这通常是程序员的一种糟糕素质),那么这是比简单得多的可行选择。写自己的语言。这最多可能需要几个星期。与此相比,该会在一个适当的语言来度过,具有丰富的调试支持等。

如果您的问题大于此(全局变量是默认的,因此需要您在local所有地方使用),则可以解决其中的一些问题(通过使用更改的环境和元表,只需将新的全局变量声明为错误即可)。如果您不喜欢将函数用作一流的对象,协程,垃圾回收或Lua的其他基本元素,那么……那么您将陷入困境;)

如果你真的,真的想成为它的铁杆,你可以写你自己的语言,编译成Lua中。因此,您至少可以使用!Lua语言来充分利用经过充分测试的Lua运行时,卓越的Lua API和其他基本Lua工具。这需要时间,但它仍然不会是几年,你会在别的花。


对我来说,如果我要使用Lua,似乎可以对这些函数进行编码。感觉就像我在移动代码。
skiperic 2013年

1
@skiperic:我不知道你的意思。能给我举个例子吗?
Nicol Bolas 2013年

请参阅上面的Laurent答案。
skiperic 2013年

2
@skiperic:并不能真正解释您所关注的问题。你可以用Lua编码吗?是。问题是...?
Nicol Bolas 2013年

1
@BartekBanachewicz:即使接受它是一个C API,它仍然优于我所见过的许多基于C ++的脚本API。这是我实际上考虑使用raw的唯一脚本API ,而没有某种自动绑定程序。
Nicol Bolas 2013年

3

为了补充其他答案,这不是严格的二进制选项。在现有的脚本语言中,您还可以创建自己的Domain-specific_language。这种方法的优点是:

  • 实际上,您不必麻烦正式的语法,解析器,实现自定义编辑器等。
  • 您将获得实现特定于游戏的专用脚本语言(即额外的抽象)的最大收益。
  • 与自制软件之比:熟悉您所选择的脚本语言的用户将可以立即使用您的DSL。
  • 与现有的普通语言相比:不熟悉脚本语言的用户只需要了解DSL即可修改游戏。

主要缺点是您将在“基本”语言的约束下设计DSL。


2

根据您的游戏机制,这可能很有意义。某些机制很简单的游戏可以使用解释语言来使自己免于过度复杂的Lua / Python编码,但是节省的复杂性可能不值得太多。例如,那些Interactive Novel游戏之一可以轻松使用定制编写的脚本引擎。

如果您的游戏涉及物理引擎,各种游戏组件以及各种复杂的角色和能力,那么您绝对应该考虑使用其他现有的脚本语言,这样您就不会在自己的自定义脚本语言中添加必要的功能,也不会修正错误。它。虽然Lua最有可能是最快的,但是您可能会喜欢许多其他的语法,并且其中许多人吹嘘它们与C集成的难易程度。Python,Ruby和Angelscript的例子很少。(请在评论中提及其他人)

如果您确保将这些语言仅用于“逻辑控制”(即处理某些对象类型的特定碰撞情况,例如喷火器接触冰块),那么性能将几乎不会成为问题。当然,另一方面,如果将它们用于更多的常规代码(制定运行每个帧的自定义碰撞检查算法),则很可能使您陷入困境。


1
Lua还是游戏开发中非常流行的脚本语言。
菲利普

是的,Lua到处都是,但OP指出他不太喜欢。编码样式的首选项可能很重要。
Katana314 2013年

1
例如,那些Interactive Novel游戏之一可以轻松地使用自定义编写的脚本引擎。 ”显然您没有看到Inform。确实,即使是文字冒险,也没有必要编写自己的语言。
Nicol Bolas 2013年

1
@ Katana314:“ 编码风格的首选项很重要。 ”坦白说,如果程序员对“编码风格”大加赞赏,那么世界将会变得更好。如果我们停止认为<在这里插入首选语言>从根本上比其他所有人的<在这里插入首选语言>更好,那么我们都会成为更好的程序员。使用您可能不喜欢build字符语法的语言,并有助于避免这种错误思考。
Nicol Bolas 2013年

1

我说,去吧。在简历上,这将是对能力的额外展示。但是请记住,您首先需要该功能。这将不容易,它将非常困难。有关于该主题的书,也有在线教程,但最终将取决于您以及您对编译器如何工作以及如何解析和翻译代码的理解。

确保您从简单开始,经常测试,并坚持自己的理想。但请始终记住,LUA在那里为您服务。


1
我想这里的问题是目标是制作游戏,还是制作简历上看起来不错的东西。
Tridus
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.