怎么优雅的研究 RGSS3 (二) 为游戏结束画面添加简单的选项

如何优雅的研究 RGSS3 (二) 为游戏结束画面添加简单的选项

上一期研究了场景中显示窗口的基本原理,本期就动手亲自写一写简单的窗口。

向 Gameover 场景里添加自制的窗口是个不错的选择, 因为 Scene_Gameover 是少数不含窗口的场景之一。

在 RGSS3 的默认脚本中,游戏因为某种原因(战斗失败、HP变为0、强制游戏结束)结束后,会直接跳到 Gameover 场景。

而在 Gameover 场景中按下空格键就会回到游戏标题画面。

这不科学嘛,虽然在标题画面中也可以读取游戏进度,但是显然在游戏结束画面直接读取进度更为方便。

所以我们现在就为游戏结束画面添加一个窗口,给出三个选项:回到标题画面、载入进度、关闭游戏。

虽然在上一期中已经了解了窗口类的工作原理,但是具体细节仍然不太清晰,突然要我们自己写一个还是不知道该从何入手。

#encoding:utf-8
#==============================================================================
# ■ Window_OverLoad
#------------------------------------------------------------------------------
#  游戏结束后可以读档的窗口。
#==============================================================================

class Window_OverLoad < Window_Command
先把注释写好肯定错不了,我们要写的结束后读档的窗口显然是 Window_Command 的子类。

我们的窗口类有三个选项而且还能读档,这和标题画面中选择“开始游戏/继续游戏”的窗口的功能非常接近。

所以直接把 Window_TitleCommand 的代码复制一份粘贴到 Window_OverLoad 好了。不会有问题的!

但是在标题画面的窗口中,第一个选项是开始新游戏,在 Window_OverLoad 中应该改成回到标题画面。

于是我们把生成指令列表的方法 make_command_list 中的第一句话 add_command(Vocab::new_game, :new_game) 改为 add_command(Vocab::to_title, :to_title) 。

new_game 与 to_title 都是在 Vocab 模块中定义的一些表示游戏用语的字符串常量。

  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize
    super(0, 0)
    update_placement
    select_symbol(:continue) if continue_enabled
    self.openness = 0
    open
  end
  #--------------------------------------------------------------------------
  # ● 获取窗口的宽度
  #--------------------------------------------------------------------------
  def window_width
    return 160
  end
  #--------------------------------------------------------------------------
  # ● 更新窗口的位置
  #--------------------------------------------------------------------------
  def update_placement
    self.x = (Graphics.width - width) / 2
    self.y = (Graphics.height * 1.6 - height) / 2
  end
  #--------------------------------------------------------------------------
  # ● 生成指令列表
  #--------------------------------------------------------------------------
  def make_command_list
    add_command(Vocab::to_title, :to_title)
    add_command(Vocab::continue, :continue, continue_enabled)
    add_command(Vocab::shutdown, :shutdown)
  end
  #--------------------------------------------------------------------------
  # ● 获取“继续游戏”选项是否有效
  #--------------------------------------------------------------------------
  def continue_enabled
    DataManager.save_file_exists?
  end
end
在窗口初始化时先计算了窗口所在的位置,将窗口设定在游戏画面*偏下的位置。

接下来判断了一下是否有存档,如果没有存档记录的话就禁用“继续游戏”选项。

self.openness = 0 将窗口设置为了没打开状态,然后调用 open 展示了一个窗口打开动画。


接下来向 Scene_Gameover 类中添加 create_command_window 方法的定义,并在 start 方法的最后调用它。

  #--------------------------------------------------------------------------
  # ● 生成指令窗口
  #--------------------------------------------------------------------------
  def create_command_window
    @command_window = Window_OverLoad.new
    @command_window.set_handler(:to_title, method(:command_to_title))
    @command_window.set_handler(:continue, method(:command_continue))
    @command_window.set_handler(:shutdown, method(:command_shutdown))
  end
在里将游戏结束场景中的三个方法 command_to_title、command_continue、command_shutdown 与 Window_OverLoad 的实例 @command_window 关联在了一起。

虽然现在我们还没有添加这三个方法的具体内容。

  def command_to_title
  end
  
  def command_continue
  end
  
  def command_shutdown
  end

现在进入游戏的结束画面,发现多了一个跟游戏标题画面里的窗口很像的窗口,里面有三个选项。

怎么优雅的研究 RGSS3 (二) 为游戏结束画面添加简单的选项

现在不管在什么选项上按下确定键都只会回到标题画面。

为什么会这样呢?

  #--------------------------------------------------------------------------
  # ● 更新画面
  #--------------------------------------------------------------------------
  def update
    super
    goto_title if Input.trigger?(:C)
  end
发现 Scene_Gameover 的 update 方法中有这样一句话 goto_title if Input.trigger?(:C) 。

goto_title 也是 Scene_Gameover 中的方法。调用它切换到标题画面。

这样一句话就是说在更新场景时,如果侦测到按下了确定键,就调用 goto_title 回到标题画面。

  #--------------------------------------------------------------------------
  # ● 切换到标题画面
  #--------------------------------------------------------------------------
  def goto_title
    fadeout_all
    SceneManager.goto(Scene_Title)
  end
SceneManager 是场景切换的管理器,调用其 goto 方法可以直接切换某个场景(无过渡)。

现在我们将其删去。然后向 command_to_title 方法中添加 goto_title 的调用。

类似的,向 command_continue 方法中添加一句 SceneManager.goto(Scene_Load) ,向 command_shutdown 方法中添加一句 SceneManager.exit。

  def command_to_title
    goto_title
  end
  
  def command_continue
    SceneManager.goto(Scene_Load)
  end
  
  def command_shutdown
    SceneManager.exit
  end
这下三个选项的基本功能就做好了,不过还是有一些瑕疵在里面。

当我们选择继续游戏选项后,进入载入游戏画面,然后我们按下取消键,游戏就自己关掉了- -

这是因为我们是用 goto 方法进入的新场景,SceneManager 切换场景的方法有两个:

  #--------------------------------------------------------------------------
  # ● 直接切换某个场景(无过渡)
  #--------------------------------------------------------------------------
  def self.goto(scene_class)
    @scene = scene_class.new
  end
  #--------------------------------------------------------------------------
  # ● 切换
  #--------------------------------------------------------------------------
  def self.call(scene_class)
    @stack.push(@scene)
    @scene = scene_class.new
  end
goto 方法直接切换场景,而 call 方法将现在的场景压入栈中,然后再切换场景,这是为回到上一个场景的 return 方法做准备。

  #--------------------------------------------------------------------------
  # ● 返回到上一个场景
  #--------------------------------------------------------------------------
  def self.return
    @scene = @stack.pop
  end
当在载入游戏的场景中按下取消键时,Scene_Load 的父类 Scene_File 更新画面时的 update_savefile_selection 方法检测到了取消键,于是执行 on_savefile_cancel 方法:

  #--------------------------------------------------------------------------
  # ● 更新画面
  #--------------------------------------------------------------------------
  def update
    super
    @savefile_windows.each {|window| window.update }
    update_savefile_selection
  end

  #--------------------------------------------------------------------------
  # ● 更新存档文件选择
  #--------------------------------------------------------------------------
  def update_savefile_selection
    return on_savefile_ok     if Input.trigger?(:C)
    return on_savefile_cancel if Input.trigger?(:B)
    update_cursor
  end

  #--------------------------------------------------------------------------
  # ● 存档文件“取消”
  #--------------------------------------------------------------------------
  def on_savefile_cancel
    Sound.play_cancel
    return_scene
  end

return_scene 是游戏中所有 Scene 类(场景类)的父类 Scene_Base 中的方法,它调用了 SceneManager 的 return 方法。

  #--------------------------------------------------------------------------
  # ● 返回前一个场景
  #--------------------------------------------------------------------------
  def return_scene
    SceneManager.return
  end
现在我们知道问题出在哪了,将 command_continue 中的 SceneManager.goto(Scene_Load) 改为 SceneManager.call(Scene_Load)。

这样在载入游戏场景中按下取消键就会返回游戏结束场景。

嗯,这样就好多了,可是还是有些别扭,选中选项时总是感觉有些僵硬。

与标题画面做了对比后发现,我们的窗口少了关闭时的动画,难怪感觉有些不对。

  #--------------------------------------------------------------------------
  # ● 关闭指令窗口
  #--------------------------------------------------------------------------
  def close_command_window
    @command_window.close
    update until @command_window.close?
  end
没错,正是 Scene_Title 中的 close_command_window 方法使标题画面中的窗口有了关闭动画。

在 Scene_Title 中的每个选项方法,在执行其功能之前,都会先调用 close_command_window 显示一个关闭窗口的动画。

我们也将 close_command_window 方法的定义和调用添加到 Scene_Gameover 中。

  def command_to_title
    close_command_window
    goto_title
  end
  
  def command_continue
    close_command_window
    SceneManager.call(Scene_Load)
  end
  
  def command_shutdown
    close_command_window
    SceneManager.exit
  end
这样我们的窗口也变得酷炫了起来。

虽然很想说这样就完美了,不过呢,还是漏了一点东西。

在 Scene_Base 中有一个 fadeout_all 方法,淡出各种音效以及图像。

  #--------------------------------------------------------------------------
  # ● 淡出各种音效以及图像
  #--------------------------------------------------------------------------
  def fadeout_all(time = 1000)
    RPG::BGM.fade(time)
    RPG::BGS.fade(time)
    RPG::ME.fade(time)
    Graphics.fadeout(time * Graphics.frame_rate / 1000)
    RPG::BGM.stop
    RPG::BGS.stop
    RPG::ME.stop
  end
调用这个方法可以让当前的所有声音与画面在1秒内渐渐消失。

在离开 Scene_Gameover 场景的时候调用一下这个方法可以让场景的切换不那么突兀。

  def command_to_title
    close_command_window
    fadeout_all
    goto_title
  end
  
  def command_continue
    close_command_window
    fadeout_all
    SceneManager.call(Scene_Load)
  end
  
  def command_shutdown
    close_command_window
    fadeout_all
    SceneManager.exit
  end

这样我们的窗口终于完美啦,赶紧 Gameover 体验一下吧。