如何优雅的研究 RGSS3 (一) 场景中窗口的工作原理
来源:互联网 发布:软件注册码怎么破 编辑:程序博客网 时间:2024/05/15 07:27
*其实我根本不会 ruby 也不会 RGSS3,以下内容都是我瞎编的*
在RGSS3中,场景是应该是游戏的基本组成单位。
一个场景就是一个包含相同逻辑的的连续完整的画面,两个场景对象的场景类相同说明它们之间的逻辑相同,拥有相似的功能。
比如标题画面啦,地图啦,物品栏啦,技能栏啦,装备栏啦,保存啦,读档啦,战斗画面啦,GameOver画面啦等等就是默认的场景。
打开脚本可以发现,所有的场景都有一个共同的父类:Scene_Base。
所以我们从这个游戏中所有 Scene 类的父类开始研究。
一打开 Scene_Base 首先看到的就是主逻辑 main 方法,main 在各种场景类中起到了入口的作用,调用一个场景就是执行它的main方法。
而场景的 main 方法是在 SceneManager 模块中的 run 方法中调用的。
#-------------------------------------------------------------------------- # ● 运行 #-------------------------------------------------------------------------- def self.run DataManager.init Audio.setup_midi if use_midi? @scene = first_scene_class.new @scene.main while @scene end可以发现,变量 @scene 是 方法 first_scene_class 返回的类的一个实例,而 first_scene_class 返回的是我们定义的玩家打开游戏后的第一个场景的类。
#-------------------------------------------------------------------------- # ● 获取最初场景的所属类 #-------------------------------------------------------------------------- def self.first_scene_class $BTEST ? Scene_Battle : Scene_Title end通常情况下是标题画面,在战斗测试的情况下是战斗场景。
获得 @scene 之后就是一个 while 循环,它不停的调用场景实例的 main 方法直到场景不再存在(即游戏结束)。
现在回到 Scene_Base 类研究 main 方法,看看 @scene 究竟在循环执行什么内容。
#-------------------------------------------------------------------------- # ● 主逻辑 #-------------------------------------------------------------------------- def main start post_start update until scene_changing? pre_terminate terminate end大意是,先执行开始处理,再执行开始后处理,然后不停更新直到场景改变,然后执行结束前处理,再执行结束处理。
可见场景中画面的改变全是依靠的 update 方法。
#-------------------------------------------------------------------------- # ● 更新画面 #-------------------------------------------------------------------------- def update update_basic end #-------------------------------------------------------------------------- # ● 更新画面(基础) #-------------------------------------------------------------------------- def update_basic Graphics.update Input.update update_all_windows end而 update 方法中只有一个 update_basic 方法的调用。
update_basic 中依次进行了图像(Graphics)、输入(Input)和窗口(windows)的更新。
#-------------------------------------------------------------------------- # ● 更新所有窗口 #-------------------------------------------------------------------------- def update_all_windows instance_variables.each do |varname| ivar = instance_variable_get(varname) ivar.update if ivar.is_a?(Window) end end所以这个更新所有窗口的方法就是本次要研究的重点了。
instance_variables、instance_variable_get、is_a?都是 ruby 元编程中的技巧。
instance_variables 能以字符串数组的形式返回对象的实例变量名。
instance_variable_get(varname) 取得并返回对象的实例变量的值,用变量 varname 来指定实例变量名。
ivar.is_a?(Window) 判断变量 ivar 是不是类 Windows 的实例。
如果是 ivar 是窗口类的实例,那么就调用 ivar 中的 update 方法,即更新这个窗口。
因此 update_all_windows 方法实际上就是将场景实例中的所有实例变量遍历一边,如果是窗口实例的话就更新它。
接下来看游戏中所有窗口的父类 Window_Base 是怎样更新的。
class Window_Base < Window显然 Window_Base 是 Window 的子类。
#-------------------------------------------------------------------------- # ● 更新画面 #-------------------------------------------------------------------------- def update super update_tone update_open if @opening update_close if @closing end首先调用了父类 Window 的更新方法。
然后更新色调,如果处于正在打开窗口的过程中则更新打开处理,如果处于正在关闭窗口的过程则更新关闭处理。
可见窗口的开闭动画是在 Window_Base 中处理的。
现在回到场景类看窗口是怎样和场景关联起来的。
由于 Scene_Base 中没有窗口,所以现在以 Scene_Title 做例子。
#-------------------------------------------------------------------------- # ● 开始处理 #-------------------------------------------------------------------------- def start super SceneManager.clear Graphics.freeze create_background create_foreground create_command_window play_title_music endScene_Title 中的开始处理方法多了一些内容,不过不要在意这些细节,直接看 create_command_window 方法。
#-------------------------------------------------------------------------- # ● 生成指令窗口 #-------------------------------------------------------------------------- def create_command_window @command_window = Window_TitleCommand.new @command_window.set_handler(:new_game, method(:command_new_game)) @command_window.set_handler(:continue, method(:command_continue)) @command_window.set_handler(:shutdown, method(:command_shutdown)) end所以在这个方法里新建了一个窗口类 Window_TitleCommand 的实例,这是标题画面中,选择“开始游戏/继续游戏”的窗口。
这样标题画面场景就与标题选项窗口关联了起来,在场景刷新的时候也会刷新窗口。
说起来 Window_TitleCommand 还是 Window_Command 的子类,Window_Command 是带有指令选择的窗口的父类。
Window_Command 又是 Window_Selectable 的子类,Window_Selectable 是拥有光标移动、滚动功能的窗口的父类。
set_handler 是 Window_Selectable 中定义的方法,command_new_game、command_continue、command_shutdown 则是 Scene_Title 中的方法。
#-------------------------------------------------------------------------- # ● 设置动作对应的处理方法 # method : 设置的处理方法 (Method 实例) #-------------------------------------------------------------------------- def set_handler(symbol, method) @handler[symbol] = method end正如注释所说,当窗口实例执行 symbol 对应的操作时,就调用场景中对应的 method 的方法,这是闭包的知识,它将方法作为了变量。
总之不要在意这些细节,现在看 Window_Command 好了。
class Window_Command < Window_Selectable #-------------------------------------------------------------------------- # ● 初始化对象 #-------------------------------------------------------------------------- def initialize(x, y) clear_command_list make_command_list super(x, y, window_width, window_height) refresh select(0) activate end它在初始化的时候调用了 make_command_list 方法。
#-------------------------------------------------------------------------- # ● 生成指令列表 #-------------------------------------------------------------------------- def make_command_list end #-------------------------------------------------------------------------- # ● 添加指令 # name : 指令名称 # symbol : 对应的符号 # enabled : 有效状态的标志 # ext : 任意的扩展数据 #-------------------------------------------------------------------------- def add_command(name, symbol, enabled = true, ext = nil) @list.push({:name=>name, :symbol=>symbol, :enabled=>enabled, :ext=>ext}) end所有 Window_Command 的子类可以通过调用 add_command 添加选项到选项列表 @list 中。
@list 接受四个参数,后两个有默认值,所以前两个才是重点。
name 是指令名称,symbol 是对应的符号。
回到 Window_TitleCommand 看它重写的 make_command_list 方法。
#-------------------------------------------------------------------------- # ● 生成指令列表 #-------------------------------------------------------------------------- def make_command_list add_command(Vocab::new_game, :new_game) add_command(Vocab::continue, :continue, continue_enabled) add_command(Vocab::shutdown, :shutdown) end它添加了三个选项:新游戏、继续游戏、关闭游戏对应的 symbol。
我已经有点乱了...
捋一捋继承关系
Window_Base
Window_Selectable
Window_Command
Window_TitleCommand
以下内容可以跳过
-------------------------------------------------------------------------------------------------
一些跟 symbol 有关的内容。
当一个窗口中的选项被选中时。
调用 Window_Command 中的 select_symbol(symbol) 方法,用方法 select(i) 选中 symbol 所在的选项。
这里的 symbol 就是之前在 make_command_list(生成指令列表)方法中调用 add_command(添加指令)添加的 symbol 。
#-------------------------------------------------------------------------- # ● 将光标移动到指定的标志符对应的选项 #-------------------------------------------------------------------------- def select_symbol(symbol) @list.each_index {|i| select(i) if @list[i][:symbol] == symbol } end
select(i) 是 Window_Selectable 中定义的方法。
#-------------------------------------------------------------------------- # ● 选择项目 #-------------------------------------------------------------------------- def select(index) self.index = index if index end它指定变量 index 指向选择的选项编号。
在 Window_Selectable 中调用 call_handler 来可以执行之前用 set_handler 方法注册的方法。
#-------------------------------------------------------------------------- # ● 调用处理方法 #-------------------------------------------------------------------------- def call_handler(symbol) @handler[symbol].call if handle?(symbol) end
---------------------------------------------------------------------------------------------------
#¥#@%@#¥#@%@#
总之就是在 Window_TitleCommand 用 make_command_list 注册了一些 symbol。
在 Scene_Title 中用 create_command_window 调用 Window_TitleCommand 的实例 @command_window 中的 set_handler 方法将窗口中的 symbol 与场景中的方法关联起来。这样当窗口中的选项被选中时就会执行场景中定义的方法。
于是这就是场景中窗口的工作原理。
- 如何优雅的研究 RGSS3 (一) 场景中窗口的工作原理
- 如何优雅的研究 RGSS3 (三) 调整窗口的细节
- 如何优雅的研究 RGSS3 番外(二) 显示文字信息的窗口中的纤程
- 如何优雅的研究 RGSS3 (四) 使窗口从画面边缘弹出
- 如何优雅的研究 RGSS3 (七) 添加LOGO画面
- 如何优雅的研究 RGSS3 番外(一) ruby 实现的后缀自动机
- 如何优雅的研究 RGSS3 (二) 为游戏结束画面添加简单的选项
- 如何优雅的研究 RGSS3 (五) 输入数字的画面
- 如何优雅的研究 RGSS3 (六) 技能与物品画面剖析
- 如何优雅的入门LISP(一)
- 如何优雅的生活 --工作后
- 代理服务器工作原理的研究
- 代理服务器工作原理的研究
- 代理服务器工作原理的研究
- 代理服务器工作原理的研究
- 代理服务器工作原理的研究
- 代理服务器工作原理的研究
- Structs深入研究(一)-----Struts framework的工作原理和组件
- Codeforces Round #FF 446 C. DZY Loves Fibonacci Numbers
- Android中Activity之间的数据传递(Intent和Bundle)
- C++学习笔记(达内视频版)
- jQuery表单插件jquery.form.js用法详解
- c语言练习 8-2. 求矩阵的局部极大值
- 如何优雅的研究 RGSS3 (一) 场景中窗口的工作原理
- c语言练习 8-3. 组个最小数
- poj1087 A Plug for UNIX 二分匹配+floyd
- spark关键PR
- 博弈--ZOJ 3084 S-Nim(SG)
- 递归转化为非递归的一般方法
- C++ STL 学习体会
- 如何学习运营,如何思考运营
- C# 首个程序