加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 ⑨姐姐 于 2018-9-12 16:36 编辑
一直想开这个坑,今天终于开了。这一系列会专注于一些比较边缘地带的知识,它们经常被作者们当做“玄学”,或者被不屑一顾。但是,每个现象都是有它的道理的,深入了解下去,就能对RM的运作方式更加熟悉,制作出更符合心意的游戏效果。
这系列面向的是有一定事件基础的制作者们,并没有脚本水平的要求(完全不会也没关系)。另一方面,也许写了很长时间脚本但对RM的默认内容关注不多的,也能从中得到一些有用的东西吧。
欢迎反馈关于内容深浅以及描述方式等等的各种问题,也欢迎提供各种大家在RM中的“玄学”以及一直当做习惯却难以理解的地方,在以后的几期中也可以把这些作为主题的。
-------------------------------------------------------------
一天,埃里克百无聊赖地在海洋上行走(不要问他是如何做到的)。
他觉得,是时候开始游戏了。“开始游戏”……就是说需要个游戏标题:
用图片当做三个按钮,然后用并行事件判断当前的选项和图片的透明度,像这样——
可是,埃里克发现了一个问题:按左右键的时候,不是每次都成功的,有时候按了但是毫无反应。
“为什么我的事件标题手感不好呢?明明所有地方都写对了。”
“你遇到问题总是愁眉苦脸,这可不是勇者该有的样子呢。”娜塔莉不知什么时候出现在这里。
“我猜,按键没反应,是不是RM内部的按键输入时好时坏?”
“不可能。”娜塔莉否定了埃里克的猜想,“如果RM的输入真的有问题,为什么其他菜单的窗口都那么流畅呢?”
“难道是很多人说的,事件的效率天生不如脚本,用事件就会卡顿?呜呜呜,我要去学脚本……”
“给我打起精神来你这白毛!你忘了事件的背后就是脚本在运行吗?这两者又有什么区别呢。”
“事件的背后就是脚本?”
“一看你就没有好好听大贤者的讲课。来,我们打开脚本编辑器,找到Game_Interpreter。46行。”
#-------------------------------------------------------------------------- # * Event Setup #-------------------------------------------------------------------------- def setup(list, event_id = 0) clear @map_id = $game_map.map_id @event_id = event_id @list = list create_fiber end
#--------------------------------------------------------------------------
# * Event Setup
#--------------------------------------------------------------------------
def setup(list, event_id = 0)
clear
@map_id = $game_map.map_id
@event_id = event_id
@list = list
create_fiber
end
在埃里克疑惑的目光中,娜塔莉解释道:“你看,这个setup,就是把事件的内容放入脚本中去执行了。list是事件列表,event_id是事件编号。所以我们编辑的每一个事件,其实都会丢到脚本里去执行的。既然这样,为什么要说事件速度慢呢?根本就是无稽之谈。”
埃里克还是没有完全搞懂:“我还记得一些脚本,这种等于号的都只是把一个值放到另一个变量里的操作,比如a=2就是把a变成2。就算我生成了好多个变量,但它们也只是数据倒来倒去而已,事件放进去以后,怎么执行呢?”
“事件执行的奥秘,就在create_fiber里了。”
娜塔莉一遍说着,一遍顺着脚本往下追溯:
#-------------------------------------------------------------------------- # * Create Fiber #-------------------------------------------------------------------------- def create_fiber @fiber = Fiber.new { run } if @list end
#--------------------------------------------------------------------------
# * Create Fiber
#--------------------------------------------------------------------------
def create_fiber
@fiber = Fiber.new { run } if @list
end
#-------------------------------------------------------------------------- [font="]# * Execute[/font] #-------------------------------------------------------------------------- def run wait_for_message while @list[@index] do execute_command @index += 1 end Fiber.yield @fiber = nil end
#--------------------------------------------------------------------------
[font="]# * Execute[/font]
#--------------------------------------------------------------------------
def run
wait_for_message
while @list[@index] do
execute_command
@index += 1
end
Fiber.yield
@fiber = nil
end
“埃里克,你看这个run,不就是执行事件的过程吗。execute_command的意思是执行一条事件指令,@index += 1的意思是把事件执行的位置往下移一格。你可以想象,你用铅笔指着一条事件,执行完以后再执行下一条事件的感觉。”
埃里克反而更疑惑了:“从这里看来,我上面写的事件应该是一点问题也没有啊,怎么可能按键失效呢。除非事件没有正常执行,没能通过条件分歧判断到我的按键,才会出现失效的情况吧。”
娜塔莉沉思片刻,两眼突然放出了光芒,把埃里克吓了一跳:“我明白了,你的事件确实没问题,问题出在事件结束的时候。埃里克,我考你一个问题,从上面的run当中,你能看出事件结束的时候会发生什么吗?”
“事件结束的时候,@index就超过事件列表的长度了,所以@list[index]就拿不到事件的内容,这时候while……”
“答对了,while @list[index]的循环就会正常退出,事件也就结束了。接下来呢?”
“接下来……Fiber.yield,这是什么意思呢?”
娜塔莉把代码翻到了Game_Interpreter的236行:
#-------------------------------------------------------------------------- # * Wait #-------------------------------------------------------------------------- def wait(duration) duration.times { Fiber.yield } end
#--------------------------------------------------------------------------
# * Wait
#--------------------------------------------------------------------------
def wait(duration)
duration.times { Fiber.yield }
end
“这里duration.times就是重复duration那么多次的操作。你看,一个叫做wait的东西,重复了那么多次的Fiber.yield,还不能猜出Fiber.yield是做什么的?”
埃里克恍然大悟:“wait我知道,就是事件里的等待。用N次某个操作组成了等待N帧,那某个操作就是‘等待1帧’了吧。”
娜塔莉点点头:“在这里确实可以理解成等待1帧。准确地说,是暂时退出Fiber上下文,去执行其他内容,等到1帧以后再来继续执行这个Fiber。”[如果想要了解关于Fiber更多的知识,可以看这篇文章的介绍]
“你还是别讲Fiber啊上下文什么的了,我暂且理解成等待1帧没错吧,那为什么是这里会导致出问题呢?”
“并行事件只要在地图上,它就会循环重启执行,这一点你作为事件党,应该很了解吧。”[如果想要了解它的原理,可以参考Game_Event脚本,一直拉到最后就能看到的def update]
“那是当然,我埃里克再怎么说,也是游戏制作勇者啊。”
“可是你发现了吗,你用来判断按键的并行事件,其实不是和游戏帧率一样,每秒60次,而是每秒30次。”
“每秒30次……也就是实际上一次并行事件占用了2帧吗。是不是因为,当到达下一帧的时候原本并行事件该重启了,结果却因为上面多等待了一帧,结果没有重启?这等待的一帧,就是我的键盘‘失灵’的时机?”
“你终于明白了。”娜塔莉笑道,“实际上你每次按键,都是在赌随机数啊。赌你按键的时刻是不是恰好落在执行事件的一帧上。假如落到了等待的一帧,那就没反应啦。所以快改脚本吧。”
“不行,不行。我作为勇者,怎么能认输呢,我偏要用事件的方式,把这个问题解决了。”
“这回你倒是说对了,说不定有什么其他人的脚本,会利用到这条特性呢。那就看看你能用事件搞出什么花样吧。”
只过了片刻,埃里克就找到了解决问题的办法:
果然,这样处理以后,按键就完全流畅了。
那么今天的课后习题就是,为什么这样处理以后按键流畅了呢?上面提到的“多出的一帧”去哪儿了呢?
用到的工程:
DeviantEventTutorial1.zip
(1.33 MB, 下载次数: 291)
正确答案 |