Gamemaker studio2的另一种编译模式——YYC编译

分类栏目:gamemaker教程

130

一、什么是YYC编译?

YYC(YoYo Compiler)编译是GMS2的两种编译模式之一。

GMS2其实提供了两种编译模式,一种叫做VM(虚拟机)模式,另一种是YYC模式。许多人甚至可能都没有注意到YYC模式的存在,这是很合理的——VM模式是GMS的默认编译模式,而YYC模式的启用则要求你在GMS界面的角落里点上它。

此处我们不多谈其编译本质。那不是我们的中心内容。我就简单介绍一些YYC模式的好处:



YYC模式下,程序的执行效率会高得多。

YYC编译时做了数不胜数的优化工作。YOYOGAMES没有公开技术细节,但是执行效率的提升是确凿无疑的——VM模式下的GML执行效率只能说可堪入眼,而使用了YYC模式之后的GML执行效率甚至能与C系的执行效率碰碰——若是各位有兴趣,可以利用GML中的delta_time这一内置变量做点测试,看看执行效率的变化。

YYC编译从根本上就杜绝了反编译的可行性。

请不要相信任何告诉你YYC模式的程序可反编译的言论。它真的不能反编译。

在本质上来讲,VM模式输出了字节码,而YYC则直接输出机器码。计算机语言由底层至高层分为四级,依次是机器语言,汇编语言,高级语言还有脚本语言。YYC直接输出到了机器语言这一层,所以当我们谈及“拆”YYC程序时,我们谈的应该是反汇编。

不过,即使你走到了反汇编这一层,花了大量的时间去学习反汇编技术,你实际上也拆不出任何有意义的内容。现行理论不认为这是可能的。

说到这里,YYC模式似乎百密无一疏,只有好处,没有坏处,这种想法当然也是错误的——如果YYC模式真的只有好处,为什么YOYOGAMES会将VM模式作为默认编译模式?

YOYOGAMES在手册中是这样写的:

YYC: The YYC (YoYo Compiler) takes the normal GameMaker output and compiles it into native code for the target platform, "stripping out" unneeded functions and performing a host of other optimisation techniques to create a smaller and performance enhanced executable package. This can increase your games performance by at least two or three times, especially on logic-heavy games, and is ideal for those larger or CPU intensive projects. Compile times may take longer and you should always clear the compiler cache before building any final complete asset package for a target platform. Note that the YYC target may require extra tools to be installed for the platform selected, otherwise it will not work.

YYC:YYC模式接受常规的GM代码,再将之编译为目标平台的机器码。它“剥离”掉游戏中不需要的函数功能,并执行许许多多的其余优化项目,从而创建一个更小又更高效的程序包。这可以使你的游戏执行效率提升至少两到三倍,对那些逻辑型代码和大量消耗CPU性能的代码尤为有效。编译的时间可能会更长,而且在这么编译以前,你永远应当先清空你的编译器的缓存。请注意,YYC模式可能会需求额外的工具来为你所选的目标平台输出,否则它就不会起效。

(专栏作者译,信息可能失真)

这么说你应该就明白了。YYC编译的编译时长要比起VM编译长很多(这意思就是说,虽然将程序打包给其他人以后,游玩时的执行效率会很高,但是打包这一过程会很漫长),所以YYC并不该作为平日里在开发程序时使用的编译模式,除非你希望每次在编译时都要等上好几分钟乃至几十分钟。

此外,上述我提及了YYC模式输出的是机器码,不存在把它们变成具有良好可读性的代码的可能性,所以在YYC模式下你自己也没法方便地进行bug处理工作。

实际上,YOYOGAMES的设计初衷就是将VM作为开发程序时的调试模式,而在最终发布你的作品的时候使用YYC模式来发行它。

二、我该如何使用YYC模式?


在一个不是非常起眼的角落里。


见图片右上角红框处。请单击那里。
见图中红框处。单击YYC项即可。

勾选完成后,右上角原先为VM的字样会变为YYC。


不过不要高兴地太早。如果你兴冲冲地在勾选完以后立即去编译了个程序想看看效果,那么你大概率只会得到报错警告,连程序界面都不会弹出来。

这段报错信息一般是这样的(实际上会视你的平台目标不同而有所变化,此处以Windows目标平台举例):


为什么会这样报错呢?这和YYC编译的本质有关系——对于Windows目标平台而言,YYC编译的本质是把GML代码转换为C++代码。但是光是转换成C++代码是不够的:C++代码也是高级语言,还是得经过编译。而在此处,GMS只是做了一个把GML代码转化为C++代码的工作,GMS本身并没有内置C++代码的编译器,所以你需要为它提供Visual Studio来作为C++代码的编译器。

安装Visual Studio的过程不是本篇教程的负责范围。实际上,如果你真的没有安装Visual Studio的能力的话,不解决智商问题。

安装完毕以后,请进入GMS2的“偏好设置”选项,为GMS2给出它需要的文件的所在位置。

非Windows平台请类比此方法
在这一切配置完成以后,GMS2就有能力进行YYC编译了。

不过——极为不幸的是,尽管你的程序可以运行,但是往往会在一些奇怪的地方出问题:比如某一处的instance_create_depth()函数生成了一些骇人听闻的,你根本没有填写进去的object的实例。

这就是YYC编译真正的障碍。其实上述的问题都不算什么。

三、YYC编译真正的障碍?


YYC编译真正的障碍在于:它可能会生成一些你根本没有写过的代码。是真正意义上的没写过的代码:你可能要求生成obj_a的实例,但是结果是生成了obj_1的实例。这都是很常见的。

这倒也不能太责怪YOYOGAMES。跨语言转化代码本来就是个不太靠谱的事情,更何况这还是机器批量转化,并没有谁坐在这条流水线上监工。

值得庆幸的是,我们有能力减少这种错误的发生。如果做得足够好,我们甚至有能力把这样的错误减为0.

人人都有代码风格。有的人喜欢用四个空格代替tab,有的人喜欢大驼峰命名,有的人喜欢花括号单列一行。不同的代码风格为我们带来不同的阅读体验,在此处,同样地,YYC编译器也会因为你的代码风格而无法理解它。

YYC编译器需求的代码风格大都是较为良好的代码风格:比如要求你在行末添加分号,要求你使用双等于号作判断而不是用单等于,等等。但是除却这些风格外,YYC编译器还会需求另一些代码风格。

四、YYC编译的“特定代码风格”?


其实这一栏才是重头戏。

免责声明:笔者四处寻找过YOYOGAMES有关YYC编译器的说明,得到的信息却相当之少。也许YOYOGAMES的确发布过完整而具体的代码风格要求,但是笔者并没有找到。接下来所陈述的代码风格要求,只不过是笔者本人和几位共事者的开发经验外加咨询了一些界内人士所得的。

1.在行末添加分号。




2.在进行逻辑判断时,使用双等于而不是单等于。


3.如果你的表达式较复杂,那么就使用括号来说明你的运算顺序。

YYC不一定能够清晰地理解你想要的运算顺序,这就可能导致你的运算得到一个错误的结果。请使用括号来明确地告诉YYC你想要什么顺序。


4.把你的参数“显式地”传入进函数。

此处的“显式”不是指计算机术语上的那个显式,而是指“明确地”,“清晰地”。这也是YYC编译要求的码风中最为奇怪的一点。

我们在编写GML程序时,很常见的一种写法是这样的:

instance_create_depth(random(640),random(480),depth,obj_example);

这段代码会创建一个坐标随机的实例,但是这种语法对于YYC而言可能就有点困惑了。我们在此处将random()函数的返回值作为了instance_create_depth()函数的参数,进行了一次函数嵌套——而YYC可能无法完美理解嵌套。

为了避免这种嵌套,一个自然而然的思路是:

var x_random = random(640);

var y_random = random(480);

instance_create_depth(x_random, y_random, depth, obj_example);

很有趣。

5.即使你的表达式并不是很复杂,也建议把它们加上括号。

我们经常写出这样的代码:

a = b + c;

在99.999%的情况下YYC也确实能够正常理解它。只是为了这最后的0.001%的可能性,你还是得多花一步,写作:

a = (b + c);

6.使用其他的官方手册中所倡议的语法。

官方手册中其实有很多不为人注意的细节。很多人清楚"?"是ds_map的访问器,也知道"|"是ds_list的访问器,但是很多人都不清楚数组也有其访问器"@",用于避免数组拷贝。

这种细节实在是太多太难以罗列了,请原谅笔者在此无法详细举出。


五、后记


事实上,即使你完全遵照上述要求,错误依然是可能出现的——YYC编译目前仍然处于非成熟体的阶段。遇见这种情况,如果你认为你的代码都已经遵循了上述要求的却依然报错的话,尝试清空编译器缓存,或者重启GMS2,或者重启电脑。有时这就会让一切解决。也有的时候你可能只是在某个脚本中写入了gml_pragma("forceinline")让它转为内联编译它就解决了,这都是说不准的事情。

你可以考虑用邮件联系YOYOGAMES的官方。他们其实是会处理客户问题的。

即使YYC编译的毛病颇多,YYC编译为我们带来的巨大执行效率飞跃以及100%不可被反编译性依旧值得我们尝试。