从零开始的小黄油制作—— Milovana EOS Editor教程(4) 进阶行为与JavaScript

前言

Milovana EOS Editor可以借助JavaScript来实现更具交互性的文游制作。

本文主要补充介绍之前没有讲的行为类型,功能类型,并通过少量案例介绍JavaScript在Milovana EOS Editor中的效果。

要看这篇东西的话,最好是稍微掌握一点点JavaScript基础,一点点就可以了。当然如果零基础,我也尽可能举例子讲清楚(但愿如此吧)。

由于Milovana EOS Editor基本只支持ES5,这严重影响了我的个人代码习惯(好吧,其实我也很菜),所以这个教程可能会写得很烂(搞得好像前面几篇不烂一样)。

可以用Webpack、ESBuild等工具来写ES6,然后把相应的代码转换成ES5(例如通过Babel)。这能避免折磨和代码习惯的改变。(其实代码量没那么大就是了)

EOS Editor的IntelliSense会对不少实际上能运行的代码报错,不用担心。写上去就是了,能不能跑自己试一下就行了。如果卡住加载不进去,就是不行(例如用了ES6特性什么的)。


进阶行为

之前是怕劝退一些初学者(好吧,1和2两篇教程的介绍语言风格其实已经十分劝退),所以没有介绍一些比较复杂的行为,那么在这篇教程中,介绍一些之前遗漏的行为。


Disable

Disable(禁用)某个页面,意味着它无法被随机跳转的Goto Action定位。也就是说,被Disable的页面将无法通过随机跳转的方式进入。

举个例子来说明一下。

这个案例包括三个Page,一个start,一个testPage1,一个testPage2。

在start中,先通过Disable行为禁用testPage1。而后的一个Goto是跳转到testPage*,它理论上是定位到两个页面——testPage1和testPage2。但由于testPage1被禁用,所以最终只可能跳转到testPage2,出现的交互是Hello, testPage2!

这个功能可以用来做一些隐藏结局,冒险解谜之类的东西。

当然,这个页面名同样是可以指定范围——testPage*这样。那这样就是把testPage*这一组页面全部禁用了。


Enable

Enable(启用)就是反过来,把一些已经被禁用的页面重新启用,这样就可以随机跳转过去了。

页面本身默认就是Enabled状态,不用刻意去开启一遍。


Prompt

Prompt是一个交互动作,其实它很简单,弹出一个交互,让用户输入一个值,然后存储在一个变量当中。

它类似于这种,当然这是ES5,ES6就用let了:

1
var usrAge = prompt("");

Prompt行为的设置界面非常简单,就一个Variable,里面填的就是变量名。

一般是小驼峰(lowerCamelCase)命名法,注意规范。

(当然你要用UpperCamelCase或者snake_case也完全可以,当我没说)

这里举个例子来演示一下Prompt行为的作用。

在这个示例中,先是一个Say行为——Please input your age,提示用户输入年龄,然后是一个Prompt,把用户输入的年龄存储在usrAge这个变量中。最后是一个Say(这里稍微用了点Eval,后面会讲),把用户刚才输入的年龄显示出来。

测试一下,看看效果:

符合预期。总之效果就是这样。

这东西可以用来做usrName,usrAge,之类的,也可以用来比对什么的。总之作用很多,可以增加交互性。


If

这个,学过点编程的应该是再熟悉不过了吧。没事,就算是没接触过,也可以讲讲。没什么难度。

If,条件判断,分支。就是,先写一个条件,如果符合,那么怎么怎么做,如果不符合,那就另外如何如何做。

它的编辑界面由三部分组成——Condition, ThenElse

Condition是判断条件。

Then是条件为真(符合条件)时执行的行为。

Else是条件为假(不符合条件)时执行的行为。

讲得可能有点抽象啊,举个例子来说明一下。

这个例子可以看懂吧,上来一个Say,问用户1+1等于几。然后Prompt,用户填入答案。

接下来这个If,判断用户输入。

(注意这个EOS Editor的Prompt默认是把用户输入作为字符串(String)处理的

如果用户输入的是字符串”2”,那么回答正确,执行Then的Action。

如果用户输入的不是字符串”2”,那么回答错误,执行Else的Action。

希望这个示例足够清楚,可以把这个概念说明白。


基础行为与Eval

有几个基础行为有Eval界面,之前是怕太复杂,跳过去了没讲,今天讲讲。


Say

Say是可以在中间插入Eval Tag,可以理解为变量名,或者是一些数值之类的东西。

还是举两个例子来讲吧。

第一个例子,输入用户名:

一上来,一个Say提示用户输入用户名,然后一个Prompt,把用户输入的字符串存在usrName这个变量里面。

最后这个Say,类似于一个字符串拼接:

1
"Hello, " + usrName

它把用户刚才输入的用户名拼接到这个字符串里面打印出来。

看看效果:

第二个例子,显示时间:

看一下输出结果:

也可以用getFullYear(), getMonth(), getDate()这种方法来调取一些具体的时间。

总之,这个功能可以把一些参数展示出来。包括显示好感度等等,看设计者怎么用了。


Goto

Goto可以内置一些JavaScript语句,这其中可以写一些复杂的逻辑,最终决定跳转到哪个页面。

跳转的语句就很简单,一个字符串就行了。比如说你要跳转到testPage1,那就写:

1
"testPage1";

举个例子来说明一下这种写法:

在这个例子中,Say提示询问用户想要去哪个页面。

如果用户回答”1”,那么Goto就跳转到testPage1,否则跳转到testPage2。

测试一下运行效果,是符合预期的:

输入”1”,跳转到testPage1。

输入”2”或者什么别的,跳转到testPage2。

这个用法能用来做什么呢?有人说和之前If不是一样吗?确实。但是也不一定局限于If。你可以在里面写Switch Case,写随机数,写数学计算……总之很多,看你自己。


Timer

Timer同样是可以内置一些JavaScript语句,这可以产生一些随机性,或者是按照一些具体情况来计算出计时器时长。

在Eval当中,Timer接受的时间单位是毫秒(ms)

比如说,你想要让一个计时器的时长设定为5秒,那就这么写:

1
5000;

还是一样,举个具体的例子来说明这个用法。

在这个示例中,先是弹出一个Say,提示用户输入计时器的时长,单位是秒。然后是一个Prompt,读取时长。

最后是一个简单的数学计算,从用户输入(这是一个字符串)提取出整数部分,乘以1000,换算成毫秒,作为计时器的时长。

来看看运行效果:

可以看到,用户输入60,随后产生了一个1分钟的计时器。这个行为是符合预期的。

那,这个例子也就是说明一下用法而已,至于你要怎么得到计时器的时长,那完全是自己决定的。随机数?根据之前的互动参数来算?设置If-Else或者Switch-Case的几个分级?都可以。


Init Script

Init Script里面可以写点JavaScript代码,Webtease加载的时候就执行这部分代码。

在UI的Options-Init Script这里。

这块地方可以写点变量定义,函数定义什么的,可以节省后面的工作量。

如果打不开,就说明用了不支持的JavaScript特性。还是老问题——这EOS Editor只支持到ES5。实在不行就在本地写点东西然后用Webpack、ESBuild这种工具打包成ES5代码复制粘贴上去吧。

Init Script里面的东西,后面写的时候也还是没有IntelliSense的。注意一下,可能会出现拼写错误什么的。

还是举点例子来讲一下吧。


生成随机整数函数的例子

比如说,要在Init Script里面写一个生成随机整数的函数,之后要用:

1
2
3
4
5
6
function getRandomInt(min, max){
min = Math.ceil(min); // 最小值向上取整
max = Math.floor(max); // 最大值向下取整
return Math.floor((Math.random() * (max - min + 1)) + min);
// 对0-1之间的随机值放大(max-min+1)倍,加上最小值后向下取整
}

随后在Say的Eval Tag中调用这个函数,比如说生成一个在0-10之间的随机整数。

1
getRandomInt(0, 10);	// 调用函数,最小值为0,最大值为10

如图所示:

运行结果如下图所示:

总之,类似地。可以在Init Script中写一些函数,提高代码重用性。

毕竟在Eval甚至是Eval Tag里面,照理是不应该写太多代码的。


定义类的例子

比如说,要在Init Script里面定义类,以便之后创建对象。

那,由于这是ES5,就按照ES5的写法来好了,构造函数。当然,ES6的class语法糖用惯了可以看看Webpack和ESBuild。

比如说要定义一个对象,Player,它有三个属性——HP, MP, ATK。

1
2
3
4
5
6
7
8
9
function Player(hp, mp, atk){
this.hp = hp; // 生命值
this.mp = mp; // 能量值
this.atk = atk; // 攻击力
}

Player.prototype.getInfo = function(){
return "HP: " + this.hp + ", MP: " + this.mp + ", ATK: " + this.atk;
}

在Eval Action中可以调用构造函数创建对象:

1
var playerAlice = new Player(100, 80 ,20);

而后在Say Action的Eval Tag中,可以调用原型函数getInfo()来查看角色的属性信息:

1
playerAlice.getInfo()

上图片:

定义构造函数和原型函数:

创建对象:

调用getInfo()获取信息:

测试界面输出结果,符合预期:

当然,功能远不止这么点。希望各位读者自行发挥想象力。


teaseStorage

teaseStorage是一个可选功能,类似于JavaScript的localStorage。

要用这个功能的话,在Options-Storage这个界面打开。

空间不大,变量名不能太长,省着点用。

在teaseStorage中,数据以键值对的形式存在。主要的操作有设置、读取。

设置数值:

1
teaseStorage.setItem("playCount", 0);

读取数值,不存在则返回null:

1
var playCount = teaseStorage.getItem("playCount");

移除数值:

1
teaseStorage.removeItem("playCount");

查看长度:

1
var storageLength = teaseStorage.length;

查询键名:

1
var keyName = teaseStorage.key(0);

清空全部内容:

1
teaseStorage.clear();

这么说可能稍微有点抽象吧,举个具体的例子来说。

就比如说,要启用一个功能,统计玩家到底玩了几次这个Webtease:

1
2
3
4
5
6
7
8
9
var playCount = teaseStorage.getItem("playCount");

if (playCount == null) {
playCount = 0;
}

playCount++;

teaseStorage.setItem("playCount", playCount);

会报错的,你别管EOS那个报错,这报错简直是乱来。能跑就是能跑,别慌。

这个例子中,先创建一个变量用来存储玩家的游玩次数。从teaseStorage中尝试读取。

读不出来的话赋0+1,读出来的话就正常+1。最后写回。

(代码习惯稀烂)

测试运行效果:

可以用来存储一些长期保存的键值对,比如说游玩次数,关卡进度,好感度什么的。这样在start里面再做个分支跳转,可以对一个较长的webtease保存游玩进度什么的(不过真的有人会分好几次玩吗?)


结语

本文差不多简单介绍了一下之前第二篇行为和机制没有涉及到的一些东西,主要都是JavaScript相关的东西。

这些内容能够帮助制作出来的文游更具交互性。

当然,作者本人的水平很次,代码习惯也很烂。有问题还请指出。

如果你觉得文章写得还不错,可以赏作者一杯咖啡喝,或者一顿饭吃。感谢支持!THYzrcoMQf7d7wzGu1PvDraTef87abSv9V