Game脚本语言比较这些东西是平时遇到的, 觉得有一定的价值, 所以记录下来, 以后遇到类似的问题可以查阅, 同时分享出来也能方便需要的人, 转载请注明来自RingOfTheC[ring.of.the.c@gmail.com]
发现一篇很好的比较常见脚本语言的文章, 翻译过来 :)
我很熟悉Lua, 也了解一些GameMonkey, 最近正在翻译GameMonkey的资料, 对于本文中其他的语言, 都只是停留在知道这样的层面, 翻译中可能有一些不当的地方, 先翻出来再说吧
原文地址是:
http://codeplea.com/game-scripting-languagesGame Scripting Languages
概述
许多游戏都使用脚本来制作动画和游戏玩法逻辑. 脚本语言通常都擅长于快速原型, 以及提供更好的代码组织结构. 几乎每个有用的游戏引擎都会使用某种脚本语言.
有很多有用的脚本语言能完成工作. 其中
Lua可能是最为流行的游戏脚本语言. 其他选择有:
AngelScript,
GameMonkey,
Io,
Pawn,
Squirrel以及Scheme. 还有一些重量级选手可以选择, 比如
Python,
Ruby, 当然这些语言往往不易现实嵌入, 其次它们在速度上值得商榷.
在这里我将比较AngelScript, GameMonkey, Pawn, Lua以及Squirrel. 也会简单介绍一下
TinyScheme, 但并不把它和其他语言运行做比较. Io看起来很有趣, 但是它文档太过匮乏, 我也不打算在这里比较它.
语言介绍
Pawn在这群语言中是个古怪的家伙, 我从它开始介绍
Pawn 小, 而且简单, 它使用C风格的语法, 而且只有一种数据结构, 叫做cell. 一个cell通常是一个integer, 但是它经过处理后也可以表示character, boolean, float. Pawn不支持struct 和class, 但是使用数组的"named position"来模拟struct.
在这篇文章中提到的所有语言中, Pawn是唯一一个将编译器和虚拟机完全分离的脚本. 当然, 它在编译期提供更多的检查. 所有的变量必须声明, 所有的本地函数必须前向声明. 这很好, 因为这些检查是在compile-time进行的, 避免了在run-time时对本地函数参数的检查. 我发现编译器也给出了考虑周到的警告信息, 比如它会警告你使用了不正确的缩进, 这样可以帮助你维持一致的编程风格, 防止意外错误. 对于它, 我唯一抱怨的地方就是, 在使用float类型常量的时候, 你必须写成0.5形式, 而不能是.5这个被认为是语法错误.
Pawn有完整的文档和广泛的应用, 以及一个不太活跃的论坛. Pawn是我见过的最快的轻量级脚本语言. 我在游戏中使用Pawn来进行低级脚本编程, 包括角色和对象的动画.
Lua拥有自己独特的语法, 有点像Basic. 它也很快, 编译出的byte-code相当小. Lua是动态语言, 变量在使用之前不需要声明, 而且function作为first-class值(这意味着它的编译器和抽象机制是紧密结合在一起的, function可以保存在一个变量中). Lua中的table被广泛应用, 它是Lua中唯一的复杂数据类型. 一个table中可以部分存储function, 另一部分存储data, 这样就可以用来模仿class, object.
Lua广泛的应用在工业中, 它拥有一个活跃的在线社区和一个巨大的开源组件基地. Lua是本文提及的所有脚本语言中最早产生的, 而且深深地影响着本文中的其他语言, 我发现熟悉Lua的用法之前(这需要大量的联系), lua API document是个难以理解的东西. Lua也有一本书可以用来学习—Programming in Lua, 这本书覆盖了这本语言方方面面的细节.
GameMonkey借鉴了很多Lua中的概念, 不同的是它采用C风格脚本. 它使得table变的非常强大, 可以像Lua那样用来模拟object. 它提供有限状态机机制, 变量使用前不需要声明, function也是作为first-class.
GameMonkey的速度和byte-code的小巧着实让我惊讶(我的意思是它是至精至简的). GM的API参考文档相当的匮乏. 源代码是使用Doxygen结构注释的, 所以可以使用Doxygen来生产帮助文档(我并没有发现生成好的online帮助文档). 它有一个远程脚本调试器. 我只是短暂的玩了一下这个调试器, 但是还是发现它是相当简洁的.
看不到有关于GameMonkey的天花乱坠的宣传, 但是它有一个活跃的论坛. 几个社区成员在背后推进着GameMonkey的发展, 主要是提供各种bindings, 以及更好的调试器等.
Squirrel是一个高级动态类型, 面向对象的语言, 提供了类和继承, 使用C风格. 同样, 它借鉴了Lua中的table. 它很容易编译, 而且看起来有高水平的文档.
尽管Squirrel是一门年轻的语言, 但是它以及被用在了一些商业程序中, 它有一个活跃的论坛.
AngelScript是一门静态类型语言, C++风格. AngelScript拥有最好的原生支持bindings. 通常, 一个函数或者是类只需要注册到AS的虚拟机中, 就可以在AS脚本中使用了. 本文中的其他脚本语言都需要中间插件帮助才能实现函数的binding. 当然, 如果本地函数没有先注册的话, AS脚本也是编不过的. 这给那些想要预编译AS byte-code的人增加了一个额外步骤.
AngelScript不支持table, 事实上这一点不会造成麻烦, 因为AS是静态类型脚本.
AS拥有一个活跃的在线论坛, 漂亮的文档, 而且保持持续的更新.
TinyScheme有被拿来讨论的价值. 它包含在一个C的源文件中. 不像本文中提到的其他脚本语言, 它是解释性的, 所以它的速度很慢, 当然, 如果速度问题对你没有太大影响, 而且你刚好想将Scheme加入到你的项目中去, 我强烈推荐TinyScheme.
我考验过很多其他的Scheme, 但是都碰到了严重的编译问题. TinyScheme很容易编译, 而且它有很多选项可以供你hack.
测试用的版本和授权
语言版本授权
AngelScript2.16.0zlib
GameMonkey1.25MIT
Lua5.1.4MIT
Pawn3.3.4058zlib
Squirrel2.2.2zlib
TinyScheme1.39BSD
绑定
如果嵌入式脚本语言不能调用本地C函数或者是C++方法, 作用就会大打折扣. AngelScript的binding非常简单, 只要你告诉lib你的函数, 就可以马上在脚本中使用了. 其他脚本都需要一些额外的粘合代码.
Pawn把一个int型的数组作为传给函数的参数. float需要简单的转换, 对于string来说你需要调用一些特殊的函数.
GameMonkey, Lua, Squirrel使用一个栈来传递参数. 因为他们都是动态类型语言, 值可以以不同的类型从栈中弹出(比如: int, float, char*). 这个方法有一点不好理解, 但是它可以工作的很好. 每个语言都定义了一些宏, 使得对栈的操作更加轻松容易.
每个库需要第三方库的支持, 以便使得binding更加容易. 比如,
这是一份Lua可用的binding库. 个人认为, 你可以使用本地binding很好的工作, 它们通常是一些宏.
并发
AngelScript, Lua, GameMonkey和Squirrel都支持某种形式的并发. 这允许脚本创建让虚拟机看起来是在并发的thread. 这样可以避免在执行一个很长的算法时扰乱职责. 这些thread不会提供性能上的优势, 因为它们都是用户线程, 但是他们提供了程序设计上的优势, 那就是产生一个单独的执行流去执行一个处理过程.
GameMonkey原生的提供让thread阻塞在某个事件上只到该事件发生. 比如说, 如果一个thread需要等待一个"door open"事件, 它可以在此之前去睡眠, 只到某个线程抛出"door open"事件. 我认为要给其他的脚本加上类似的机制不是一件困难的事情, 但是GameMonkey已经提供了这样的机制.
速度
我对好几种语言进行了速度测试, 这些测试不能够反映出真实应用中的情况, 但是他们给出了一个各种语言之间的比较. Fibonacci数列测试, 脚本函数递归调用1,402,817,464次. Primi测试主要是反复的迭代和基本的integer运算. 原生的string测试是脚本中调用本地C/C++函数10兆次, 传递12个字符的字符串参数. 原生数字测试调用本地函数billion次, 传递一个单独的数字参数. Lua只能支持double, 但是其他语言使用integer.
每个测试, 都先将脚本编译成为byte-code, 因此测试时间包括从磁盘上载入byte-code, 但是它不包含脚本的编译时间. Pawn, Lua, Squirrel使用独立的编译器. AS和GM使用的byte-code但没有独立的编译器, 但是我不得不去汇集程序产生的byte-code.
一个值得提到的信息是Lua, Pawn, Squirrel都有自己的Just-In-Time编译器可以. 我没有测试它们. Pawn附带着一个预编译的实现, 所以我测试了它.
自从脚本语言被经常用于字符串处理以来, 我就在基准测试中向本地函数传递变长字符串. Pawn在它的虚拟机中有一种独特的表现字符串的方式, 转化一个长的字符串的代价很大. 在其他语言中不需要做什么特别的转换, 所以速度和字符串的长度没有什么关系.
Byte-Code大小
在发布游戏的时候, 往往不会直接发布游戏的脚本源码, 而是发布编译后的byte-code. 每个测试的脚本语言都产生了较小的byte-code, 它们很适合用来当着配置脚本. 在很多情况下, 在大小和速度上, 这些脚本语言都要优于传统的配置文件, 比如XML.
库大小
这个可能很少会是一个问题, 其实就是指的虚拟机库大小. 换句话说, 如果你想你的程序运行脚本, 你可以通过库大小预期你的程序的膨胀程度.
语法
我将进行了基准测试的
脚本的源码提供给你, 通过这些代码就可以给你一个对各种脚本语言语法的基本感受.
AngelScript, GameMonkey, Pawn和Squirrel使用C风格, Lua拥有自己的类Basic风格, 这种风格继承了Basic亲近初学者的传统, 使得Lua对非程序员更加容易接近.
Scheme的语法和其他的有一些区别, 但是如果你知道C, 你应该可以在10分钟之内掌握其他任何一门语言语法的90%的内容.
结束语
每个语言都有其长处和短处, 如果你需要一门嵌入式的脚本语言, 我希望这篇文章能给你一定的帮助.
我尽可能的保持公平, 但是我不确定自己有没有在这个问题上犯错误, 欢迎任何的评论/建议/纠正.
标签:
脚本技术