上一期研究了场景中显示窗口的基本原理,本期就动手亲自写一写简单的窗口。
向 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
现在进入游戏的结束画面,发现多了一个跟游戏标题画面里的窗口很像的窗口,里面有三个选项。
现在不管在什么选项上按下确定键都只会回到标题画面。
为什么会这样呢?
#--------------------------------------------------------------------------
# ● 更新画面
#--------------------------------------------------------------------------
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 体验一下吧。