| 赞 | 0 |
| VIP | |
| 好人卡 | |
| 积分 | 1 |
| 经验 | |
| 最后登录 | 2017-9-15 |
| 在线时间 | 269 小时 |
- 梦石
- 0
- 星屑
- 50
- 在线时间
- 269 小时
- 注册时间
- 2014-5-9
- 回帖
- 124

|
发表于 2014-7-1 12:36:34
|
显示全部楼层
本帖最后由 泉塚四季 于 2016-1-10 12:44 编辑
[fold=$game_variables是什么东西?似乎很像是一个数组?]
实际上,RMVA中所有的事件指令都可以看作一段程序代码,这些程序代码和脚本中的代码并没有什么区别,只是一个有中文你看的更清晰一些而已。每一则指令都与脚本中的一段语句一一对应,而$game_variables对应的则是事件中的变量,也就是下面这张图中你所操作的东西。
稍微看过事件脚本教程的人应该都有看到类似这样的用法:- 变量操作:
- $game_variables[编号] = 数值
复制代码 正如前文所说,每一则事件指令都与脚本中的一段语句一一对应,上面这个截图中执行这个指令完成的工作在脚本代码中就可以表示为 $game_variables[1] = 0,那么,$game_variables究竟是怎么和事件中这条指令对应起来的呢?
为了理解的更清楚一些,我们一起来新建一个工程,在这个全新的工程的脚本编辑器中全局搜索一下所有包含$game_variables的脚本。
似乎找到了不少呢,这次我们就从截图中选中的那个脚本Game_Variables脚本看起。
这个是脚本的内容:- #encoding:utf-8
- #==============================================================================
- # ■ Game_Variables
- #------------------------------------------------------------------------------
- # 处理变量的类。本质上是套了个壳的 Array 。本类的实例请参考 $game_variables 。
- #==============================================================================
- class Game_Variables
- #--------------------------------------------------------------------------
- # ● 初始化对象
- #--------------------------------------------------------------------------
- def initialize
- @data = []
- end
- #--------------------------------------------------------------------------
- # ● 获取变量
- #--------------------------------------------------------------------------
- def [](variable_id)
- @data[variable_id] || 0
- end
- #--------------------------------------------------------------------------
- # ● 设置变量
- #--------------------------------------------------------------------------
- def []=(variable_id, value)
- @data[variable_id] = value
- on_change
- end
- #--------------------------------------------------------------------------
- # ● 设置变量时的处理
- #--------------------------------------------------------------------------
- def on_change
- $game_map.need_refresh = true
- end
- end
复制代码 并不是多复杂的脚本,让我们一句一句来分析吧。- # 处理变量的类。本质上是套了个壳的 Array 。本类的实例请参考 $game_variables 。
复制代码 首先是注释,我们只要去关注“本类的实例参考$game_variables”这一句就行了。实例是面向对象的编程中的术语,这里并不打算讨论。如果不知道这句是什么意思的话你可以粗浅的理解为$game_variables就是这个Game_Variables的电话号码,在脚本中要寻找Game_Variables时我们需要通过$game_variables这个电话号码来找他(这个说法并不准确,有条件的话还是建议你去详细了解一下实例的概念)。这个是脚本的开头部分,可以看出这个Game_Variables并没有父类。- def initialize
- @data = []
- end
复制代码 Game_Variables的载入方法,也就是执行Game_Variables.new这个脚本语句时Game_Variables所做的事情。在这个载入方法中只做了一件事,设置了一个名为@data的实例变量,并且@data是一个空的数组。- def [](variable_id)
- @data[variable_id] || 0
- end
复制代码 Game_Variables的[]方法,也就是执行$game_variables[n]时的方法,n的数值就是这个方法的variable_id参数。执行这个方法后电脑首先会检查@data[variable_id]的数值。@data前面已经说了,是一个数组。读取数组中某个元素大家想必已经在熟悉不过了吧,例如@data[0]就是读取数组的第一个元素……至于代码后半句的|| 0的作用,请看下面这段代码:- array = [1,2]
- array[0] = 1
- array[1] = 2
- array[2] = nil
- array[2] = array[2] || 0
- array[3] = array[3] || 0
复制代码 通过 || 0 这个操作,所有数组中不存在的元素将会自动替换成0。因此,我们在事件中使用变量操作时所有变量的默认值才会是0。同理,只要这么修改的话:- def [](variable_id)
- @data[variable_id] || 1
- end
复制代码 执行变量操作的时候没有用过的变量的数值就变成1了。
但是,前面已经介绍过的initialize在载入时不是将@data设置成一个空的数组了吗,不论怎么返回@data[variable_id]都是不存在的元素啊,@data这个数组是怎么被修改的呢?我们继续看下一个方法。- def []=(variable_id, value)
- @data[variable_id] = value
- on_change
- end
复制代码 这是一个[]=方法,也就是执行$game_variables[n] = value时的方法,用中文来讲的话就是给变量赋值的方法。下面以执行$game_variables[1] = 2这个语句时发生的全部事情为例详细说明一下:
[box=LemonChiffon]↓
$game_variables是Game_Variables的电话号码,进入Game_Variables中。
↓
执行的是[]=方法,寻找这个方法
↓
找到了这个方法,- def []=(variable_id, value)
- @data[variable_id] = value
- on_change
- end
复制代码 方法需要的参数是variable_id和value这两个
↓
根据执行的指令$game_variables[1] = 2,参数variable_id是1,参数value是2
↓
执行方法,将variable_id替换为1,value替换为2,代码变成了[/box]
@data是一个数组,所以上面那句指令在做的,实际上就是你再熟悉不过的数组的赋值了。
最后再说一下,执行完@data[1] = 2之后还执行了on_change这个方法,on_change是Game_Variables中的最后一个方法,内容是:- def on_change
- $game_map.need_refresh = true
- end
复制代码 这个方法的作用是判断变量的数值改变以后地图上有没有出现条件满足的事件页,如果有的话就让对应的事件切换事件页。这里就不深入讨论了。
下面重新搜索一下$game_variables,分析一下它在各个代码中的实际功能吧。
首先是module DataManager中- $game_variables = Game_Variables.new
复制代码 这个语句就是给Game_Variables设置$game_variables这个电话号码。如果将这个语句改成这样:- $game_var = Game_Variables.new
复制代码 以后在脚本中就没办法使用$game_variables[1] = 2这样的语句修改变量的数值而只能使用$game_var[1] = 2这样的语句来修改了。
我们继续搜索- contents[:variables] = $game_variables
复制代码 这个是保存存档时执行的指令,将$game_variables保存进了存档,保存进存档的自然不只有一个“电话号码”,Game_Variables里的所有数值也随着$game_variables一起保存了进去。- $game_variables = contents[:variables]
复制代码 读取存档的指令,将$game_variables这个“电话号码”替换成了存档里保存的“电话号码”,虽然名字相同,两个“电话号码”打过去找到的Game_Variables可是不同的哟。
继续搜索,在Game_Battler中找到了这样一句代码。- value = item.damage.eval(user, self, $game_variables)
复制代码
伤害的计算公式里不是允许输入v[n]来获取n号变量的数值吗,这个操作对应到脚本里,计算伤害时确实就用上了储存变量的数值的$game_variables。至于为什么获得数值用的是$game_variables而不是上面所说的$game_variables[n]这里并不详细分析。
继续搜索,在Game_Event中看到了这样一句代码- return false if $game_variables[c.variable_id] < c.variable_value
复制代码 这句代码对应事件页事件出现条件的“变量”这个出现条件
如果变量的设置和上面的截图一样的话,这句代码可以这么翻译:- (告诉电脑:“事件不出现”)如果($game_variables[2] < 2)的时候
复制代码 继续搜索,在Game_Interpreter中能够找到大量包含$game_variables的语句,和上面的分析一样:
$game_variables[n]调用了Game_Variables中的[]方法,他的作用是获取@data数组中n号位置元素的数值。
$game_variables[n]=value调用了Game_Variables中的[]=方法,他的作用是将@data数组中n号位置元素的数值修改成value。当然,[]=除了修改元素的数值外还代表了+=、-=这样的操作符,详细的可以通过Game_Interpreter中的operate_variable方法来自己体会:- def operate_variable(variable_id, operation_type, value)
- begin
- case operation_type
- when 0 # 代入
- $game_variables[variable_id] = value
- when 1 # 加法
- $game_variables[variable_id] += value
- when 2 # 减法
- $game_variables[variable_id] -= value
- when 3 # 乘法
- $game_variables[variable_id] *= value
- when 4 # 除法
- $game_variables[variable_id] /= value
- when 5 # 取余
- $game_variables[variable_id] %= value
- end
- rescue
- $game_variables[variable_id] = 0
- end
- end
复制代码 最后,$game_variables还出现在了下面这些地方,这里简单的提一下他们出现的意义:
- Window_Base:对应显示文字指令中使用\v[n]能够读取n号变量的数值。(变量的读取)
- Window_NumberInput:对应事件指令中的数值输入处理,能够修改变量的数值。(变量的修改)
- Window_KeyItem:对应事件指令中的物品选择处理,变量的数值将被修改成选择物品的ID。(变量的修改)
- Window_DebugRight:对应工程模式下按F9出现的调试窗口中显示、修改变量数值的功能。(变量的读取和修改)
[/fold]
[fold=如何从菜单中删去“整队”选项?]
“删掉整队选项”这个问题很多做解谜游戏的新人都曾经问过。这并不是多么困难的问题,一般情况下可以用这么一个标准答案来回答:
[box=LemonChiffon]打开脚本编辑器,找到Window_MenuCommand。
↓
在里面搜索到这样的代码,默认情况下在脚本的56~58行- def add_formation_command
- add_command(Vocab::formation, :formation, formation_enabled)
- end
复制代码 ↓
将这段代码改成这个样子- def add_formation_command
- #add_command(Vocab::formation, :formation, formation_enabled)
- end
复制代码 ↓
保存工程,完成。[/box]
整个操作并没有什么特别的难点,即使是新人也能很快的掌握。但是,这里我打算讲的深入一些。具体要讲的就是“为什么将这行前面加加一个#号就能删除整队指令了?”,进一步的,如果能理解我将的内容,你还能学会“如何在菜单中添加一个指令”相关的知识。
首先先从最基本的讲起,#号是注释符号,和事件指令中的注释类似,在脚本中被注释的代码是不会执行的,在脚本编辑器中这样的代码显示成绿色的。
由于我们将add_command前添加了注释的符号,所以add_command这条指令不会执行了,于是菜单中的整队指令就消失了。反过来说,由于这个add_command方法,菜单中才会出现整队这个指令。那么,这个add_command方法究竟做了什么才往菜单中添加了整队指令呢?
我们全局搜索一下add_command。
搜索后我们得到了大量的结果,大部分的add_command都只是在调用add_command这个方法而已,唯一有定义add_command的是包括菜单指令窗口、装备窗口、标题画面选项窗口在内所有包含选项的窗口的父类Window_Command。
这个方法只有一句代码,我们现在跳转过去仔细分析一下他究竟都做了什么:- #--------------------------------------------------------------------------
- # ● 添加指令
- # name : 指令名称
- # symbol : 对应的符号
- # enabled : 有效状态的标志
- # ext : 任意的扩展数据
- #--------------------------------------------------------------------------
- def add_command(name, symbol, enabled = true, ext = nil)
- @list.push({:name=>name, :symbol=>symbol, :enabled=>enabled, :ext=>ext})
- end
复制代码 @list是Window_Command的一个实例变量,他是一个数组。@list的定义在initialize方法中的第一句代码,clear_command_list方法中可以找到。
def clear_command_list
@list = []
end
push方法往@list数组中插入了一个一个元素,在这个方法中,插入数组的元素是一个由方法参数组成的哈希表,以刚才的插入整队指令为例:
现在假设@list是一个空数组[],脚本执行了下面的语句:- add_command(Vocab::formation, :formation, formation_enabled)
复制代码 现在,@list变成了这样- @list = [
- {:name=>数据库中整队指令的名称, :symbol=>:formation,
- :enabled=>判定是否允许整队代码的最终结果,:ext=>无数据}
- ,
- ]
复制代码 第一次见到数组中包含哈希表的例子,感觉有点混乱?别担心,我们紧接着再执行几个add_command指令试试看:- add_command(1,2,3,4)
- add_command(5,6,7,8)
复制代码 现在,@list变成了这样- @list = [
- {:name=>数据库中整队指令的名称, :symbol=>:formation,
- :enabled=>判定是否允许整队代码的最终结果,:ext=>无数据},
- {:name=>1, :symbol=>2, :enabled=>3,:ext=>4},
- {:name=>5, :symbol=>6, :enabled=>7,:ext=>8},
- ]
复制代码 先不用在意这样的代码是否可以执行,从上面的分析我们了解到add_command只是往名为@list的数组中添加进了一个哈希表作为元素而已。
这个@list是做什么用的?接下去让我们仔细研究一下整个Window_Command的类。- def initialize(x, y)
- clear_command_list
- make_command_list
- super(x, y, window_width, window_height)
- refresh
- select(0)
- activate
- end
复制代码 在initialize方法中和其他窗口类相区别的是在生成窗口前出现了clear_command_list和make_command_list这两个方法,clear_command_list这个方法之前已经说过了,执行后生成了@list这个空数组。make_command_list这个方法在Window_Command中并没有内容,而在其子类,比如刚刚我们的菜单Window_MenuCommand类中,专门用来执行add_command这个的方法。- def make_command_list
- add_main_commands
- add_formation_command
- add_original_commands
- add_save_command
- add_game_end_command
- end
- def add_main_commands
- add_command(Vocab::item, :item, main_commands_enabled)
- add_command(Vocab::skill, :skill, main_commands_enabled)
- add_command(Vocab::equip, :equip, main_commands_enabled)
- add_command(Vocab::status, :status, main_commands_enabled)
- end
- def add_formation_command
- add_command(Vocab::formation, :formation, formation_enabled)
- end
- def add_original_commands
- end
- def add_save_command
- add_command(Vocab::save, :save, save_enabled)
- end
- def add_game_end_command
- add_command(Vocab::game_end, :game_end)
- end
复制代码 现在我们已经发现了,在一个选项窗口添加选项的语句add_command会把所有的选项的数据推入到实例变量@list中。但是,推入之后会怎样呢?我们回到Window_Command的 initialize方法继续看下去。
super(x, y, window_width, window_height)
在生成窗口的时候,电脑调用了window_height这个方法来获取生成窗口的高度。- def window_height
- fitting_height(visible_line_number)
- end
复制代码 窗口的高度由visible_line_number(选项的行数)决定。- def visible_line_number
- item_max
- end
复制代码 visible_line_number(选项的行数)由item_max(选项的个数)决定。- def item_max
- @list.size
- end
复制代码 item_max(选项的个数)由@list究竟有多少个元素决定。
接着往下看,会发现initialize调用了一个refresh方法。- def refresh
- clear_command_list
- make_command_list
- create_contents
- super
- end
复制代码 前三句代码执行后我们又一次重新生成了一个包含全部选项数据的@list数组。当然,在initialize中执行这些代码是完全没有意义的。refresh同时也用于选项的刷新,只有当菜单中的选项突然改变了的情况下这三行代码的功能才凸显出来。
最后一个super调用了其父类Window_Selectable中的refresh方法:- def refresh
- contents.clear
- draw_all_items
- end
复制代码 contents.clear清楚了已经显示的选项的内容,接着执行了draw_all_items方法。- def draw_all_items
- item_max.times {|i| draw_item(i) }
- end
复制代码 如之前所说的item_max(选项的个数)由@list究竟有多少个元素决定。
这句代码的功能是执行选项个数次的描绘文字,描绘文字的内容又是什么呢?我们又回到了Window_Command中。- def draw_item(index)
- change_color(normal_color, command_enabled?(index))
- draw_text(item_rect_for_text(index), command_name(index), alignment)
- end
复制代码 第一行是修改文字的颜色,第二行则是描绘文字的内容。值得注意的是 command_enabled?(index)和command_name(index)这两个方法,他们分别获取了@list中对应选项的数据。- def command_name(index)
- @list[index][:name]
- end
- def command_enabled?(index)
- @list[index][:enabled]
- end
复制代码 现在一切都很清晰了,能够显示选项的窗口是通过读取@list这个数组中的元素来往窗口中增加选项的。add_command则将选项的数据传送进了@list中。窗口随时可以通过读取@list来获取这个选项的数据。
最后说明一下@list中储存的内容的意义吧:
- name:选项的名称,在描绘选项时会进行获取
- symbol:选项的符号,这是一个标志,当玩家在对应符号的选项按下确定键后电脑就会执行对应符号包含的代码
- enabled:选项是否可以选择。描绘选项文字时会进行判定,如果无法选择描绘半透明的选项。当按下按键时会进行判定,如果无法选择则提示失败。
- ext:选项的扩展数据,和symbol的功能有点类似,不同的是ext不用于判断按下选项后具体执行哪句指令。
- (在默认脚本中用在技能列表中选择“特技、魔法”的技能选择窗口中有用到,具体来说,这个指令中不论选择了特技还是魔法,接下去所做的事情都是一样的(显示技能窗口),所不同的只是窗口显示的内容而已,因此默认脚本中用相同的方式显示了窗口,在显示技能列表时再追加判断选项的扩展数据从而决定要显示的技能是特技还是魔法。)
至于为什么选择了某一个选项后能够执行对应的指令这里就不作说明了。
[/fold]
|
评分
-
查看全部评分
|