场景管理

地图画面,菜单画面这类游戏中通常称作「画面」的概念、在程序中则称为场景(Scene)。各场景的转换由 SceneManager 模块管理。

Main 脚本页

首先先来确认脚本 Main 的内容吧。扣掉注释的话 Main 的内容只有一行。

rgss_main { SceneManager.run }

实际上在内建的脚本中,与模块和类的定义无关的只有这个部分。某种程度上可以把这当成脚本真正开始执行的地方。

rgss_main是在 RGSS3 新增的内建函数。这个函数基本上只会执行 { } 括住的部分一遍,不过如果在过程中按下 F12 重置时,该函数会从头开始重新处理。如果无视重置处理,在这行就只会调用 SceneManager 模块的 run 方法。

SceneManager 模块

尽管 SceneManager 模块一直在脚本列表的最上方,但是我们到目前完全没有谈到相关的内容。SceneManager 即是管理场景切换的模块。

首先、先来看看刚刚确认刚才在一开始调用出来的 run 方法。

  def self.run
    DataManager.init
    Audio.setup_midi if use_midi?
    @scene = first_scene_class.new
    @scene.main while @scene
  end

内容只有四行,我们就一行一行看吧。

    DataManager.init

这里调用了 DataManager 模块的 init 方法数据库的读取游戏对象的初始化,就是在这里面进行的。

    Audio.setup_midi if use_midi?

数据库的[系统]标签页里面的[启动时初始化 MIDI]的选项如果打了勾,就会进行 MIDI 的初始化。

    @scene = first_scene_class.new

这句调用了同样是 SceneManager 模块的 first_scene_class 方法,生成了当做返回的类的实例,并将其代入实例变量 @scene 中。相关内容会在下一节详细解释。

    @scene.main while @scene

这里所使用的 while 是修饰符的形式。和 if 或 unless 同样,while 也有这样的用法。 如果采用基础篇学到的循环的语法再写一遍就会变成这个样子。

    while @scene
      @scene.main
    end

以内容来看,只要实例变量 @scene 不为 nil ,就会持续调用 @scene 的 main 方法。虽然看起来或许很像无限循环,但它的构造是在 main 方法的内部链接到 SceneManager 里面,@scene 所指向的对象是由外部变更,因此不成问题。

场景类

再回到第三行。

    @scene = first_scene_class.new

我们来看看这里调用的 first_scene_class 方法定义的内容吧。

  def self.first_scene_class
    $BTEST ? Scene_Battle : Scene_Title
  end
? 加上 : 这样的符号是运算符形式的分支条件。如果 $BTEST 变量的值为真则返回 Scene_Battle 为假则返回 Scene_Title 。所谓的 Scene_Battle 或 Scene_Title 是类的名称,在 Ruby 里面可以像这样把类当成方法的返回值。

全局变量 $BTEST 是由 RGSS 自动设置的变量,表示是否开启了战斗测试。也就是说如果开启了战斗测试,那么游戏的开始画面就会是战斗画面,如果没开启那就会是标题画面。

场景的基本处理

SceneManager 调用出 main 方法是在场景域的最上方的 Scene_Base 类所定义的。来确认看看吧。

  def main
    start
    post_start
    update until scene_changing?
    pre_terminate
    terminate
  end

这里依次调用了以下五个方法:

方法 内容
start 开始处理
post_start 开始后处理
update 更新帧
pre_terminate 结束前处理
terminate 结束处理

开始处理和开始后处理、结束处理和结束前处理的主要差别在于,该画面的图形是否已经实际显示出来。由于通过子类重新定义来新增一些处理比较好控制这些时间点,所以又像下方这样细分:

    update until scene_changing?

位于正中间的这行,表示的是在场景切换(画面切换))之前的这段时间,会持续调用 update 方法。在各场景中如果有需要每一帧都处理的话,可以重新定义这个 update 方法。

场景类的种类

下表为各场景类对应的画面。类的名字与脚本页的名称是一一对应的。

内容 父类
Scene_Title 标题画面 Scene_Base
Scene_Map 地图画面 Scene_Base
Scene_MenuBase 菜单画面系的基本处理 Scene_Base
Scene_Menu 菜单画面 Scene_MenuBase
Scene_ItemBase 物品画面和技能画面的共通处理 Scene_MenuBase
Scene_Item 物品画面 Scene_ItemBase
Scene_Skill 技能画面 Scene_ItemBase
Scene_Equip 装备画面 Scene_MenuBase
Scene_Status 状态画面 Scene_MenuBase
Scene_File 存档画面和读取画面的共通处理 Scene_MenuBase
Scene_Save 存档画面 Scene_File
Scene_Load 读取画面 Scene_File
Scene_End 游戏结束画面 Scene_MenuBase
Scene_Shop 商店画面 Scene_MenuBase
Scene_Name 输入名字画面 Scene_MenuBase
Scene_Debug 调试画面 Scene_MenuBase
Scene_Battle 战斗画面 Scene_Base
Scene_Gameover 游戏结束画面 Scene_Base

负责菜单画面类的基本处理的 Scene_MenuBase 类被许多别的类所继承。在这个类中,包含了把地图画面模糊化后的图像当做背景显示的处理。

场景切换

游戏中的场景切换的方法有 goto、call、return 三种。

像是游戏结束这种切换过去就不会再回原场景的画面,会使用最简单的 goto。

SceneManager.goto(Scene_Gameover)

call 则是像菜单画面这种,需要回到调用呼出菜单的原场景时使用的。

SceneManager.call(Scene_Menu)

return 则是,当我们要从用 call 调用呼出的场景回到原场景时会使用的。

SceneManager.return

实际上场景切换如何使用。在接下来的实践篇中会解说。

补充、直到 VX 之前的版本,是通过将场景对象赋值到 $scene 这个全局变量的形式进行场景切换,不过随着导入了更完整的 SceneManager 模块后,该方法就被废弃了。以前使用到现在的使用者请注意这一点。