Project1

标题: 【新手向】RMVA - RGSS3脚本错误讯息指南 [打印本页]

作者: RaidenInfinity    时间: 2017-3-20 13:36
标题: 【新手向】RMVA - RGSS3脚本错误讯息指南
本帖最后由 RaidenInfinity 于 2017-3-20 13:52 编辑

RGSS3脚本错误讯息指南




本帖记载常见的RGSS3脚本错误,与一般排除的方法。

在使用本帖之前,建议先阅读本区置顶的RGSS3脚本教程,以便理解一些术语和用词。


前言


各位看官们啊,你们可曾因为遇到脚本报错而不知所措?



因为看不懂英文而不知道出的错误是什么,结果折腾了半天,原来只是忘记加个end,变量名字拼错了,或者少了什么重要的步骤没写…



这个教程的意义,就是以简单易懂的方式,和丰富的例子,让新手可以认识RPGMaker VX Ace 脚本的弹窗错误讯息,并可以排除一些简单的错误。

不过,请各位看官注意。这是个教程,不是帮助文档。本教程只概括RGSS3常出现的错误讯息,而且还是新手常遇到的。因此,在作出评论之前,请先了解这一点。

在观看这个教程之前,请确保你有足够的RGSS3基础知识,如知道什么是class,如何赋值等。如果没有基础但硬要继续的话,请先阅读置顶的RGSS脚本教程RPGMaker VX Ace F1帮助文档以获得所需要的知识。


正文


A. 讯息解读

首先,在你遇到错误讯息弹窗的时候,请留意窗内的三个地方。这分别是出错脚本与行数,错误类型,和详细内容。


出错脚本与行数告诉你的是哪儿出错了,让你去排除。RMVA的编辑器在测试模式时脚本出错后,打开脚本编辑窗,系统会自动跳转到出错的地方,让你修改。

举个栗子,上面的讯息是指,在“test”脚本的第6行出错了。打开脚本窗,来看到底是发生了什么事情!



噢,报错的是这里啊…报了什么错呢?

错误类型:SyntaxError
详细内容:unexpected $end, expecting keyword_end end

不会英文的新手们此时恐怕是懵逼的。26个字母我懂,可是怎么拼成字词就看不懂了呢?!

好,这就是本教程即将介绍的第一种错误类型,也就是SyntaxError。这个,本教程稍后会进入详细解释的回合。现在我们暂时先只需要知道…

Syntax Error 是句法错误,也就有些重要的句法写错了,系统无法运行该脚本。
unexpected $end, expecting keyword_end end 是说,少一个end。

那么重点来了。少了一个end!没错。def 要配一个end。if 也要配一个end。
上面范例中的脚本,的确只有一个end。

那我们知道出了什么错之后,就可以进行排除了!



好,现在运行这个脚本,就没事了…不。还是有事。什么事呢?我们来看下一个章节。



B. 错误讯息

1. SyntaxError 句法错误

就如上个章节所提到的,句法错误的意思就是脚本的基本句法出了问题。少了或者多了end,给数组赋值时少了逗号,把elsif拼成else if之类的…



一般SyntaxError的内容会有两个部分:unexpected(不该出现的)和expecting(该出现的)。前面的并不太重要,我们主要看后面的这一部分。

expecting ']',就是这里应该要有一个]符号。为什么呢?不是少了逗号吗?



的确,有时候,SyntaxError并不会准确地报告错误的详细内容。下图解释了这个情况。



在知道出什么错之后,施与修复:



总的来说,接到这种报错最重要的事情,检查整个脚本看有没有句法上的错误(多了不该有的,或者少了该有的)。这个错误是在游戏一开始运行就会出现的,因此不会太难发现。



2. ArgumentError 参数错误

调用方法时(包括创建对象)输入的参数数量与所需要的不相符就会出现这种错误。

假设我们有如此的代码:


我们定义了一个类,并用它来创建了一个对象。
这个类的initialize方法需要2个参数,因为在创建(new)的时候,需要输入两个参数。
如果参数数量错误,就会出现如此的错误:



详细内容所显示的 1 for 2,意思是,必须输入的参数有两个,但是你只给了一个。如果你给了三个,就会显示 3 for 2,就是参数太多了。

方法调用的范例:




在这个范例内,add这个方法接受两个参数a和b。调用的时候却输入了3个参数。因此就出错了。错误讯息表示,这个方法需要正好2个参数,但是你却给了3个。

这种错误排除的方法非常简单。只要检查方法的定义和调用的参数数量是否匹配就行了。

注意:定义默认参数的方法(比如 def add(a, b, c = 0)) 出错的时候,如果输入的参数过少,错误讯息会告知你最少需要多少个参数(比如1 for 2)而如果输入的参数过多,错误讯息将告知最多可以输入几个参数(比如4 for 3)。



3. NameError 名称错误

名称错误一般是找不到局部变量,方法或者常量的时候出现的。出现这个错误,八成就是代表…你眼睛出了点问题,有变量或方法名拼错了。

另外二成是啥呢?就是使用局部变量之前没有赋值。局部变量必须先赋值才能使用,否则会出现NameError。
RUBY 代码复制
  1. p a

上面这行脚本直接运行铁定出错。NameError,名称错误。
讯息:undefined local variable or method `a' for main:Object
释义:main(主运行范围对象)是个Object类的对象,它没有定义名为a的局部变量或者方法。
如果是@a的话,可能会出现nil。但是你这是局部变量,不是实例/类/全局变量。没先定义(赋值)是不能用的。

解决方法呢?当然是先赋值啊。就算你想要给它nil也好,也赋值啊。
RUBY 代码复制
  1. a = nil
  2. p a


NameError绝大部分出现的时机是局部变量,常量,或者方法名字拼错。下面这段代码给了几个范例。
RUBY 代码复制
  1. #范例1:局部变量名字拼错
  2.  
  3. actor = "Eric"
  4. p actr #出错!actor错拼成actr!
  5.  
  6. #范例2:方法名字拼错
  7.  
  8. def abcde
  9.   p "666"
  10. end
  11.  
  12. abdef #出错!abcde错拼成abdef!
  13.  
  14. #范例3:常量名字拼错(总之大字母开头的变量就对了)
  15.  
  16. class Window_Menu
  17. end
  18.  
  19. MENU_WIDTH = 200
  20.  
  21. WindowMenu.new #出错!Window_Menu错拼成WindowMenu!
  22.  
  23. p MENU_WITH #出错!!MENU_WIDTH错拼成MENU_WITH!


NameError和下面会说到的NoMethodError可以说是粗心大意所造成的错误。虽然看起来差不多,但是有一定的分别。
让我们继续看下去…



4. NoMethodError 未定义方法错误

字面上的意思,未定义方法错误就是在系统找不到你要调用的方法的时候出现的。
比如我们有这样的代码:
RUBY 代码复制
  1. a = "abc"
  2. a.abs


出错!脚本第2行发生了NoMethodError。报错讯息:undefined method `abs' for "abc":String

这个报错讯息说的是,"abc"是一个String(字符串)类的对象,而它没有abs这个方法。
注意:abs是整数/浮点数的方法,用途是取绝对值。所以很明显,字符串并没有这个方法。

与NameError(名称错误)的分别就是,NoMethodError(未定义方法错误)是用句号(.)来调用一个对象的方法时发生的。
你可以这么理解:没有句号的时候或者句号左边出问题,出现的是NameError。句号的右边出问题,出现的是NoMethodError。

为什么会找不到方法呢?这有两种主要原因:
一,你拼错了方法名,或者想使用的方法实际上不存在这个类里面(没有定义或者用undef注销了)。
二,句号左边的变量里面存着的对象不是预期的类型

第一种情况,拼错了方法名。好,我觉得需要带好眼镜点好眼药水再继续。
RUBY 代码复制
  1. arr = [1,2,3,4,5]
  2. arr.revrse
  3. arr.shufle
  4. arr.empty

上面的这段代码,第二,三,四行都是会报错的。为啥呢?
reverse(数组反转)错拼成了revrse。
shuffle(数组洗牌)错拼成了shufle。
empty?(数组是否为空)后面少了个问号,写成了empty。

这一切不就都是眼睛的问题吗?所以啊,出现NoMethodError,绝大部分情况就是有什么拼错了。
当然,不止方法名拼错。就连变量名,尤其是实例变量(@前缀),类变量(@@前缀)和全局变量($前缀)拼错也会出问题!

第二种情况,变量的内容不是预期的类型。你以为这变量里面存着字符串,其实它里面存着整数,这样的情况。
不过,绝大部分因为变量内容异常而导致NoMethodError的情况,就是遇上了nil(空值)!万恶的nil!

如果你看到这儿并不知道什么是nil,那么你得去看置顶的RGSS3脚本教程看到知道为止才能继续了。
上面提到的三种变量,如果并没有存着一个有效的数值,那么引用它们时会返回nil。
然后呢?当然是爆炸了啊。

RUBY 代码复制
  1. @index = 5
  2. @inddex += 4

上面这段脚本会在第2行出现NoMethodError错误。
错误讯息:undefined method '+' for nil:NilClass
错误讯息解读:变量里存的是nil(空值),并没有 + 方法。

于是你看到了错误,哎哟怎么变量里存的是nil呢?不是5吗?
妈啦!原来是拼错名字了啦!@index怎么拼成了@inddex?!
这就难怪读出来的值是nil了啦。

除了拼错实例/类/全局变量的名字导致读出nil而造成NoMethodError之外,数组和哈希表等容器的引用失败也会读出nil来。
RUBY 代码复制
  1. arr = [1.6,2.3,3.9,4.1,5.5]
  2. arr1 = arr.select{|i| i > 7.0}
  3. p arr1[0].round

这段范例的第3行会出现NoMethodError错误。
错误讯息:undefined method 'round' for nil:NilClass
错误讯息解读:变量里存的是nil(空值),并没有 round 方法。

啥?我没拼错变量名字啊。为什么会出错了呢?
那么你想想,arr1这个变量里在第三行时存着什么。
我们在第二行和第三行之间加一个p arr就会发现…它是个空的数组
既然是空的数组,arr1[0]肯定就会读出nil来了。然后尝试调用它的round方法,就出错了啊。

解决这种问题的方法,就是要加nil检测。实际上,在写脚本的时候,尤其是数组的操作上,你应该要到处都考虑会不会出现nil。
RUBY 代码复制
  1. arr = [1.6,2.3,3.9,4.1,5.5]
  2. arr1 = arr.select{|i| i > 7.0}
  3. p arr1[0].round if !arr1.empty? #加一个检测防止出事


当然,修改的策略还是得看实际应用和情况的。不管怎样,检查再检查是写脚本非常重要的一环。
尽量减少拼写的错误,你可以省去很多浪费在排除报错上的宝贵时间。



5. SystemStackError 系统堆栈错误

系统堆栈深度过深时,就会出现这种错误。

啥?什么是系统堆栈?百度搜索是你的好朋友哟!如果还是不了解的话,就这样比喻吧。如果在方法内调用别的方法,可以视为是“潜下一层”。被调用的方法结束后,就“浮上一层”。如果因为某些缘故而导致方法调用而不返回,没完没了,“潜”得太深了就被水压压死了,就出错了……好吧,这大概太抽象了。就直接到范例吧!

假设我有这样子的一段代码:


我们可以明显看到,方法a呼叫方法b,而方法b又呼叫方法a。这就是个无限循环啊!所幸的是,ruby的系统可以侦测出这种情况,而截停程序显示错误。



卧艹!怎么没有行数?!怎么活?

没错。因为这段代码理论上是可以运行的,因为没有句法错误,也没有调用和操作上的错误。但是,它却造成了方法调用无限循环的情况,被系统截停。因此,排除类似错误的方法,就是检查有没有这种情况,包括方法呼叫自身且无法脱出循环的情况。

要说堆栈错误最常发生的地方,就要说说alias这种玩意儿。alias(别名),是插件脚本的基础。RGSS3优越的默认脚本构造允许了alias的大幅度使用。对于这个,可以参考VA帮助文档,或者脚本作者写的插件脚本。

范例:


假设我们有这样的一段脚本。运行后的输出是"z"和"x"。这是因为,使用alias赐予方法aaa一个别名bbb,在定义一个新的aaa方法覆盖(隐藏)原方法的时候,可以透过这个别名来呼叫原方法。

那么假设我在一个插件脚本里面写了如此的代码:
RUBY 代码复制
  1. class Game_Battler < Game_Battler_Base
  2.         alias :new_initialize :initialize
  3.         def initialize
  4.                 new_initialize
  5.                 #增添的功能
  6.         end
  7. end

但是我也在另一个插件脚本里面给Game_Battler也安上了alias :new_initialize :initialize,并调用了initialize。

这会导致什么事呢? 当然是出错了!系统堆栈错误!

同样的别名被安到了同样的原方法上超过一次(不同的原方法只会造成覆盖),系统就凌乱了。到底要用哪一个?都用吧?于是无限循环就出现了。

因此,如果在安装了插件脚本后出现这个错误,请检查插件脚本本身是否重复。如果有,请删掉其中一份。如果是插件脚本不相容而导致这个错误的出现,那么请更改别名.

上面的范例,可以如此更改:
RUBY 代码复制
  1. class Game_Battler < Game_Battler_Base
  2.         alias :new_initialize_a1234 :initialize
  3.         def initialize
  4.                 new_initialize_a1234
  5.                 #增添的功能
  6.         end
  7. end


相信6R里面制作插件脚本的大触们很多都会考虑到这种状况而在使用别名时给名字增添一些乱码成分。道理就是这个。当然,不一定真的要用乱码。只要你喜欢而且确定不会“撞名”就都可行。



6. TypeError 类型错误

在执行计算,变量转换等操作时,因为数据类型不匹配而操作失败则出现此类错误。

范例:


a是个数组,b是个字符串。如果使用b作为a的索引,会发生什么事情?



当然是爆炸啊!数组的索引哪能放字符串啊?

解决方法呢?当然是确保索引是整数!除了索引之外,一些要求整数作为参数的方法,比如Array(数组)的delete_at(在输入的索引处删除),也会在输入非数字的参数时出错。

再举个例子吧!




字符串加整数,类型不匹配所以出错了。解决方法如下:



这段代码的输出是"string5",先吧整数使用to_s方法转换为字符串,方可与别的字符串作加法。假如a是可以被转换为数字的字符串(比如"6"),那样的话可以考虑使用to_i方法将a转换为整数,方可执行计算操作。

总之呢,出现TypeError 类型错误的时候,请检查出错的行数,变量存着的数据的类型,是否与当前的操作所需的类型匹配。在Ruby里字符串是不能和整数直接相加的,要切记啊!



7. FloatDomainError 浮点错误

在试图将正负无穷或非数字(Infinity, -Infinity, NaN)这三种特殊浮点数数据转换为整数时所出现的错误。

范例:


假设我们有如此的代码。上面这行是将浮点数(可容纳小数的数据类型)1.5除于0的操作,得到的结果是Infinity(无穷数)。这是因为浮点数的数据结构和整数不同,因此可以容纳无穷数,而不会产生错误。负浮点数除于零会产生负无穷(-Infinity)而0.0除于0会产生一个特殊的数:非数字(NaN,Not a Number的缩写)。

简单地说,如果使用浮点数除于0的操作,得到的结果是异常的。尝试将这个异常数转换为整数(下面那行的a.to_i)就会出现…



出现这种错误时,请检查是否有任何浮点数除于零的状况,或者在转换操作之前检查是否异常,并使用if条件分歧来排除。

例子:


如果b.infinite?不等于nil或者b.nan?为真(这两个都是Float类的方法,分别检查该浮点数是否无穷数或非数字),那么就设置b为0。

会出现这种状况的例子,是在绘制计量条的时候。比如最大MP为0的情况下绘制MP条,肯定会出事了。非不得已的情况下还是尽量避免有任何除于零的情况。ZeroDivisionError 除数为零错误的说明也有一个方案,可以作为参考。

注意:如果执行计算操作时两个值都是整数,除于零会产生ZeroDivisionError除数为零错误。请参考此错误的说明。




8. ZeroDivisionError 除数为零错误

在执行整数除法的时候,如果除数为0,那么就会出现这种错误。

范例:



很明显,第12行这里,5除于0,两个都是整数而答案也会是整数,出这个错误是必然的。

排除方法?这要看你的脚本而定了。尽量避免除于零的状况吧。如果实在没办法的话,那么就使用if条件分歧,处理这个状况。当然要怎么处理,是你自己的决定。



注意:如果执行计算操作时其中一个值是浮点数(例如: 1.5 / 0),那么得到的结果也会是浮点数类型。这种情况下,除于零会间接造成FloatDomainError(浮点域错误),请参考此错误的说明。



9. RegexpError 正则表达式错误

使用句法不正确的正则表达式时,因编译失败而产生的错误。

啥?你问什么是正则表达式?那么你大概是不会遇到这种错误了(笑)。好,给个正则表达式的例子吧!Window_Base的第297行处。



这一串在两个/之间的,标记为紫色的字符串,就是正则表达式。想要知道更多关于正则表达式的知识,可以去百度搜索或者查看F1帮助文档的附录。

那么,如果出错了呢?

范例:

这段代码 将一个字符串内,大字母开头的字给提取出来。运行这段代码的话,可以看到输出是"World"。那么如果我们捣个蛋,移除一个重要的句法元件,会怎样呢?


当然是爆炸了啊!


括号不配对!

不过要注意的是,这种情况是只有使用Regexp.new才会出现的。如果是直接为变量或常量赋值,会先触发SyntaxError句法错误。因此这个错误可以说是极为罕见,是拿来凑字数的(你滚)。

附录


F1帮助文档 - 内建异常类

原链接:http://miaowm5.github.io/RMVA-F1 ... s/s_exceptions.html
展开折叠




作者: shitake    时间: 2017-3-21 15:37
  1. RaidenInfinityError = Class.new(Exception)
  2. RaidenInfinityError.new("RaidenInfinity 是个大 Hentai!")
复制代码


undefined method xxx for nil:NilClass 这种错误在RM的实际开发中往往是由于对边界情况检查做得不够好而发生的。
比如从空数组里取元素然后对其操作等。

在实际的脚本编写中,大家最常犯的错误就是,没有考虑边界(特殊)情况,虽然代码初看是能正常运行,但往往跑着跑着就出现问题。

作者: fjm    时间: 2017-4-1 22:27
shitake 发表于 2017-3-21 15:37
undefined method xxx for nil:NilClass 这种错误在RM的实际开发中往往是由于对边界情况检查做得不够好而 ...

感谢,很多错误看不懂,正好对照可以参照下




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1