赞 | 13 |
VIP | 118 |
好人卡 | 28 |
积分 | 12 |
经验 | 35779 |
最后登录 | 2017-7-6 |
在线时间 | 1564 小时 |
Lv3.寻梦者
- 梦石
- 0
- 星屑
- 1195
- 在线时间
- 1564 小时
- 注册时间
- 2008-7-30
- 帖子
- 4418
|
加入我们,或者,欢迎回来。
您需要 登录 才可以下载或查看,没有帐号?注册会员
x
本帖最后由 DeathKing 于 2014-1-28 13:12 编辑
这篇文章因为某种原因被写得又臭又长,你如果不能够忍受这种排版,可以访问:http://ftp.66rpg.com/user/deathking/RGSS3_Exp_Doc_1_BT.pdf本文中的图片(png或vsd档)及参考代码,可以从http://ftp.66rpg.com/user/deathking/RGSS3_Exp_Attach_1.rar 取得
RGSS3小探(一)
——更智能的选择窗体
1 引言
和传统的RGSS/RGSS2系统相比,RGSS3在Window脚本组方面并没有做革命性的结构调整,继续沿袭由Window类开始继承的风格。下面给出了不太深的一些继承关系:
而仔细研究RGSS3,你会发现确实有令人眼前一亮的地方。在浏览RGSS3脚本的过程中,我们发现了set_handler和add_command等有趣的方法来处理与选择项有关的内容。在比较了这些新引入的方法与RGSS/RGSS2的异同后,我们欣喜的发现,RGSS3确实使用了一种更先进且智能的方法。我们将来探讨他们。
2 历史
让我们回过头来看看以前的RGSS系统是如何处理选择窗体的(Window_Selectable及其子类)。从以往制作者所反馈的问题来看,“如何向菜单中加入新选项,如‘用语辞典’”是一个很热门的话题。为此,我们还需要剖析一下旧版本的RGSS系统相关部分,我们利用RGSS2做示范。
菜单由Scene_Menu负责处理,而选择项部分则由其实例变量@command_window维护。生成@command_window的代码如下:
代码片段 2.1 RGSS2::Scene_Menu- #--------------------------------------------------------
- # ● 生成命令窗口
- #--------------------------------------------------------
- def create_command_window
- s1 = Vocab::item
- s2 = Vocab::skill
- s3 = Vocab::equip
- s4 = Vocab::status
- s5 = Vocab::save
- s6 = Vocab::game_end
- @command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])
- @command_window.index = @menu_index
- if $game_party.members.size == 0 # 如果队伍为空
- @command_window.draw_item(0, false) # 无效化物品选项
- @command_window.draw_item(1, false) # 无效化技能选项
- @command_window.draw_item(2, false) # 无效化装备选项
- @command_window.draw_item(3, false) # 无效化状态选项
- end
- if $game_system.save_disabled # 如果禁止存档
- @command_window.draw_item(4, false) # 无效化存档选项
- end
- end
复制代码 语句@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])告知RGSS2生成宽度为160,并且有6个选项的选择项,这六个选择项的文字即为s1~s6的值。当然,这个选择项目前没有实际意义,他只是将外壳给做好了而已。实际投入应用时,还需要做一些相应。
代码片段 2.2 RGSS2::Scene_Menu- #-----------------------------------------------------------
- # ● 更新命令窗口
- #-----------------------------------------------------------
- def update_command_selection
- if Input.trigger?(Input::B)
- Sound.play_cancel
- $scene = Scene_Map.new
- elsif Input.trigger?(Input::C)
- # 省略了部分代码
- Sound.play_decision
- case @command_window.index
- when 0 # 物品
- $scene = Scene_Item.new
- when 1,2,3 # 技能、装备、状态
- start_actor_selection
- when 4 # 存档
- $scene = Scene_File.new(true, false, false)
- when 5 # 结束游戏
- $scene = Scene_End.new
- end
- end
- end
复制代码 而Window_Selectable定义的一些类容,使得当我们按下↑(上)或↓(下)后矩形选框能够做反应,并且改变一个记录变量的值。上述update_command_selection方法的过程如左图所示,请注意,我们省略了一部分内容:
因此,当我们添加一个选项后,还需要在负责组织的相应的Scene类中添加相应的响应。令人沮丧的是,这种方法不太智能,一种可能遇到的麻烦就像下面描述的那样:
当我们添加新选项后,每个选项实际对应的index值会改变,例如,添加后“状态”的index值不再是3,因此,选择“状态”或许会弹出存档界面。并且,我们不是很能够预知这种改变。这样就给一些脚本作者带来了麻烦:他们需要为菜单添加“任务”一项,然而,他们并不知道游戏制作者之前是否也添加类似的选项,而且强行覆盖掉并不是一个很好的方法,我们就这样陷入了两难。
3 革命
RGSS3决定采用一个更智能化的方法,继承自Window_Selectable的类Window_Command在初始化时,通过调用clear_command_list方法来初始化了一个@list变量。@list变量是一个数组(Array),他按照一定的顺序存放选项。值得说明的是,这些选项是以散列表(Hash)的形式存在的。
选项的结构如下[ 来自脚本注释,Window_Command类,第55~61行 ]:
:name | 指令名称 | :symbol | 对应的符号 | :enabled | 有效状态标志 | :ext | 任意的扩展数据 | 一个合法选项的表述类似与下面的情况:- {:name => "新选项", :symbol => :new_cmd, :enabled => true, :ext => nil}
复制代码 一个可能的@list如下:- @list = [
- {:name => "物品", :symbol => :item, :enabled => true, :ext => nil},
- {:name => "特技", :symbol => :skill, :enabled => true, :ext => nil},
- {:name => "装备", :symbol => :equip, :enabled => true, :ext => nil}
- ]
复制代码 如果要添加一个选项,不推荐直接对@list变量操作,并且,它也未对外公开。取而代之的是add_command方法。想要添加上述的选项,可以这样做:- add_command("新选项", :new_cmd)
复制代码 其他的直接让系统取默认值即可。如果enable的值不为true或者ext的值不为空,你可以这样写:- add_command("新选项", :new_cmd, false, "User Added")
复制代码 我们也需要建立一个响应,但情况与RGSS2大不相同,RGSS3引入了“处理器(Handler)”的概念。调用set_handler来设置对应的选项所响应的操作。Scene_Menu中就有这么一句:@command_window.set_handler(:cancel, method(:return_scene))。
set_handler并不神秘,但你需要知道Window_Selectable在初始化的时候,也初始化了一个@handler变量用于存放对应选项的相应相应方法。当调用set_handler(:new_cmd, Proc.new{ msgbox_p "Hi" })的时候,@handler变量将会新添加一项:- @handler[:new_cmd] = Proc.new{ msgbox_p "Hi" }
复制代码 当选项:new_cmd被点击后,RGSS3将自动寻找@handler变量中是否有对应的处理方法,如果有的话就调用(Call)它。
使用@list变量的好处是可以分开地添加选项,这样就使得脚本有更大的灵活性。Window_MenuCommand中定义了make_command_list方法,他按照一定的顺序组织选项:
代码片段 3.1 RGSS3:: Window_MenuCommand- def make_command_list
- add_main_commands # 添加主选项
- add_formation_command # 添加队列选项
- add_original_commands # 添加自定义选项
- add_save_command # 添加存档选项
- add_game_end_command # 添加结束游戏选项
- end
复制代码 上面的数个方法便是分别为@list变量添加内容。而add_original_commands方法对于我们来说更有意义。我们稍后讨论。
4 机理
RGSS3采用了一个更复杂的调用关系,但可以确保绝对的抽象。我们简化了一下这个调用过程,制成了下面所示的图像。从图示可以看出,参与内容更新的都不是直接由Scene_Menu和Window_MenuCommand定义的方法,这种高度抽象有着不可言说的好处。
起点是Scene_Menu,从Scene_Base继承而来的update方法一直在被调用,因此可以不断更新。update调用了update_basic,这是希望自定义脚本不要变动一些东西。update_basic中又调用了update_all_window来刷新Scene中的窗口。此例中,调用了Window_MenuCommand的update方法来刷新选项窗体。此update方法由Window_Selectable类定义,Window_MenuCommand只是继承而来。process_cursor_move处理光标的移动,process_handling处理按键的响应,process_ok调用按下确定键的动作。
call_handler(current_symbol)即调用由set_handler建立的响应关系。
RGSS3是在按下确定键的瞬间开始检索,current_symbol指明了当前选择项的:symbol值(参考第3小节给出的参考),如果使用了set_handler为当前选项制定相应方式,那么就启用他。RGSS和RGSS2更关心选择项的顺序(Order),而不是选择项的意义。因此容易出错。
5 实战
我们分别以两个独立脚本作者的角度,制作分别制作“任务”和“世界地图”脚本,并且,要向菜单选项中添加这两个选项。首先,我们制作“任务”。考虑到“最简化我们的工作”,我们不讨论如何去制作任务系统,而是讨论这个独立系统如何融入制作者的系统。
菜单画面由Window_MenuCommand负责,因此需要覆盖一下。至此,我们遇到了先前提及的add_original_commands方法,这个是官方留好的接口(Interface),当我们添加自定义选项时,要合理使用此方法:
代码片段 5.1 RGSS3::Window_MenuCommand 自定义- class Window_MenuCommand < Window_Command
-
- alias add_original_commands_task add_original_commands
-
- def add_original_commands
- add_command("任务", :task, true,"User Added" # 我们故意设置了:ext的值来说明这是我们人为添加的
- end
-
- end
复制代码 因为我们不得不修改add_original_commands方法,而我们不希望破坏add_original_commands的内容。因此,我们使用关键字alias创建了一份add_original_commands的拷贝。并在重新定义的add_original_commands方法中调用重定义前的方法,然后才是我们新定义的内容。这种委曲求全的方式使得脚本有着很高的可扩展性。
相应的内容在Scene_Menu中:
代码片段 5.2 RGSS3::Scene_Menu 自定义- class Scene_Menu < Scene_MenuBase
-
- alias create_command_window_task create_command_window
-
- def create_command_window
- create_command_window_task
- @command_window.set_handler(:task, method(:command_task))
- end
-
- def command_task
- msgbox_p "A Interface for Task."
- #SceneManager.call(Scene_Task)
- @command_window.activate
- end
- end
复制代码 用同样的技巧处理create_command_window方法,但我们需要首先调用create_command_window_task,因为@command_window是由该方法创建的。我们追加的语句@command_window.set_handler(:task, method(:command_task))是为“任务选项”添加一个响应,当点击“任务”选项后,便激活此响应。
这个相应存放的是类似于闭包的东西,你不需要理解这些,只需要了解生成这些东西的通常做法。RGSS3的标准做法是先把这个响应定义为一个方法,如我们定义的command_task。然后在set_handler的时候使用method方法将这个方法变成一个闭包(不要忘记有冒号),虽然听起来有点绕,但却是是这样。
像上述处理后,当我们点击“任务”,则会按照command_task的定义行事。
值得注意的一点,如果最后调用的是场景切换(示范中给处理掉了),如SceneManager.call(Scene_Task),就不需要再做额外的处理,但其他情况则最好加上一个@command_window.activate来激活选项框。另外,指定的别名最好能区别与一般的别名,避免发生Stack too deep[ 这是由于不合理的递归引起的,合理的使用别名,就不会出现此类错误。]的错误(比如可以以具体的功能为后缀,如_task)。
“世界地图”功能亦可如法炮制。而我们的最后结果如下:
并且,我们编制的脚本有很好的兼容性,任意的增删任何一个脚本都不会影响其他的脚本。
6 总结
从对RGSS3脚本的分析来看,add_command和set_handler等新增的方法的确很好用,这得益于新的编程思路,并且,这些新技巧使得脚本制作者更能写出用户友好型的脚本。这里,强烈建议RGSS3脚本制作者关注以下几个方面:
- Window_MenuCommand类的add_original_commands方法
- Window_Selectable类的set_handler以及call_handler方法
- Window_Selectable类中的@list和@handler变量
- 虽然不提倡使用@index变量,但他任然可以作为处理的工具
- Scene_Menu类的create_command_window方法
RGSS3确有很多革新,这些革新是有意义的,他使得我们能够做出更成熟的系统。我也希望大家能加入到这个探索中来,让RGSS3的魅力早日展现在我们面前!
|
评分
-
查看全部评分
|