Cache 是用来快速读取图像文件的模块。
在基础篇显示图片的时候,我们把 Bitmap 类像下面一样使用过:
skeleton.bitmap = Bitmap.new("Graphics/Battlers/Skeleton")
这样,在生成 Bitmap 类的实例时、指定该图片的文件名作为参数,这样、就可以以此来读取图片了。
不过、每次需要图片的时候都需要读一次文件,这样运行效率就会变差。 因此RM提供了将生成过一次的位图 Bitmap 对象保存下来的机制,而这指的就是 Cache 模块。
Cache(缓存)原本是有「仓库」意思的单词,不过在电脑的世界里,它已经被固定为「用来放置经常会使用到的数据的地方」的意思。本模块的命名也是仿照这一点。
比起 Cache 模块的內部到底是如何定义的,更重要的是这些东西该如何使用。
实际上它的用法如下(注意:由于 Cache 模块还没有定义到,所以不能在前面的 TEST 脚本页使用)。
skeleton.bitmap = Cache.battler("Skeleton", 0)
这段脚本的意思是从缓存中获取敌方角色里 "Skeleton" 位图。所取得的位图对象即是返回值。就跟直接调用 Bitmap.new 时一样,我们即使没有指定"Graphics/Battlers/" 这个文件夹路径也是可以的。
第二个参数「0」表示的是色调的变化。值的范围为 0~360,只有「动画」和「敌人」这两种素材才可以指定。
各种素材的文件夹对应的方法如下。
方法名 | 参数 | 文件夹 |
---|---|---|
Cache.animation | 文件名、色调 | Graphics/Animations/ |
Cache.battleback1 | 文件名 | Graphics/Battlebacks1/ |
Cache.battleback2 | 文件名 | Graphics/Battlebacks2/ |
Cache.battler | 文件名、色调 | Graphics/Battlers/ |
Cache.character | 文件名 | Graphics/Characters/ |
Cache.face | 文件名 | Graphics/Faces/ |
Cache.parallax | 文件名 | Graphics/Parallaxes/ |
Cache.picture | 文件名 | Graphics/Pictures/ |
Cache.system | 文件名 | Graphics/System/ |
Cache.tileset | 文件名 | Graphics/Tilesets/ |
Cache.title1 | 文件名 | Graphics/Titles1/ |
Cache.title2 | 文件名 | Graphics/Titles2/ |
此外、与 Sound 模块相同、这个模块设计成即使指定空的文件名也不会出错、遇到这种情况的时候,会生成一个 32×32 大小的空白位图返回。之所以大小不是 1×1 是为了配合 RGSS 内部实际操作上的方便性。这种做法比较不会让效率变低。
为了能够更加了解有关缓存的内容,让我们看看他是如何实现的吧。首先从 Cache.battler 方法开始。
def self.battler(filename, hue) load_bitmap("Graphics/Battlers/", filename, hue) end
很简单对吧。就只是将敌人文件夹名一起指定为参数,然后调用 load_bitmap 这另一个方法而已。好了,接着看看所调用的 load_bitmap 方法。
def self.load_bitmap(folder_name, filename, hue = 0) @cache ||= {} if filename.empty? empty_bitmap elsif hue == 0 normal_bitmap(folder_name + filename) else hue_changed_bitmap(folder_name + filename, hue) end end
这一段可能比较难理解。让我们分割得细一点来解释吧。
def self.load_bitmap(folder_name, filename, hue = 0)
第一行是模块方法的定义,把文件夹名、文件、色调当做参数取用的定义方式。指定给色调 (hue) 的「0」是默认参数。 它和函数基本上是类似的。
解释第二行就有点麻烦了。
@cache ||= {}
这就是 空值保护(nil_guard),是 Ruby 特有的惯用语法。||= 这个东西,是把 「OR 运算」和「自赋值」两者结合在一起的产物。如果两者都不省略的话就会变得像下面这样:
@cache = @cache || {}
不过这样说还是很难明白吧。|| 运算,在分支条件中有提过,主要是用来表示「或者」这个意思,这就是实际使用时的写法。
在 Ruby 中 nil 和 false 以外的值全都当做 true 处理。而 || 这个运算符,如果 true 时就会执行返回左边,如果是 false 就返回右边的值。因此,上一段脚本又等同下方的脚本:
if @cache != nil @cache = @cache else @cache = {} end
记得 {} 这个符号是什么吗?他是制作空的哈希表的方法。将以上整理起来的意思就是,如果 @cache 这个实例变量的值为空,那么就生成一个空的哈希表对象赋值到 @cache。实例变量通常是通过类中定义的一般的方法来使用,不过这里则是在模块方法中使用。像本次的情况,实例变量可以理解为隶属于 Cache 模块本身的变量。
这是一个比较高级的写法,但是为了让脚本更加简洁,在预设脚本中也会用到。
接着继续往下看吧
if filename.empty? empty_bitmap elsif hue == 0 normal_bitmap(folder_name + filename) else hue_changed_bitmap(folder_name + filename, hue) end
这个条件分支虽然很长,但内容并没有那么复杂。
首先是确认 filename 是否为空、如果是的话就调用 empty_bitmap 然后返回该方法的值。如果 filename 不为空、当 hue (色调) 的值为 0 时调用 normal_bitmap 方法、 0 以外的场合调用 hue_changed_bitmap 方法。
也就是说 load_bitmap 方法、在内部又调用了别的方法。就像这个方法一样,VX Ace 的脚本相对上来说,会倾向将各种处理以「方法」为单元划分撰写。如此一来初学者可能會在解读上产生困惑,但是这正是让方法的重复利用性和可读性提高的做法,到最后你也会认为这样比较好吧。
保险起见也来解释一下下面这段。
folder_name + filename
这个纯粹只是字符串的加法。比方说 folder_name 是"Graphics/Battlers/"、filename 是"Skeleton" 的时候、会把这两者连接起来变成"Graphics/Battlers/Skeleton"。
接下来,先确认 normal_bitmap 的操作。
def self.normal_bitmap(path) @cache[path] = Bitmap.new(path) unless include?(path) @cache[path] end
unless 置于右侧,这是修饰符(modifier)形式的分支条件。在这里进行的是这样的处理:如果对应该路径的位图并不存在于缓存中的话,就会以新增的方式读取图片,如果已经存在,就直接返回。
include? 方法的操作如下:
def self.include?(key) @cache[key] && !@cache[key].disposed? end
这个条件进行判定的是,@cache 所指向的哈希表对象中是否含key值,且这个位图是否已经被释放。有关 disposed? 方法,请参考 Bitmap。
Cache 模块中还包含色调变化等等的处理,但也没有非看不可的部分,所以就省略解释了。