玩转RMMZ的字符串变量
我写了这三篇 js 入门类的文章,这次也算是入门的延续,来写写关于“值”的内容。
不过,感觉其中一半左右已经算不上入门了。
主要设想用在“变量操作-脚本”中,但也会时不时提到在其他命令中的用法。
不说明在插件中的用法。
在官方插件入门中,大概就是最速js讲座:变量这部分吧。
我想可能也有人想要正经学习 js 本身了,所以列举一些入门网站。
请随意挑选你喜欢的。
- dotinstall
- progate
- 哈佛大学 JavaScript Lecture 5 CS50's Web Programming with Python and JavaScript 2020(日语字幕)
术语
这次是把之前粗略写过的术语像下面这样详细解说。
数组…给值编号并排列而成的值。示例: [ 10, 4, 6 ] (这里并列的是数字作为值,但也可以并列其他类型的值)
索引…读取数组(或保存数组的变量)中值时使用的数字。示例: array[ 0 ] (js 数组的索引从 0 开始)
字符串…将字符排列而成的值。在 js 中称为 String。也可以看作每个元素固定为单个字符的数组。示例: "这是字符串"
布尔值…取真(正・ON)和假(负・OFF)两种状态的值。在 js 中称为 Boolean,两个值分别用 true 和 false 表示。
逻辑运算…计算结果返回布尔值的运算。包括判断数值大小等的条件表达式。
变量…用于保存值。js 的变量与 rmmz 的变量大致相同。定义示例: let variable; 以前定义变量时不使用 let 而使用 var,但 var 存在很多问题,现在(2022年时)已不太常用。反过来,如果看到代码中用了 var,即使是初学者也能轻易看出这是旧代码。另外,从 rmmz 开始,核心脚本已经废弃了 var。
常量…与变量大致相同,但一旦设定值就不能再更改。乍一看似乎不便,但实际上比变量更方便,在实际编程中几乎不需要使用变量。定义示例: const constant = 1;
虽然有些啰嗦,但以下内容中,rmmz 的变量会用方括号括起来书写,而 js 的变量则直接书写,以此区分。
文字列
字符串…将字符排列而成的值。在 js 中称为 String。也可以看作每个元素固定为单个字符的数组。示例: String "文字列でーす"
虽然之前解释得比较粗略,但这里我想再稍微详细地说明一下。
最简单且有用的脚本,大概就是通过“变量的操作”中的“脚本”将字符串放入变量中吧。
像下面这样用双引号 " 括起来,就能轻松地将字符串赋值给变量。

"用双引号将字符串括起来就行了喵";
然后,在“显示文章”中写成 \v[1] 这样的形式,就可以直接显示该字符串。
使用这个方法,可以制作出非常细致的台词变化。
基本上这一页想传达的内容已经差不多都传达了(笑)
字符串的条件判定
在条件判断中,虽然可以将变量作为条件,但并未提供与字符串进行比较的功能。
你可能会想,这样是不是没什么用?但其实有多种方法可以解决。
首先,因为存在变量与变量之间的比较,所以可以先把字符串赋值给一个临时变量,再进行比较。
在“执行内容”中就像下面这样:
◆变量的操作:#0003 = "判定字符串"
◆变量的操作:#0004 = "条件字符串"
◆条件分支:#0003 = #0004
◆显示文章:无, 无, 窗口, 下
: :\v[3] 和 \v[4] 相同!
◆
:除此以外的情况
◆显示文章:无, 无, 窗口, 下
: :\v[3] 和 \v[4] 不同!
◆
:分支结束
另一种方法是在“条件分支”中使用“脚本”进行判断。
$gameVariables.value( 3 ) === "条件字符串";
在“执行内容”中就像下面这样:
◆变量的操作:#0003 = "判定字符串"
◆条件分支:脚本:$gameVariables.value( 3 ) === "条件字符串";
◆显示文章:无, 无, 窗口, 下
: :\v[3] 和 条件字符串 相同!
◆
:除此以外的情况
◆显示文章:无, 无, 窗口, 下
: :\v[3] 和 条件字符串 不同!
◆
:分支结束
用 js 进行判断时,“不相等”的情况也很容易判断。
$gameVariables.value( 3 ) !== "条件字符串";
Details
严格相等运算符
=== 这个符号,为什么要连写三个等号,挺让人费解的吧。其实用两个等号 == 也能进行判断。
但是,下面这个判断中左右两边分别是数值和字符串,结果却变成了真 true。
2 == "2"
我认为这是因为 js 的基本方针是“随便写也能随便判断并执行(大意)”,所以才设计成这样的规格。
结果就变成了:必须知道它在什么情况下会有什么行为,否则就无法使用,这完全与对新手友好背道而驰。
哦?感觉好像在 rmmz 里也体验过类似的事情呢…。
话说回来,使用 === 时,下面的结果是 false。
2 === "2"
如果试图区分使用反而容易出错,所以最好一开始就决定:比较时一律写成 ===。
如果想将字符串作为数值来比较,应该显式地(以脚本可见的形式)把字符串转换成数字,比如像下面这样写:
2 === Number( "2" );
可能也有人疑问:判断左右相等的等价运算符,从一开始就用 === 而不是 = 或 == 不就好了吗?
毕竟使用和数学不同的符号,确实难以理解。
但是因为已经把赋值运算符定为 =,所以就不能再把 = 用作等价运算符了,真是够蠢的。
顺便一提,也有像 ALGOL 那样用 := 赋值的编程语言,或者像 AppleScript 那样用 set a to "B" 这种表达式进行赋值的语言。
但是由于主流编程语言的赋值运算符都是 =,所以似乎已经无法翻盘了。
实际上,初期使用 = 的 B 语言也变成了 =。大家已经太习惯 = 了,现在改变它反而会产生 bug。
大家也放弃挣扎,接受 = 作为赋值运算符吧(唉唉)。
虽然有点多余,但以“子(し)”结尾的词语在计算机术语中经常出现,它的意思大致是“执行某事的東西”。
你见过“編集子”这个词吗?它和“編集者”意思基本相同,但匿名性稍高一些。
也就是说,“子”既不是指孩子也不是指老鼠,而是相当于英语中的“-er”。
也就是说,Dragon Slayer 翻译过来就是“竜討伐子”,而 Demon Slayer 翻译过来就成了“竈門禰豆子”(笑)。
字符串的连接
连接字符串时使用 + 符号。
在“脚本”中书写时就像下面这样:
const result = "前面的字符串" + "后面的字符串";
不过,在“脚本”中操作变量很麻烦对吧。
其实在“变量的操作”中,选择“操作”-“加法”,然后在“操作数”-“脚本”中写入字符串,就可以执行向变量追加字符串并将结果存回变量的操作。
另外,如果在“操作数”-“变量”中,目标变量里已经存有字符串,也可以同样进行追加并存回变量的操作。
隐式类型转换
当一侧是字符串,另一侧是数值时,js 会将数值作为字符串进行拼接。
"123" + 4; // 结果是字符串 "1234"
也就是说,它将数值隐式地(以在脚本中不可见的形式)转换成了字符串。
乘法的情况会怎样呢?看起来好像会报错。
"123" * 4; // 结果是数值 492
没想到……这次是把字符串隐式地转换成了数值。
这乍一看似乎很方便,但结果你还是需要记住它在所有模式下的行为,一点都不方便!
一般人会认为:既然乘法里被当作数值处理,那么加法里也应该被当作数值处理才对.
rmmz 也是乍一看很方便,但如果不掌握它的行为就没法用,这种笨拙之处还真像啊!
比如自动图块之类的,也感觉是往不方便的方向倾斜呢!!
因此,当需要把字符串作为数值处理时,最好使用 Number() 等方法显式地(以在脚本中可见的形式)将其转换为数值后再使用。
另外,如果要显式地拼接字符串,可以使用 concat() 方法。
"123".concat( 4 ); // 结果是字符串 "1234"
不过,这个方法不太常用,因为 + 要快得多呢…。
如果使用反引号 ``` 和 ${},还可以写出像把值嵌入字符串中的写法。
const n = 100;
const result = `找零是 ${n} 日元`;
反引号支持包含换行的多行字符串,因此在处理较长的字符串时,可以写得非常清晰易读。
不过,毕竟我们不会在原本就很窄小的“脚本”框中写很长的字符串,所以只要做到看到示例中用了反引号时不感到惊讶,这个程度就可以了。
关于使用反引号的写法,更多内容请参考 MDN!
小知识
话说回来,什么是 operand 呢?是像《爱乐之城》那样的音乐剧吗?
并非如此。并非如此。
Operand 是编程术语,意思是“作为操作对象的数据”。
……直接用“值”或“数据”不就好了吗?
顺带一提,操作的那一方叫做操作码(opcode)……为什么只采用了 operand 这个词,也是个谜。
即使你常年编程(当然也取决于开发环境),使用 operand 这个词的情况也很少见
<Bad!> 那为什么要把这个词用在以“不会编程也能做游戏”而闻名的 rmmz 的术语里呢!?
从字符串中获取一个字符
实际上,字符串可以很方便地指定位置,因此用一个变量就能进行多种判断。
获取变量 ID 1 中第4个字符的方式如下:
$gameVariables.value( 1 )[ 3 ] === "字";
请注意,编号从 0 开始,因此指定第4个字符时要用 3。
前面的圆括号 ( ) 是用于执行方法的括号,后面的方括号 [ ] 是用于对方法的返回值(字符串)写索引的括号。
因此可以分解为如下形式:
const string = $gameVariables.value( 1 );
string[ 3 ] === "字";
像这种简单情况虽然没必要分解,但知道分解的方法,在很多地方都能灵活运用。
如果指定了字符不存在的位置,会返回一个特殊值 undefined,意思是“未定义”。
将它和字符串比较时,结果总是 false
另外,因为它是字符串,所以可以使用 js 自带的那些方法,特别是活用正则表达式,可以实现复杂的处理。
如果在这里展开说明,篇幅就不只是这一页的程度了,所以请去看大家都喜欢的 MDN。
正则表达式
也许你在之前调查 rmmz 或 js 相关信息时,曾经瞥见过正则表达式的字符。
查看帮助的话……别说正则表达式了,连部分匹配的说明都没有。
<Bad!>好歹写一下啊!!
正则表达式是为进行字符串搜索(模式匹配)而创建的符号体系。
不仅是 js 以外的编程语言,文本编辑器……以及如前所述,我们的 rmmz 中也会使用这种通用机制。
基本格式相同,但不同环境下存在细微差异,这点比较麻烦。)
下面脚本中的 r.+ 这一部分在正则表达式中表示“以 r 开头,后面跟着一个以上非空白字符”。
既然能将这么多信息浓缩在 r.+ 这样短的字符串中,我想你已经体会到正则表达式的便利了。
"This is a regular expression!".search( /r.+/ );
对字符串 "This is a regular expression!" 执行 search() 方法,并将正则表达式对象作为参数传递。
结果是 10,这就是正则表达式所指定的字符串所在的位置。
如你所见,正则表达式对象可以通过用 / 括起来生成。这类似于字符串用 "、' 或` `` 括起来的感觉。
由于可以进行极其灵活的处理,学习意愿旺盛的朋友请务必掌握并活用正则表达式。
要真正掌握正则表达式,需要花费相当于学习一门新编程语言的成本,所以一开始可以先浅尝辄止。
顺便一提,我自己也并没有那么扎实地掌握正则表达式。
哦哦,你注意到了啊。照例又要把锅甩给 MDN 了!
如何一次性使用多个值?
如果把字符串中的一个字符看作一个开关,那么用一个变量就能管理大量的开关。可能有人会这么想。实际上确实能做到。
直观上,像下面这样写似乎就能替换了。
$gameVariables.value( 1 )[ 3 ] = "変";
这是把刚才出现的脚本中的比较运算符改成了赋值运算符。
虽然可以用 [ ] 取出字符串中的一个字符,但用这种方法替换却行不通。
下面是替换脚本的示例。
const str = $gameVariables.value( 1 );
const result = str.substring( 0 , 3 ) + "変" + str.substring( 4 );
$gameVariables.setValue( 1, result );
如你所见,很麻烦,所以不推荐这样做!
也许有人会想:利用数值的位数不也能处理多个开关吗?
很敏锐。js 也能处理二进制,可以进行位运算。
如果是标志管理,比用字符串更简单!(像鲍勃的绘画教室那样的笑容)
……直觉敏锐的你,没错,不可能像鲍勃那样顺利!!
为了将多个值集中在一起使用,js 中提供了一种叫做“数组”的东西。
位运算
那么,刚才顺带提到的位运算,你有点在意吗
你可能听说过计算机中使用的数字以二进制为基础,基本上可以认为“二进制 = 位”。
也就是说,位运算就是“对二进制数的计算”,js 也提供了相应的机制。
二进制数在开头加上0b来表示。
0b10000;
结果是 16。从这个结果可以看出,在 js 中它被当作与普通数值相同的十进制数来处理。
咦,不懂二进制?那“二进荞麦面”就懂?网络搜索是你的朋友!
那么,位运算是处理二进制数时很方便的运算,例如 & 运算符会将两边都是 1 的位保留为 1,其余位设为 0。
0b11111 & 0b10000;
结果是 16。也就是说,可以只提取出位为 1(即为 1)的位状态。
无论哪个位被保留,在 js 中 0 会被判定为 false,0 以外的数值会被判定为 true。
利用这一点,不就可以实现前面所说的“在一个数值中存放多个开关”了吗!!!
……嫌麻烦?以前大家可都是用这个来管理标志的啊!你们也得给我做!!(老害发言)
所以,让你久等了,MDN 来了!
用位来处理标志甚至被说成是一种坏习惯呢……。
在 rmmz 中,地图数据的图块属性是用位来处理的,但即使不直接操作,也有方法可以将其分解后取出。
因此,我认为在脚本中使用位运算的机会几乎为零。
Tori Beer♪
而且可能有人已经注意到了:因为位运算符用了 &,所以逻辑运算符就不得不使用 && 了……。
js 属于 C 语言系,所以这可以说是 C 语言的责任,但在 C 语言使用的领域中会频繁使用位运算,因此这并非那么奇怪的判断。
所谓各有各的历史,编程语言也是经历了各种曲折才确定了现在的规格,有些东西乍看很奇怪,但往往在制定时是无法预料的。
js 的情况大概是:随便把 C 语言的规格拉过来匆匆制定,没想到后来会被如此广泛使用。
因此,js 的规格有时并不符合现状,但既然谁都无法预测,就不要去苛责了。
我个人最多只会在心里骂“这该死的规格!!!”(其实已经全漏出来了?)
数组
数组…给值编号并排列而成的值。示例: [ 10, 4, 6 ] (这里并列的是数字作为值,但也可以并列其他类型的值)
之前对数组的解释比较粗略,这里再稍微详细说明一下。
在 js 中,它是 Array 这个类…MDN!MDN!
诶,太快了?
数组也和字符串一样,可以通过“变量的操作”中的“脚本”放入变量中。
像下面这样用 [] 括起来,按顺序写上值就可以了。
[ 10, 4, 6 ];
然后,在“显示文章”中写成 \v[1] 这样的形式,值就会以逗号 , 分隔并连接起来显示…不过。
和字符串相比,直接这样使用的情况大概很少。
那么具体怎么使用呢?我们来看详细内容。
数组的获取
那么,虽然我们已经能把值放入变量了,但取出时该怎么做呢?
在“变量的操作”的“脚本”中像下面这样写,就可以取出放入变量 ID1 的数组中的第4个值。
$gameVariables.value( 1 )[ 3 ];
因为数组的各个元素也可以放入数值以外的值,所以我们尝试用一下字符串。
[ "里德", 15, "♂" ]; // 赋值给变量 ID1
然后在“变量的操作”中取出并进行连接。
将变量 2 设置为“单独”,将“操作”设置为“代入”,在“操作数”的“脚本”中如下书写。
const a = $gameVariables.value( 1 ); "他是" + a[ 0 ] + "、" + a[ 1 ] + "岁 " + a[ 2 ]; // 赋值给变量 ID2
然后在“显示文章”中使用 \v[2] 就可以显示出来。
他是里德、15岁 ♂
应该会这样显示。
没有复制粘贴的人可能会报错…还是复制粘贴吧! 虽然偶尔也会写出错误的脚本…不,我在发布前是测试过的!但不知为何就是会出错!!
那么,刚才的脚本因为先把数组放入了常量 a 中,所以后面写起来很简单。如果不放入常量而直接写,就会像下面这样。
"他是" + $gameVariables.value( 1 )[ 0 ] + "、" + $gameVariables.value( 1 )[ 1 ] + "岁 " + $gameVariables.value( 1 )[ 2 ]; // 赋值给变量 ID2
顺便一提,使用反引号的写法是这样的。
const a = $gameVariables.value( 1 ); `他是${ a[ 0 ] }、${ a[ 1 ] }岁 ${ a[ 2 ] }`; //赋值给变量 ID2
如果你觉得“原本用了三个变量,现在只需要一个,很方便!”的话,可能感觉有点微妙。
但如果考虑能够使用变量的层级结构,那么比起把文件直接扔在根目录,用文件夹整理好的方式更易于使用,这是不言自明的。
向数组元素赋值
另外,如果能在“变量的操作”中直接给数组的各个元素赋值,会很方便,但这是做不到的。
因此,我们使用“脚本”,向上文变量 ID1 的数据中的索引为1的元素赋值。
$gameVariables.value( 1 )[ 1 ] = 10;
因为在字符串中虽然可以取出字符,但无法设定,相比之下数组的性能更高!
如你所见,写起来相当简单。
他是里德、10岁 ♂
这样,索引为1的元素就从 15 变成了 10。
顺便分解一下,就像这样。
const a = $gameVariables.value( 1 );
a[ 1 ] = 10;
使用这种方法,即使修改变量的值,也不会触发事件的重新扫描,因此不会根据“出现条件”改变事件页。
不过,原本就无法在“出现条件”中设置数组的值,所以我认为这应该不是问题。
事件指令中的开关、变量与数组
实际上,rmmz 中的开关和变量就是数组。
它们带有编号,并保存着与编号对应的值。
这不仅仅是概念上的说法,实际上核心脚本正是以 js 数组的形式来保存开关和变量的。
$gameVariables._data; // 这是数组!
也就是说,可以用索引来指定值。
$gameVariables._data[ 1 ];
如果只是这样,那还不如直接用“变量的操作”更简单。
正如刚才所做的那样,数组与字符串不同,可以对用索引指定的值进行赋值。
$gameVariables._data[ 1 ] = 10;
这里让人在意的是,它与下面的脚本有什么不同。
$gameVariables.setVariable( 1, 10 );
基本上是一样的,但区别在于:使用 setVariable() 时,设置的数值的小数部分会被舍去,并且在设置之后会重新扫描事件的条件,根据被改变的变量来切换事件页。
这与通过“变量的操作”改变变量时的行为相同。
相反,直接赋值给上面的 _data 数组时,可以设置小数部分,并且不会重新扫描事件的条件(说白了就是处理速度更快)。
在“变量的操作”以外的地方,同时存在“通过方法”和“不通过方法”两种操作的情况并不少见,但通常两者之间没有区别。
但是,也有像 setVariable() 和 _data 这样行为不同的情况。
基本上,使用方法的做法更接近事件指令的行为,所以最好还是使用方法。
字符串与数值的转换
字符串和数字相互转换的方法有很多,但我推荐使用在类名后面加括号的方式,这应该也是编程语言中比较通用的方法。
类型转换有时被称为 cast(type casting)。虽然我觉得“类型转换”就够了,但 cast 这个词也很常用,所以还是提一下。
另外,其他方法也会以一定频率出现,为了让你看到时不至于惊呼“这啥玩意儿?”,这里也一并列出来。
从字符串转换为数值
推荐。
Number( "2" );
像是小技巧的方法。
1 * "2";
即使字符串后面跟着多余字符也能转换。
通过设置第二个参数,还可以从二进制或十六进制记法的字符串进行转换。
parseFloat( "2円", 10 );
此外还会舍去小数点以下的部分。
parseInt( "2.8円", 10 );
从数值转换为字符串
推荐。
String( 2 );
像是小技巧的方法。
"" + 2;
`${ 2 }`;
不仅限于数值,基本上所有类都自带的方式。
2.toString();
结束语
以上就是关于可以赋值给变量的 js 值——“字符串(String)”和“数组(Array)”这两者的介绍。
如果能让你感受到“数值(Number)”所不具备的可能性,那就太好了。
这两个值,比起赋值给 rmmz 的变量来使用,也许更多时候会直接使用核心脚本的方法或属性返回的值。
关于那些方法…说起来又会很长,所以有机会再说吧。
另外,其他类的值也可以赋值给变量,但我觉得说明起来很麻烦却又不太有用,所以就不说明了。
顺便一提,关于“布尔值(Boolean)”基本没写,因为它本质上就是开关,没有存到变量里的意义,所以省略了。
因为变量会包含在存档数据中,所以请务必注意不要随意赋值数据量很大的值。
因为有可能你放进去的对象意外地持有很大的数据!
另外,我在想关于数组的循环处理是不是说明一下比较好,如果有机会的话再说吧。
不过,各位也已经用脚本做游戏大概一年了吧(这是前提),我想差不多也该能直接阅读用“js + 想查的内容”搜索出来的普通文章了。
Let's enjoy, ツクール生活!