|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 百里_飞柳 于 2022-6-27 11:38 编辑
(写点没人看的东西)
在RM的游玩过程中,事件的编写可以说占据了一大半的时间。可视化的新建事件、高效全面的事件指令、直观的触发方式,充盈了整个游戏流程的血肉。
以下的插件脚本,如果链接失效,请直接前往我的GitHub仓库中的Event System目录下查找。
(毕竟我很可能会进行一些没啥用的重命名)(Gitee禁止了未登陆用户访问,就很怪)
0
先对默认设计的几种事件执行方式进行一些说明:
“按键触发”是最基础的方式,当玩家移动到事件面前,按下确定键(空格键)就能执行该事件,效果等同 event.start (比如 $game_map.events[1] 就是 1号事件 的 Game_Event对象),本质上是给该事件设置了一个flag,而在 $game_map 中有一个实时监测的事件解释器,当监测到有 flag 的事件时,就会把它的当前事件页的内容拉过来执行,也因此,当任一事件执行时,其它事件就没法再执行了(而玩家的按键移动也有判定,当存在事件执行时,不准移动)。
“玩家接触”和“事件接触”的原理是一样的,只不过一个是在 $game_player 中判定,一个是在 Game_Event 中判定,具体的,在进行移动但失败时,如果面前一格是事件/玩家,那就给该事件/自身设置 flag,然后再由 $game_map 的事件解释器来后续执行。
“自动执行”就更简单了,每一帧都会给自己设置 flag,也就是说它在疯狂抢占 $game_map 的事件解释器,不留给其它事件机会,因此存在这类事件,且没有及时消除、关闭时,游戏就会和卡死了一样,根本无法操作。
“并行执行”就有一些特殊了,它会在事件生成时,同时给这个事件一个专属的事件解释器,确保了它能够顺利执行,不会抢占其它事件的执行资源。但要注意,一些公共资源(比如对话框)还是会抢占的。
1
特别的,在事件指令中的“公共事件”,它相当于把某个公共事件的全部内容拉了过来,全部执行完成后才会继续。这里有一个特别要注意的地方,公共事件中的事件指令“暂时消除事件”因为是依据事件ID,所以没有问题,还是消除了触发公共事件调用的原事件,但公共事件中的事件指令“中止事件处理”只能中止公共事件的执行,无法中止原事件,
“公共事件”是一个非常方便的设计,能够将重复的内容提取出来,就如同Ruby中的方法(可惜公共事件没法传入参数,只能自己约定全局变量),但是什么都写进公共事件的话,总觉得有点太臃肿,而且数量多了后,也有点不好整理和搜索。
注意到03都可以自由调用任意事件页来执行,怎么XP之后就删去了这个指令呢?
我写了一个这样的扩展:呼叫指定事件
不仅可以如同调用公共事件一样,调用任意地图中的任意事件的任意页,还可以在菜单等原本无法执行事件的地方,执行事件页(当然,功能有删减)。
在事件“脚本”中,编写 call_event(mid, eid, pid) 来执行指定的事件页,其中 mid 为地图ID,eid 为编辑器中的事件ID, pid 为事件页的序号(0时代表取符合出现条件的最大页)。
当然,这个调用和“公共事件”是一样的,其中指令的【本事件】都会变成当前事件。
2
在进行一些面向对象编程时,我发现“作为一个独立个体”的方式真的很直观,那么能不能用到事件上呢?
在利用C++的QT时,令人印象深刻的是GUI中的消息机制,其实也就是“客户端-服务端”的模式,“服务端”时刻刷新,查看是否有来自“客户端”的消息,如果有,就及时做出响应。
据此,我设计了这样一个插件:事件消息机制
将事件作为一个独立更新的个体,外界可以给它传入消息,然后它自己进行检索,看看自己是否能够响应这个消息(存在预先写好的对应内容),如果能,就处理一下。
具体的,对于一个事件 event ,可以给它传入一个 “被击退” 的消息,即 event.msg("被击退") ,随后,它会在自己的全部事件页中查找有没有 “被击退” 的标签,如果有,就会创建出一个并行执行的解释器,来执行 标签“被击退” 到 标签“END” 之间的指令。
依据这样的思想,就可以很容易实现各种互动,比如子弹击中敌人,本质上可以是开枪时,朝玩家前方检索事件,如果检索到了一个满足条件的事件(比如名称中含有“敌人”),就可以给这个事件发送一个 “被击中” 的消息,然后在这个事件里去处理后续效果。
当然,这个模式下还存在一个巨大的问题:需要能够进行事件检索,才能知道到底要给哪个事件发送消息,而这,只能依靠自己手动编写……
此外,每个事件都要在自己的事件页里编写执行内容,哪怕用公共事件之类的偷懒,也还是要编写标签,才能让事件知道自己可以响应,于是在以后需要修改时,会是地狱吧(确信
更关键的,没有参数,真的没有地方可以塞参数啊,事件没有只属于它自己的变量(就连扩展的独立变量,其实都是在一个全局变量里划分出一小块,依据地图id、事件id、变量id来检索……),使用事件指令时也没法方便调用自定义的参数。
3
在游玩各种新游时,我发现其中的NPC都会有不止一个功能选项,尽管在RM中可以利用选择框来进行设置,但额外的确定、切换、取消操作略显繁琐,而且在编辑器里套了一层选项后,再后续编写其它内容就有点臃肿了。
为此,我编写了一个简易的互动扩展UI:事件互动扩展
当玩家面对事件时,且已经是按下确定键就能触发的状态时,将会显示一个简易的指令UI,用于展示该事件的全部功能,以及按下确定键后将会执行的功能。此时,按下 SHIFT 键可以切换到下一个功能,而按下方向键,将正常移动走人,并自动关闭指令UI。
本质上还是将当前事件页利用标签对进行了划分,标签对的格式继承了“事件消息机制”。当默认中使用 event.start 来表示执行当前事件页时,该脚本中使用 event.start_ex(sym) 来执行指定的互动(其中 sym 为对应标签的字符串,比如之前说过的 "被击中"),当然,执行时效果是一样的,只是被执行指令的范围缩小了。
同时,也兼容了“呼叫指定事件”,使得它也能指定事件页中更小的范围了。
同时,也兼容了“事件消息机制”,因为标签对格式一样,也可以用 event.msg(sym) 来并行执行。
4
在进行地图上的简易ARPG的编写时,我发现,阻碍的有几个方面:一是技能的判定范围,即检索指定范围内的事件,二是敌人事件的编写,即如何处理敌人的攻击、受伤,甚至是特殊的动作。
关于技能判定范围,依然没有找到什么较好的方法,于是现在还是只能是yyds的圆心判定……
而关于敌人事件,之前在创作《弱水愿》时,我完全使用了“事件消息机制”和“事件警报机制”,最后果然被绕晕了,全是并行的判定,而且每个敌人都要写一遍整套的,如果不是有“事件拷贝”,可能就直接摆烂了。
在我继续更新这个脚本时:简易事件ARPG扩展
我尝试将事件个体进一步独立化,即执行的内容不再局限于自己预先编写的,也可以是别的事件编写好的,而这不同于“呼叫指定事件”的是,此处的执行必须要是并行的,因为不可能等着它判定完才能往玩家继续移动啊。
于是,对于一个事件 event,可以通过 event.run_event(params) 来让它为【本事件】,去并行执行指定的事件页,比如 $game_map.events[1].run_event({:mid => 1, :eid => 1}) 就是让 1号事件并行执行 1号地图1号事件的(符合出现条件的事件页的)内容。
这样做,就相当于把这些事件本应该写在它自己事件页里的内容,又提取了出来放到一个独立事件里,后续想要修改时也更加方便,而要制作特殊情况时,也可以在事件脚本里对当前事件进行判断。
5(2022.6.27)
上一节中提到了“事件警报机制”,它是一个给事件附加的实时判断,当满足编写的条件时(编写的Ruby脚本在被eval后返回true),将执行预先编写的Ruby脚本,也就相当于每个事件都自带了一个并行,只不过判定条件和执行内容都是依靠在注释中编写脚本。
但在实际使用的过程中,我发现了几个问题:每个事件的注释中都要重复写一大段脚本;如果想要的判定稍微复杂一点,在一个注释指令中甚至可能写不下,还是要在脚本中自己写全局方法,再在注释中调用;后续修改时很不方便,一大段文本没有换行没有高亮,属于是写完就扔不想再看的程度了。
于是我重新把它写成了:事件自动触发,同时废弃之前的警报机制。
比如是事件页开头的注释中编写 <自动 eval=玩家距离2 sym1=惊讶>,就是利用脚本中的 “玩家距离2” 所对应的预设脚本(具体功能是判定玩家与当前事件的距离是否小于等于2),当条件返回真时,就触发当前页的“惊讶”互动,如果没找到该互动,那就直接触发当前事件页。
在新插件中,统一把需要判定的Ruby脚本放置于了脚本的常量中,在事件注释中只需要指定该脚本的预设名称,这样做大幅简化了注释中的设置,但可惜不能传入参数(因为我丑陋的各种封装),同时满足判定条件时的执行内容变成了当前事件页,而如果使用了之前说的“事件消息机制”或者“事件互动扩展”,同样可以指定当前页中的对应标签对,并且引入了三个不同的执行时机:判定条件由不满足变成满足时、判定条件一直满足时、判定条件由满足变成不满足时,分别都可以设置独立的标签。
本质上其实是自造的一个自动执行,大概方便制作既要按键触发,又要自动触发的事件吧,但我也还没实际用过(心虚)
6(2022.6.27)
在不断使用“呼叫指定事件”时,我发现了一个关键的不足。在同时使用比如“事件互动扩展”时,不得不这样来设计:
标签:某个动作
脚本:call_event("eid=1, sym=某个动作")
标签:END
因为“事件互动扩展”只会读取标签指令,并不会关注你实际运行什么指令,也就导致了如果想用“呼叫指定事件”来偷懒,不得不再编写一组标签让它知道能够响应这个动作。
偷懒了,但没完全偷懒,如果中途修改了动作的名称,全部事件又要改一遍……
这真的不符合快乐做游戏的原则!
所以我给“呼叫指定事件”进行了一次小小的升级:现在可以通过事件注释,来把指定事件页的内容直接全部贴过来了!
比如编写事件注释: 事件页替换 | mid=1 eid=2 pid=3 ,就是把1号地图2号事件的第3页的全部内容放置到该注释位置,同时删去该注释,实际效果就和在该位置编写了这些事件指令一样,所以也就能保证那些读取注释、读取标签名的插件脚本都能正常使用了。
待续...
|
评分
-
查看全部评分
|