关于GMS2 YYC编译的研究
gamemaker吧
全部回复
仅看楼主
level 12
_GASTER☜_ 楼主
GMS2在输出游戏时提供两种编译模式,一种是VM,也即默认编译模式,一种是YYC。
VM就是字节码,这样的输出虽然快,但是有被破解的风险。
YYC则是将GML转化为cpp进行输出,尽管编译时间相比VM来讲长的多,但是造出来的程序是几乎不可能被破解的,此外,YYC游戏的处理效率远高于VM编译。
因此,有时候使用YYC编译就显得很必要了。
这里给不知道的同学们提一下,切换地点就在GMS2的右上角,那个像是个瞄准器的图标,点下去,在Output窗口中切换为YYC即可。
但是,YYC有很多毛病——这也是我今天要讲的问题。
有时候,你在VM中编译起来毫无问题的游戏,转入YYC后,你会发现bug奇多——而且这些bug不是你能想象的。例如,instance_create_depth与instance_create_layer,往常,哪怕你的代码再混乱,最起码创建的实例也是正确的。
但是,在YYC中,可能它创建的实例根本就不是你想要的。你填的是obj_123,它有可能给你创建obj_456。实际上,这还不是最糟的,它最后的运行结果可能会比这还要可怕的多......包括但不限于出现死循环,出现你根本没调用的函数——从某种意义上,它的创造力比Shader的创造力还强。
这就是我们今天探讨的问题:到底该怎么减少这种错误编译?
仅据个人研究,如果你的码风(代码风格)符合“某种规范”,可以大大减少这种错误率。
首先,你必须尽一切可能地减少那些不符合大众所认可的码风,包括但不限于:语句中不能省略括号,逻辑判断必须使用双等于,每一行结束后添加分号以示结束......
但是这并不是我们所需要的全部。即使你完全遵循这些公认码风,YYC的编译依然会出错。
以下是某个示例:
这是可以在YYC中运行的代码(注意图中仍然有些问题,如等于号前后的空格问题)。
这些代码有个很奇怪的现象,第六行,第七行的临时变量似乎是完全不必要的,你完全可以在下文中的instance_create_depth中将它们替换掉,不是吗?
按照常规理解确实如此,但是,事实证明,如果我不这么干,在执行这里时,创建的实例是一个叫做“obj_system_heart”的实例。
事实上,各种平日里可以在函数里直接写入的表达式,似乎都必须使用一个变量储存表达式的值,然后在函数内写入变量......反正很莫名其妙。
现在,我想请教的问题是,还有什么码风是能降低YYC编译错误概率的?又或者不是码风,总之,什么能规避YYC的出错?
(是的,这其实不是个科普贴,是个请教问题的贴子)
2020年02月29日 12点02分 1
level 12
_GASTER☜_ 楼主
现在有了点新的研究。
事实证明,YYC要求极其严苛的语法,几乎不能有一丝问题,平日里省略的括号必须被加上。
一个很好的示例,是这样的:
a = b == c || d;
这个式子看着乱七八糟,现在,请先试着在心里把它用括号标出来求值顺序。
......
好了。通常情况下,我们想要的顺序,是这样的:
a = ((b==c)||d);
这也是Windows上的求值顺序。但是,对于YYC来讲,你的括号必须被加上,如果不加上,YYC就有可能错误地理解你的求值顺序,比如会变成这样:
a = (b==(c||d));
这样一来,程序可能就会从此走向一系列你不想要的产物......最后彻底坏掉。
2020年02月29日 15点02分 2
level 12
_GASTER☜_ 楼主
还有一项新的发现是关于一楼说的“某种码风”的具体定义。
就像是GMS2的帮助手册中所写,你需要显示地调用。红激汉化的手册中原文如下,我在此搬来:
---------------------------------------------------
当使用GML编写游戏时,您应该知道函数调用参数不能保证评估顺序。这意味着,您在代码中放置函数的顺序将从平台变为平台,因此您应该以显式的方式对它们进行编码。这是由于不同的目标平台之间的优化差异,例如在Windows的目标函数上可以从右到左计算,但是在HTML5的目标上,它们可以从左到右进行评估。为了避免任何问题,你最好不要在函数调用的参数中调用多个函数,因为你很可能依赖于评估的顺序。
要查看这意味着什么,请考虑下面的代码,它调用了几个函数,并将它们用作脚本的参数:
buffer_seek(buff, buffer_seek_start, 0);
scr_buffer_get_info(buffer_read(buff, buffer_s8), buffer_read(buff, buffer_s16),buffer_read(buff, buffer_s16));
现在,这里的问题是,在某些平台上,最后的buffer_read将被调用,因此脚本的所有参数都将是错误的,因为数据正在从缓冲区中以“反向”顺序读取,正如您所看到的那样。这将会影响到buffer_read函数的所有其他值,因此传递给这个脚本的所有参数都是错误的!
要解决这个问题,您应该显式地调用所需订单中的函数,并将返回的值存储在变量中,如下所示:
var val[0] = buffer_read(buff, buffer_s8);
var val[1] = buffer_read(buff, buffer_s16);
var val[2] = buffer_read(buff, buffer_s16);
scr_buffer_get_info(val[0], val[1], val[2]);
虽然它可能看起来比较冗长,但它可以保持一切清晰,并避免任何可能出现的问题。
---------------------------------------------------
是的,YYC同样要求这样的语法。如果你不这么做,就可能会让函数瞎几把调用参数。
现在我的怀疑是,我一楼神秘创建的obj_system_heart就是argument[0]+obj_character.x_extra对应的值所对应的id。因为,我的obj_system_heart所对应的数字还真的就和那个值差不多。
2020年02月29日 16点02分 3
level 15
说几点我的经验:
1. 操作符+-*/&&||等,在F1手册里明确写了要求加好括号,否则即使是vm编译,在不同平台也可能导致不同求值顺序
2. gml对函数参数进行求值的时候,似乎确实是从后往前,特别是buffer这很明显,之前我在gms1即使vm编译也是如此
3. yyc确实更严格。前几年在gms1用yyc的时候,执行报错,后来检查发现确实是我代码有问题,甚至不知道为什么vm能够正常运行,具体什么情况忘了。。而且记得当时yyc如果编译出错,就清理一下工程,然后再重新编译,就可能会恢复正常。
4. gms2之后没再用过yyc,有什么变动就不清楚了
2020年03月01日 10点03分 4
level 4
0v0要我装个啥VS 我没装
2020年03月01日 17点03分 5
吧务
level 13
还没用过YYC,理解一下就是,运算符要加上括号,函数不要使用嵌套,这样??
加括号咱倒是习惯了,不要嵌套这个……emmmmmmmm
2020年03月02日 01点03分 6
level 2
我觉得这该是厂商需要优化的问题,可以多发发邮件,我原来有问题发邮件,他们都回了,不过效率很低,到现在还没改,不过他们还是有列计划表的
2020年03月02日 06点03分 7
level 1
函数调用的时候参数顺序都可能乱,这明显不是什么特性而是实实在在的bug
2020年03月23日 12点03分 8
1