跳到主要内容

玩转RMMZ的字符串变量

原文地址

我写了这三篇 js 入门类的文章,这次也算是入门的延续,来写写关于“值”的内容。

不过,感觉其中一半左右已经算不上入门了。

主要设想用在“变量操作-脚本”中,但也会时不时提到在其他命令中的用法。

不说明在插件中的用法。

在官方插件入门中,大概就是最速js讲座:变量这部分吧。

我想可能也有人想要正经学习 js 本身了,所以列举一些入门网站。

请随意挑选你喜欢的。

术语

这次是把之前粗略写过的术语像下面这样详细解说。

数组…给值编号并排列而成的值。示例: [ 10, 4, 6 ] (这里并列的是数字作为值,但也可以并列其他类型的值)

索引…读取数组(或保存数组的变量)中值时使用的数字。示例: array[ 0 ] (js 数组的索引从 0 开始)

字符串…将字符排列而成的值。在 js 中称为 String。也可以看作每个元素固定为单个字符的数组。示例: "这是字符串"

布尔值…取真(正・ON)和假(负・OFF)两种状态的值。在 js 中称为 Boolean,两个值分别用 truefalse 表示。

逻辑运算…计算结果返回布尔值的运算。包括判断数值大小等的条件表达式。

变量…用于保存值。js 的变量与 rmmz 的变量大致相同。定义示例: let variable; 以前定义变量时不使用 let 而使用 var,但 var 存在很多问题,现在(2022年时)已不太常用。反过来,如果看到代码中用了 var,即使是初学者也能轻易看出这是旧代码。另外,从 rmmz 开始,核心脚本已经废弃了 var

常量…与变量大致相同,但一旦设定值就不能再更改。乍一看似乎不便,但实际上比变量更方便,在实际编程中几乎不需要使用变量。定义示例: const constant = 1;

虽然有些啰嗦,但以下内容中,rmmz 的变量会用方括号括起来书写,而 js 的变量则直接书写,以此区分。

文字列

字符串…将字符排列而成的值。在 js 中称为 String。也可以看作每个元素固定为单个字符的数组。示例: String "文字列でーす"

虽然之前解释得比较粗略,但这里我想再稍微详细地说明一下。

最简单且有用的脚本,大概就是通过“变量的操作”中的“脚本”将字符串放入变量中吧。

像下面这样用双引号 " 括起来,就能轻松地将字符串赋值给变量。 img

"用双引号将字符串括起来就行了喵";

然后,在“显示文章”中写成 \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!

模板字面量 - MDN

小知识

话说回来,什么是 operand 呢?是像《爱乐之城》那样的音乐剧吗?

并非如此。并非如此。

Operand 是编程术语,意思是“作为操作对象的数据”。

……直接用“值”或“数据”不就好了吗?

顺带一提,操作的那一方叫做操作码(opcode)……为什么只采用了 operand 这个词,也是个谜。

Operand (オペランド) - MDN

即使你常年编程(当然也取决于开发环境),使用 operand 这个词的情况也很少见

<Bad!> 那为什么要把这个词用在以“不会编程也能做游戏”而闻名的 rmmz 的术语里呢!?

从字符串中获取一个字符

实际上,字符串可以很方便地指定位置,因此用一个变量就能进行多种判断。

获取变量 ID 1 中第4个字符的方式如下:

$gameVariables.value( 1 )[ 3 ]  === "字";

请注意,编号从 0 开始,因此指定第4个字符时要用 3。

前面的圆括号 ( ) 是用于执行方法的括号,后面的方括号 [ ] 是用于对方法的返回值(字符串)写索引的括号。

因此可以分解为如下形式:

const string = $gameVariables.value( 1 );
string[ 3 ] === "字";

像这种简单情况虽然没必要分解,但知道分解的方法,在很多地方都能灵活运用。

如果指定了字符不存在的位置,会返回一个特殊值 undefined,意思是“未定义”。

将它和字符串比较时,结果总是 false

另外,因为它是字符串,所以可以使用 js 自带的那些方法,特别是活用正则表达式,可以实现复杂的处理。

如果在这里展开说明,篇幅就不只是这一页的程度了,所以请去看大家都喜欢的 MDN。

String - JavaScript | MDN

正则表达式

也许你在之前调查 rmmz 或 js 相关信息时,曾经瞥见过正则表达式的字符。

查看帮助的话……别说正则表达式了,连部分匹配的说明都没有。

<Bad!>好歹写一下啊!!

正则表达式是为进行字符串搜索(模式匹配)而创建的符号体系。

不仅是 js 以外的编程语言,文本编辑器……以及如前所述,我们的 rmmz 中也会使用这种通用机制。

基本格式相同,但不同环境下存在细微差异,这点比较麻烦。)

下面脚本中的 r.+ 这一部分在正则表达式中表示“以 r 开头,后面跟着一个以上非空白字符”。

既然能将这么多信息浓缩在 r.+ 这样短的字符串中,我想你已经体会到正则表达式的便利了。 "This is a regular expression!".search( /r.+/ );

对字符串 "This is a regular expression!" 执行 search() 方法,并将正则表达式对象作为参数传递。

结果是 10,这就是正则表达式所指定的字符串所在的位置。

如你所见,正则表达式对象可以通过用 / 括起来生成。这类似于字符串用 "' 或` `` 括起来的感觉。

由于可以进行极其灵活的处理,学习意愿旺盛的朋友请务必掌握并活用正则表达式。

要真正掌握正则表达式,需要花费相当于学习一门新编程语言的成本,所以一开始可以先浅尝辄止。

顺便一提,我自己也并没有那么扎实地掌握正则表达式。

哦哦,你注意到了啊。照例又要把锅甩给 MDN 了!

正则表达式 - JavaScript | 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 会被判定为 false0 以外的数值会被判定为 true

利用这一点,不就可以实现前面所说的“在一个数值中存放多个开关”了吗!!!

……嫌麻烦?以前大家可都是用这个来管理标志的啊!你们也得给我做!!(老害发言)

所以,让你久等了,MDN 来了!

位运算符 - JavaScript | MDN

用位来处理标志甚至被说成是一种坏习惯呢……。

在 rmmz 中,地图数据的图块属性是用位来处理的,但即使不直接操作,也有方法可以将其分解后取出。

因此,我认为在脚本中使用位运算的机会几乎为零。

Tori Beer♪

而且可能有人已经注意到了:因为位运算符用了 &,所以逻辑运算符就不得不使用 && 了……。

js 属于 C 语言系,所以这可以说是 C 语言的责任,但在 C 语言使用的领域中会频繁使用位运算,因此这并非那么奇怪的判断。

所谓各有各的历史,编程语言也是经历了各种曲折才确定了现在的规格,有些东西乍看很奇怪,但往往在制定时是无法预料的。

js 的情况大概是:随便把 C 语言的规格拉过来匆匆制定,没想到后来会被如此广泛使用。

因此,js 的规格有时并不符合现状,但既然谁都无法预测,就不要去苛责了。

我个人最多只会在心里骂“这该死的规格!!!”(其实已经全漏出来了?)

数组

数组…给值编号并排列而成的值。示例: [ 10, 4, 6 ] (这里并列的是数字作为值,但也可以并列其他类型的值)

之前对数组的解释比较粗略,这里再稍微详细说明一下。

在 js 中,它是 Array 这个类…MDN!MDN!

Array - JavaScript | 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, ツクール生活!