RPG Maker可以用来制作几乎一切2D游戏。
以下的讨论以RMVA版本为主(大部分适用于XP和VX),MV因为了解有限再加上整体感觉有些微妙的缘故,暂时不做讨论了。用MV制作其他类型的游戏,可以参考pixi引擎。(只用pixi写过半个ARPG系统,感觉不论是效率还是易用上都还不错)
RM的结构是底层RGSS,可以看作一个2D通用引擎(里面有些为RPG提供方便的东西),然后RGSS调用Ruby脚本,解释执行,在脚本中再去访问编辑器保存的各种数据(比如RMVA内部的load_data函数读取角色、装备等数据),驱动游戏的运行。
因此,只要把默认脚本删掉,换成自己的脚本,就可以实现各种2D游戏功能。在自定义的脚本中,也可以根据自己的喜好来随意利用编辑器产生的其他数据,比如把默认地图当做横版射击游戏的背景,或当做战棋游戏的格子,等等。
用RM制作2D游戏,会遇到以下的一些局限(也就是说这里是RM做不到的,除此之外应该都没问题):
- RM的底层实现效率不高,普通PC上只能允许几百个(带旋转半透明)精灵的存在。所以用来制作弹幕游戏,需要好好考虑优化问题;
- RM没有提供高效的绘图接口和shader机制,所以想做一些复杂的混合效果,比如法线映射,或者视野遮挡阴影,需要用其他的方法来绕过限制,也相对麻烦;
- Ruby的运行效率有限。这一点往往并不凸显,因为对RPG游戏来说是够用的;但如果要进行计算密集的任务,比如棋类游戏的AI,就难以胜任了。
另外,制作2D游戏,RM也不一定是最好的选择,要不要选择RM还有以下的因素:
- 我的游戏会受到RM的条件局限吗?比如上面说的性能限制,再比如RM的游戏默认只能在windows运行,在其他平台需要额外的第三方工具;
- 我的游戏能利用多少RM提供的东西?因为RM默认提供了许多内容,如果全部放弃不用,相比而言还不如另选一个引擎从头开始,列举如下:
- 数据库和地图编辑器,如果游戏类型适合RM的地图和数据库(比如战棋),那么选择RM就有先天的方便;而用RM写交互小说,虽然功能上可以实现,但事件编辑器的格子实在不适合展开大段的叙述。
- 场景机制和RPG解决方案(虽然并不能说优雅),如果想要制作FTG,可能就借用不了多少默认脚本资源了;
- 用户输入和游戏UI,RM实现了一套朴素的键盘输入,以及在脚本中有RPG常见的窗口选项机制和它兼容。如果游戏考虑用鼠标,则要多加注意了。
用RM写过一个战棋系统和一个消除类游戏系统(链接就算了有点羞耻>///<),觉得许多事情都是RM能做到的,只不过在着手用RM之前,需要考虑这样的问题:RM是最适合我的工具吗。
下面是一些具体技术方面如何突破常规RPG的内容,RM使用者想要挑战RM表现力可以参考
Q:RM是如何突破默认输入的?比如全键盘脚本和鼠标脚本?
A:它们是通过Windows API实现的。在Ruby中使用Win32API.new可以访问Windows API,然后利用它们访问键盘状态、鼠标状态,就能够从默认输入突破到更多的输入了。但是,鼠标系统还没这么简单,因为它需要和默认脚本中的窗口、地图等兼容,因此还需要给Window_Selectable等东西打补丁,最终才能形成到处好用的鼠标。
Q:RM的哪些部分是默认脚本完成的?如果都删光了,剩下的是什么?
A:以RMVA为例,脚本分下面的几块:全局模块,Game,Sprites,Windows,Scenes和main。整个游戏的入口是main,脚本驱动着游戏从开始到过程到结束的流程(而不是RGSS),包括主循环也在脚本里。Game负责各种游戏逻辑,Sprites负责一些画面上出现的东西,以及把它们打包方便管理,Windows是游戏内的各种窗口,Scenes是地图、战斗等游戏所经历过的场景。总结来说,整个RPG的系统,从逻辑到画面,都是在脚本里的。
去掉RPG“特有”的东西后,剩下的2D游戏“共有”的内容就在RGSS中了。RGSS提供了搭建一个2D游戏所需要的“零件”,比如Sprite。另外RGSS也提供了方便RPG的一些“特化零件”比如Window,或介于编辑器和游戏中间的数据对象比如RPG::Enemy和RPG::Weapon。如果默认脚本都删光了,剩下的就是一套2D游戏引擎,加上编辑器生成的对象类型,以及一些方便RPG使用的小零件。
Q:如果要在RM中写一套系统,有哪些实用技巧?
A:如果系统比较大,可以考虑换掉RM默认的场景和精灵结构。RM的场景结构是固定层数的,只有Scene、Viewport和Sprite三层。现代游戏中更加通用的是树形的场景结构,每一个节点自成一层,它的下面可以包含若干个孩子节点。修改父节点属性,孩子节点就能跟着一起动。如果画面上要处理的对象比较多,可以考虑这一方式。
另外,善用Ruby的Fiber进行流程控制也是非常方便的技巧。Fiber可以在内部随时yield,再由外部resume。如果一帧resume一次,则只要在Fiber中写5.times { Fiber.yield },就可以实现等待5帧的功能。用Fiber处理复杂逻辑相当轻快以及便于理解,在RMVA中Interpreter也已经用Fiber实现了。
Shitake 1年前
github.com/molingyu/svent
分析贴:
github.com/molngyu/blog/issues/1
发布