设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
查看: 7716232|回复: 21
打印 上一主题 下一主题

[RMVA发布] RGSS3小探——更智能的选择窗体

[复制链接]

Lv3.寻梦者

梦石
0
星屑
1045
在线时间
1564 小时
注册时间
2008-7-30
帖子
4418

贵宾

跳转到指定楼层
1
发表于 2012-1-20 20:07:48 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有帐号?注册会员

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
  1.   #--------------------------------------------------------
  2.   # ● 生成命令窗口
  3.   #--------------------------------------------------------
  4.   def create_command_window
  5.     s1 = Vocab::item
  6.     s2 = Vocab::skill
  7.     s3 = Vocab::equip
  8.     s4 = Vocab::status
  9.     s5 = Vocab::save
  10.     s6 = Vocab::game_end
  11.     @command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])
  12.     @command_window.index = @menu_index
  13.     if $game_party.members.size == 0          # 如果队伍为空
  14.       @command_window.draw_item(0, false)     # 无效化物品选项
  15.       @command_window.draw_item(1, false)     # 无效化技能选项
  16.       @command_window.draw_item(2, false)     # 无效化装备选项
  17.       @command_window.draw_item(3, false)     # 无效化状态选项
  18.     end
  19.     if $game_system.save_disabled             # 如果禁止存档
  20.       @command_window.draw_item(4, false)     # 无效化存档选项
  21.     end
  22.   end
复制代码
语句@command_window = Window_Command.new(160, [s1, s2, s3, s4, s5, s6])告知RGSS2生成宽度为160,并且有6个选项的选择项,这六个选择项的文字即为s1~s6的值。当然,这个选择项目前没有实际意义,他只是将外壳给做好了而已。实际投入应用时,还需要做一些相应。

代码片段 2.2 RGSS2::Scene_Menu
  1.   #-----------------------------------------------------------
  2.   # ● 更新命令窗口
  3.   #-----------------------------------------------------------
  4.   def update_command_selection
  5.     if Input.trigger?(Input::B)
  6.       Sound.play_cancel
  7.       $scene = Scene_Map.new
  8.     elsif Input.trigger?(Input::C)
  9.       # 省略了部分代码
  10.       Sound.play_decision
  11.       case @command_window.index
  12.       when 0      # 物品
  13.         $scene = Scene_Item.new
  14.       when 1,2,3  # 技能、装备、状态
  15.         start_actor_selection
  16.       when 4      # 存档
  17.         $scene = Scene_File.new(true, false, false)
  18.       when 5      # 结束游戏
  19.         $scene = Scene_End.new
  20.       end
  21.     end
  22.   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任意的扩展数据
    一个合法选项的表述类似与下面的情况:
  1. {:name => "新选项", :symbol => :new_cmd, :enabled => true, :ext => nil}
复制代码
一个可能的@list如下:
  1. @list = [
  2. {:name => "物品", :symbol => :item, :enabled => true, :ext => nil},
  3. {:name => "特技", :symbol => :skill, :enabled => true, :ext => nil},
  4. {:name => "装备", :symbol => :equip, :enabled => true, :ext => nil}
  5.         ]
复制代码
如果要添加一个选项,不推荐直接对@list变量操作,并且,它也未对外公开。取而代之的是add_command方法。想要添加上述的选项,可以这样做:
  1. add_command("新选项", :new_cmd)
复制代码
其他的直接让系统取默认值即可。如果enable的值不为true或者ext的值不为空,你可以这样写:
  1. 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变量将会新添加一项:
  1. @handler[:new_cmd] = Proc.new{ msgbox_p "Hi" }
复制代码
当选项:new_cmd被点击后,RGSS3将自动寻找@handler变量中是否有对应的处理方法,如果有的话就调用(Call)它。

  使用@list变量的好处是可以分开地添加选项,这样就使得脚本有更大的灵活性。Window_MenuCommand中定义了make_command_list方法,他按照一定的顺序组织选项:

代码片段 3.1 RGSS3:: Window_MenuCommand
  1.   def make_command_list
  2.     add_main_commands                # 添加主选项
  3.     add_formation_command        # 添加队列选项
  4.     add_original_commands        # 添加自定义选项
  5.     add_save_command                        # 添加存档选项
  6.     add_game_end_command        # 添加结束游戏选项
  7.   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 自定义
  1. class Window_MenuCommand < Window_Command
  2.   
  3.   alias add_original_commands_task add_original_commands
  4.   
  5.   def add_original_commands
  6.     add_command("任务", :task, true,"User Added" # 我们故意设置了:ext的值来说明这是我们人为添加的
  7.   end
  8.   
  9. end
复制代码
因为我们不得不修改add_original_commands方法,而我们不希望破坏add_original_commands的内容。因此,我们使用关键字alias创建了一份add_original_commands的拷贝。并在重新定义的add_original_commands方法中调用重定义前的方法,然后才是我们新定义的内容。这种委曲求全的方式使得脚本有着很高的可扩展性。



  相应的内容在Scene_Menu中:

代码片段 5.2 RGSS3::Scene_Menu 自定义
  1. class Scene_Menu < Scene_MenuBase
  2.   
  3.   alias create_command_window_task create_command_window
  4.   
  5.   def create_command_window
  6.     create_command_window_task
  7.     @command_window.set_handler(:task, method(:command_task))
  8.   end
  9.   
  10.   def command_task
  11.     msgbox_p "A Interface for Task."
  12.     #SceneManager.call(Scene_Task)
  13.     @command_window.activate
  14.   end
  15. 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的魅力早日展现在我们面前!


点评

膜拜了  发表于 2013-6-12 18:51
An Interface  发表于 2012-1-22 22:18

评分

参与人数 9星屑 +6750 梦石 +2 +13 收起 理由
miantouchi + 1 精品文章
1091160905 + 24 凑整
wangswz + 60 确实更智能了
仲秋启明 + 4 精品文章
R-零 + 2 = =
退屈£无聊 + 4 加分需要解释吗
忧雪の伤 + 4
各种压力的猫君 + 3332 精品文章
fux2 + 3332 + 2 精品文章

查看全部评分


See FScript Here:https://github.com/DeathKing/fscript
潜心编写URG3中。
所有对URG3的疑问和勘误或者建议,请移步至发布页面。
欢迎萌妹纸催更
头像被屏蔽

Lv1.梦旅人

梦石
0
星屑
49
在线时间
88 小时
注册时间
2011-12-17
帖子
281
2
发表于 2012-1-20 20:30:23 | 只看该作者
又臭又长么?没觉得。。。
不过我倒没觉得这方法有什么方便(虽然兼容性明显提升)。
不过由于这是RGSS3中唯一的添加新菜单项的方法,还是顶一下。
签名被屏蔽
回复 支持 反对

使用道具 举报

Lv4.逐梦者

醉啸 长风万里

梦石
0
星屑
6052
在线时间
6586 小时
注册时间
2007-12-16
帖子
4501

贵宾

3
发表于 2012-1-20 20:34:57 | 只看该作者
本帖最后由 仲秋启明 于 2012-1-20 22:57 编辑

虽然刚开始是很蛋疼,但是慢慢习惯吧

PS:如果在同一个Scene里使用同一个Window的话如
  1.     @1_window = Window_Command.new
  2.     @1_window.set_handler(:1,      method(:command_1))
  3.     @1_window.set_handler(:2,      method(:command_2))
  4.     @2_window = Window_Command.new
  5.     @2_window.set_handler(:3,      method(:command_3))
  6.     @2_window.set_handler(:4,      method(:command_4))
复制代码
就会出现两个窗口全都出现四个选项且第一个窗口只有前两个有效,第二个窗口只有后两个有效的问题

还在龟速填坑中
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1045
在线时间
1564 小时
注册时间
2008-7-30
帖子
4418

贵宾

4
 楼主| 发表于 2012-1-20 21:00:59 | 只看该作者
阿尔西斯的马甲 发表于 2012-1-20 20:30
又臭又长么?没觉得。。。
不过我倒没觉得这方法有什么方便(虽然兼容性明显提升)。
不过由于这是RGSS3中 ...

个人觉得尤其实在插入新的选项这方面,这种做法尤为有效。

@仲秋启明手头没有ACE,无法测试。不应该吧……

点评

这个我已经试验过了  发表于 2012-1-20 21:41
RGSS3中我只用4行(可以更少)的代码就完成了跳过标题。另外,也不算教程,我最近拿到的ACE,只是发布一些个人研究。  发表于 2012-1-20 21:21
“三Manager的话。。。其实就是整合和规范”确实是这样。DataManager就把以前VX和XP的Scene_Title进行的数据处理给揽了过来——这更加合理一些。  发表于 2012-1-20 21:20
MVC是RGSS的核心思想,大量的继承关系只是为了高度抽象,减少代码量和高度的可扩展性。Scene本来就是用于组织Window的,XP亦是如此。  发表于 2012-1-20 21:16
三Manager的话。。。其实就是整合和规范。。。算了,我没仔细研究过这个,还是虚心听讲等教程吧  发表于 2012-1-20 21:15

See FScript Here:https://github.com/DeathKing/fscript
潜心编写URG3中。
所有对URG3的疑问和勘误或者建议,请移步至发布页面。
欢迎萌妹纸催更
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
12 小时
注册时间
2011-7-8
帖子
16
5
发表于 2012-1-20 21:53:27 | 只看该作者
不懂啊~~~·

点评

我也不懂阿  发表于 2014-3-18 16:18
回复 支持 反对

使用道具 举报

Lv1.梦旅人

Mr.Gandum

梦石
0
星屑
226
在线时间
2070 小时
注册时间
2007-1-31
帖子
3039

贵宾

6
发表于 2012-1-20 23:05:03 | 只看该作者
学习了。第一次看Ace脚本的时候真的懵了。
回复 支持 反对

使用道具 举报

Lv2.观梦者

傻♂逼

梦石
0
星屑
369
在线时间
1605 小时
注册时间
2007-3-13
帖子
6562

烫烫烫开拓者

7
发表于 2012-1-20 23:17:08 | 只看该作者
RGSS3把整个代码梳理了一遍,提供了底层=>应用层的表达,把核心代码抽象化,简单的说就是更像Java写出来的,就是这样。事实上Ruby在抽象化的表达方面没有优势……尽管Ruby的元编程确实被很多人推崇,但是这货说是在的……完全无力吐槽啊混蛋!简单的说就是
RGSS=〉普通程序猿
RGSS2 =〉文艺程序猿
RGSS3=〉2B程序猿
……

点评

*指的是在RM里使用MVC,应为他只是在抽象上实现了MVC,比如Bitmap还是存储了图像的2进制数据,依然无法实现游戏的冷保存。  发表于 2012-1-21 02:39
MVC意义不明,但是如果要便于修改明显导出SDK是最好的选择  发表于 2012-1-21 02:38
虽然看起来更简洁明了,其实是更晦涩了  发表于 2012-1-20 23:17
哎呀,蛋疼什么的最有爱了
回复 支持 反对

使用道具 举报

Lv3.寻梦者

梦石
0
星屑
1045
在线时间
1564 小时
注册时间
2008-7-30
帖子
4418

贵宾

8
 楼主| 发表于 2012-1-21 10:38:36 手机端发表。 | 只看该作者
Shy07 发表于 2012-1-20 22:05
RGSS强调的是Ruby的好用,即使代码不够严谨也能完成理想中的效果,这是Ruby追求的对程序员友好
RGSS2强调的 ...

我觉得RGSS3一定程度上沿袭了RGSS2的特点,更加强调代码的复用,就是感觉Window类的规划不太合理,继承有点复杂。昨天花了2个小时用Visio做了一个继承关系图,多久发布上来。

See FScript Here:https://github.com/DeathKing/fscript
潜心编写URG3中。
所有对URG3的疑问和勘误或者建议,请移步至发布页面。
欢迎萌妹纸催更
回复 支持 反对

使用道具 举报

Lv2.观梦者

(?????)

梦石
0
星屑
700
在线时间
1327 小时
注册时间
2011-7-18
帖子
3184

贵宾

9
发表于 2012-1-22 00:19:14 | 只看该作者
第一次看Ace的新结构真心晕了,适应了之后感觉的确较VX优秀。
没想到能有人进行系统的讲解,前辈的帖子总能让人受益匪浅,佩服佩服啊。
回复 支持 反对

使用道具 举报

Lv1.梦旅人

梦石
0
星屑
50
在线时间
246 小时
注册时间
2011-12-11
帖子
260
10
发表于 2012-1-22 08:25:03 | 只看该作者
本帖最后由 琪露诺 于 2012-1-22 08:26 编辑

很喜欢这个结构啦~
觉得直接用method(:command)做参数比when的那个反而更直接?
(据说写RGSS3的程序员换了?)
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-4-24 23:50

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表