赞 189
VIP 627
好人卡 188
积分 95
经验 171230
最后登录 2024-7-3
在线时间 5073 小时
Lv4.逐梦者 (版主 )
梦石 0
星屑 9532
在线时间 5073 小时
注册时间 2013-6-21
帖子 3580
本帖最后由 RyanBern 于 2015-8-2 13:24 编辑
[box=RoyalBlue]第4章节:窗口的使用
[/box]
接下来我们终于要学习窗口和精灵了,这我想也是大家特别想知道的。因为在这之前,我们都是同抽象的数据打交道,难免有些枯燥,那么在这一章,我们就要把我们自己脑中所想,全部展现在画面中。下面我们就开始吧。
4.1 窗口的使用
4.1.1各种概念
在最开始,我们要明白一个窗口到底是什么,它到底有哪些属性。因此,我们必须要先介绍下面几个类。
①rect:矩形的类
顾名思义,这个类表示矩形,但这种东西是不能直接显示出来的。矩形是一种基本的类,它的作用在很多高级的类中体现尤其明显。矩形有四个属性,x坐标,y坐标,宽度,高度。其中x坐标和y坐标是相对而言的。
②viewport:视口的类
这个类是一个比较难理解的类,我在最开始也没明白这是什么东西。在屏幕上生成可见的对象都必须要指定视口viewport。简单说来,你面前有一堵墙,墙上有一个窗户可以看到墙对面的东西,那么这个窗户就可以类比成视口,你只能看到视口内有的东西,视口之外,即使有内容存在,你是看不到的。
我们举个简单的例子,大家打开脚本编辑器,在Window_BattleStatus中的initialize方法中,输入self.back_opacity = 0
然后随便找一个640*480的战斗图片(注意大小是640*480),设置为一个地图的战斗图,然后进入战斗场景,画面会变成这样:
下面的640*160的矩形区域全部都是黑色的,这就说明图片显示不全,只有上半部分显示了出来。我们说过图片是640*480的,因此出现这种情况是我们视口的设置问题。打开Spriteset_Battle,我们在前面发现了这个:
我们看到生成战斗背景的视口是640*320,因此我们的640*480图片才显示不出来。
因此,无论图片是什么,生成的可视对象都必须在视口之内。
视口的提出为我们管理界面上的可显示元素提供方便,因为视口的移动,色相改变,闪烁只会影响视口内部的元素,而视口外部的元素不受影响。
③bitmap:位图的类
我们可以认为这一个类专门表示图片,但是要注意,Bitmap对象只是一个数据而已,并不能直接作为显示在屏幕中的对象。所以,bitmap是没有x坐标和y坐标这一属性的,这个要千万注意。但是,作为游戏的一种数据,在游戏运行的时候,必须要进入到游戏的内存中,RGSS里面有个高速缓存,专门用来保存这些临时的图片,用的时候就要在这里读取。因为高速缓存空间有限,因此我们要学会将不用的bitmap释放掉。
我们来看看bitmap里面都有什么方法。
dispose:释放位图,即将不用的位图从高速缓存中释放掉,我们在写程序时,必须有及时释放的好习惯,当然如果一个位图被频繁的使用,就不用释放了。
clear:清除位图全体,即将位图的内容变成一个空的“画布”
blt(x,y,src_bitmap,src_rect,opacity):这个方法非常重要,大家一定要熟练掌握。这个方法的作用有点类似于两个位图之间的拷贝,意思就是将位图A指定矩形的内部内容拷贝到本位图中,放到指定坐标的位置。下图更加直观说明了这个事情。
这样大家应该理解了吧。
fill_rect(x,y,width,height,color)
fill_rect(rect,color):上面两个都是在位图中绘制实心矩形,可以用矩形的各种信息,也可以直接把矩形放在参数中,要指定填充的颜色,利用这两个函数,我们可以在bitmap中绘制矩形,然后把它显示出来(要用到精灵)。朴素的HP条SP条就是这样描绘的。
draw_text(x,y,width,height,str,align):在位图的指定区域描绘文字,align是对齐方式。这有点类似于Windows画图中的创建文字。这个大家也要熟练使用。F1中也说,这个处理要花费时间,尽量不要重复描绘字符串(意思就是只有在文字改变的时候才可描绘,比较省CPU,防止游戏卡顿)。
这些方法大家一定要熟练掌握,下面我们正式开始窗口的学习。
4.1.2 窗口的基本知识 一般窗口的实现
脚本编辑器中,所有窗口的父类Window没有给出,我们也无法得知它的代码,但是我们打开F1,搜索Window,就会了解Window类的方法和属性,这对我们来说是个很好的参考。
窗口实际上是一种可以在屏幕上显示的图片,因此生成必须要指定视口。但是由于系统默认的缘故,不用额外加以设置。窗口以大量精灵构成,因此要有dispose方法。我们使用一个窗口完毕后,一定要把它释放掉。这个我们在介绍场景的时候会有说明。
F1中将各种方法已经说得很详细,这个我就不用多说。不过还是强调几个地方吧。
visible是可见与否的属性,如果你已经生成了一个窗口,但是不想显示它,就要用到visible属性,注意,隐藏不意味着释放。
active是活动状态属性,主要出现在有光标的窗口中,处于活动状态的窗口可以接受命令,进行光标闪烁等,游戏不能同时处理2个以上活动的窗口(你没见过按一下方向键两个窗口跟着一起变的吧?),因此同一个场景中,活动的窗口至多1个。
ox,oy是窗口内容原点的坐标,在这里我们可以把窗口看作一个小小的视口,里面的内容只能通过窗口显示出来。
注意:游戏默认窗口内容显示的视口是窗口上下左右边界向内16个像素,因此窗口实际内容和窗口边界总有16个像素的间隔,这个就尽量不要更改了。
作为所有Window的父类,Window里面可用的方法很少,我们来看看真正能作为显示窗口类的Window_Base:
先看initialize部分:
def initialize( x, y, width, height)
super ( )
@windowskin_name = $game_system .windowskin_name
self .windowskin = RPG::Cache .windowskin ( @windowskin_name)
self .x = x
self .y = y
self .width = width
self .height = height
self .z = 100
end
def initialize( x, y, width, height)
super ( )
@windowskin_name = $game_system .windowskin_name
self .windowskin = RPG::Cache .windowskin ( @windowskin_name)
self .x = x
self .y = y
self .width = width
self .height = height
self .z = 100
end
这里,我们生成一个窗口对象要指定它的位置和大小,并且设定窗口外观(这里默认是数据库的窗口外观),最后是z坐标,窗口的高度。
紧接着后面是个释放的方法,重定义:
def dispose
# 如果窗口的内容已经被设置就被释放
if self .contents != nil
self .contents .dispose
end
super
end
def dispose
# 如果窗口的内容已经被设置就被释放
if self .contents != nil
self .contents .dispose
end
super
end
在这里我们看到,对窗口释放同时也释放了作为窗口内容显示的位图,这样的好处是让我们的操作变得简单。例如写@a.dispose这样的语句,不但释放了窗口本身,也释放了窗口内容位图,一举两得。
后面是定义获取各种颜色的方法,这我就不多说了。
刷新update:
def update
super
# 如果窗口的外关被变更了、再设置
if $game_system .windowskin_name != @windowskin_name
@windowskin_name = $game_system .windowskin_name
self .windowskin = RPG::Cache .windowskin ( @windowskin_name)
end
end
def update
super
# 如果窗口的外关被变更了、再设置
if $game_system .windowskin_name != @windowskin_name
@windowskin_name = $game_system .windowskin_name
self .windowskin = RPG::Cache .windowskin ( @windowskin_name)
end
end
这个update方法是刷新窗口,在RGSS中,有刷新作用的方法,原则上1帧调用一次,这个一定要注意。我们看到Window_Base的刷新很简单,就是要判定窗口外观的改变,并没有涉及内容的处理。因此我们在描绘内容时,尽量不要刷新它,我们刚才说到了draw_text需要花费时间,因此要避免每1帧都重新描绘文字。
下面则是一些基本的描绘:
def draw_actor_graphic( actor, x, y)
bitmap = RPG::Cache .character ( actor.character_name , actor.character_hue )
cw = bitmap.width / 4
ch = bitmap.height / 4
src_rect = Rect.new ( 0 , 0 , cw, ch)
self .contents .blt ( x - cw / 2 , y - ch, bitmap, src_rect)
end
def draw_actor_graphic( actor, x, y)
bitmap = RPG::Cache .character ( actor.character_name , actor.character_hue )
cw = bitmap.width / 4
ch = bitmap.height / 4
src_rect = Rect.new ( 0 , 0 , cw, ch)
self .contents .blt ( x - cw / 2 , y - ch, bitmap, src_rect)
end
角色图形的描绘,有3个参数,角色,描绘x坐标,描绘y坐标。
第二行是取得角色的位图,把它存储在高速缓存中。用的模块方法RPG::Cache,这个大家自行F1就好。然后是设定显示的内容,我们不能把整个图片显示出来,只需要显示左上角那个正脸的图就可以了,因此后面3行全部在设置截取图片的位置,随后,用blt方法,把截取的位置原封不动传送到self.contents中,window的contents就是表示窗口内容的位图,这恰恰就是blt完成的工作:把一个位图里面指定位置内容传给另一个位图中。在窗口中描绘图片就是这么简单。
下面我们再来看一个描绘文字的:
def draw_actor_hp( actor, x, y, width = 144 )
# 描绘字符串 "HP"
self .contents .font .color = system_color
self .contents .draw_text ( x, y, 32 , 32 , $data_system .words .hp )
# 计算描绘 MaxHP 所需的空间
if width - 32 >= 108
hp_x = x + width - 108
flag = true
elsif width - 32 >= 48
hp_x = x + width - 48
flag = false
end
# 描绘 HP
self .contents .font .color = actor.hp == 0 ? knockout_color :
actor.hp <= actor.maxhp / 4 ? crisis_color : normal_color
self .contents .draw_text ( hp_x, y, 48 , 32 , actor.hp .to_s , 2 )
# 描绘 MaxHP
if flag
self .contents .font .color = normal_color
self .contents .draw_text ( hp_x + 48 , y, 12 , 32 , "/" , 1 )
self .contents .draw_text ( hp_x + 60 , y, 48 , 32 , actor.maxhp .to_s )
end
end
def draw_actor_hp( actor, x, y, width = 144 )
# 描绘字符串 "HP"
self .contents .font .color = system_color
self .contents .draw_text ( x, y, 32 , 32 , $data_system .words .hp )
# 计算描绘 MaxHP 所需的空间
if width - 32 >= 108
hp_x = x + width - 108
flag = true
elsif width - 32 >= 48
hp_x = x + width - 48
flag = false
end
# 描绘 HP
self .contents .font .color = actor.hp == 0 ? knockout_color :
actor.hp <= actor.maxhp / 4 ? crisis_color : normal_color
self .contents .draw_text ( hp_x, y, 48 , 32 , actor.hp .to_s , 2 )
# 描绘 MaxHP
if flag
self .contents .font .color = normal_color
self .contents .draw_text ( hp_x + 48 , y, 12 , 32 , "/" , 1 )
self .contents .draw_text ( hp_x + 60 , y, 48 , 32 , actor.maxhp .to_s )
end
end
描绘角色HP。这里面参数多了一个宽度width,大家注意观察就可以发现,在菜单中描绘HP,是有最大HP的,在战斗中则没有,就是由于二者宽度不同导致的。后面的draw_text大家看看,基本上都会用。在这里提示一下,draw_text能描绘字符串,而不能描绘数字本身,因此draw_text(0, 0, 32, 32, 250)这样来描绘数字250是行不通的,Object类有个方法叫做to_s,把本身转换为字符串,因此要写250.to_s,这样等同于"250"。
另外,很多人被那些坐标还有宽度设置困扰,总也描绘不到合适的位置。这个其实也比较好解决,整个画面是640*480的,简单进行一些运算就会知道各种位置。还有,在窗口中进行的x,y都是指相对窗口内容位图的原点位置,并不是屏幕的位置,这个大家要注意。游戏默认字的高度是24个像素,一行的高度是32个像素,因此,如果5个默认大小的字连起来时,长度就是120(一个汉字的宽度=2个英文字母的宽度),因此根据这个就可以设定描绘文字矩形的宽度了。最后要注意,draw_text是不能换行的,如果文字太长,它会一直描绘下去,可能会出边界。
有了这些知识,我们就可以来刻画一个最基本的窗口了。下面来小小测试一下。
class Window_Test < Window_Base
def initialize
super ( 0 ,0 ,196 ,64 )
self .contents = Bitmap.new ( width – 32 , height - 32 )
refresh
end
def refresh
self .contents .clear
self .contents .draw_text ( 0 ,0 ,144 ,32 ,”RMXP is good”)
end
end
class Window_Test < Window_Base
def initialize
super ( 0 ,0 ,196 ,64 )
self .contents = Bitmap.new ( width – 32 , height - 32 )
refresh
end
def refresh
self .contents .clear
self .contents .draw_text ( 0 ,0 ,144 ,32 ,”RMXP is good”)
end
end
这样一个简单的窗口就定义好了。
但是这样是无法在屏幕上显示的,要让它显示出来,通常要借助场景Scene的帮助。
我们先学一点后面的东西,让我们的窗口显示出来。
在Scene_Map的main函数里面添加这样的语句:
然后再运行一下。效果如图。
在这里我们改动了Scene_Map脚本,但是也很简单,先创建一个窗口,在场景结束后再释放它,就是这样。
但是现在我们生成的窗口内容不能自动变化,如果我们要生成一个内容可能变化的窗口,这样就不行了。比方说,我们要在地图上显示角色的金钱,但是如果通过事件增加了队伍的金钱,窗口的内容是没有变化的。这就需要我们对update进行下改装,如果内容变更了,就重新描绘内容。我们知道draw_text是不适合反复调用的,因此我们只有在需要的时候,才能重新描绘窗口内容。首先要在refresh方法加上
然后我们修改update方法:
def update
super
if $game_party .gold != @gold
refresh
end
end
def update
super
if $game_party .gold != @gold
refresh
end
end
这里,我们可以设置一个不公开的变量@gold,表示当前窗口描绘的金钱数量。然后对update进行重定义,让它能自动检查内容的变化。当然,我们也要对场景脚本进行改变,场景脚本里面有个update方法,在里面,我们加入对窗口的刷新@a.update,在这里我就不给出所有的代码了,作为练习,希望读者能写出一个在地图上显示金钱的窗口。
当然,这样简单的窗口,我们还是有方法能判断内容是否变更的,但是对于复杂的窗口,我们就无法判断了,因此,我们还需要掌握其他方法。
4.1.3 滚动窗口的实现
下面我们学习如何使用滚动窗口。滚动窗口要用Window_Selectable。
打开这个脚本,我们发现它是Window_Base的子类。
#--------------------------------------------------------------------------
# ● 定义实例变量
#--------------------------------------------------------------------------
attr_reader :index # 光标位置
attr_reader :help_window # 帮助窗口
#--------------------------------------------------------------------------
# ● 初始化对像
# x : 窗口的 X 坐标
# y : 窗口的 Y 坐标
# width : 窗口的宽
# height : 窗口的高
#--------------------------------------------------------------------------
def initialize( x, y, width, height)
super ( x, y, width, height)
@item_max = 1
@column_max = 1
@index = -1
end
#--------------------------------------------------------------------------
# ● 定义实例变量
#--------------------------------------------------------------------------
attr_reader :index # 光标位置
attr_reader :help_window # 帮助窗口
#--------------------------------------------------------------------------
# ● 初始化对像
# x : 窗口的 X 坐标
# y : 窗口的 Y 坐标
# width : 窗口的宽
# height : 窗口的高
#--------------------------------------------------------------------------
def initialize( x, y, width, height)
super ( x, y, width, height)
@item_max = 1
@column_max = 1
@index = -1
end
这里多了两个属性,光标位置(索引)和帮助窗口。考虑到很多滚动窗口(比方说物品和技能窗口)都要关联帮助窗口,因此需要设置这个属性。
初始化也没什么特别的,多了2个内部的实变量@item_max和@colunm_max,分别是描绘项目的最大值和描绘列数的最大值。
继续向下看,里面有计算窗口项目位置的各种方法。包括开头行,一页的最大项目数,一页的最大行数,都非常简单,这里就不说了。
我们在这里看到,RMXP里面默认一行的高度是32,也就是说,上面的所有设置位置的方法,都是按照这个标准进行的。当然,这个功能并不完善,我们可以对Window_Selectable进行修改,让它能显示任意行高的项目。
然后就是对输入的处理和光标矩形的更新,这里简单看看就可以了,不能完全理解也没有关系,只要记住这是干什么用的就好了。另外,Window_Selectable里面并没有给出光标明灭变化的方法,那个是在Window中定义的,只是我们看不到而已。
下面我们以Window_Item为例,来看看滚动窗口该怎样制作。
class Window_Item < Window_Selectable
#--------------------------------------------------------------------------
# ● 初始化对像
#--------------------------------------------------------------------------
def initialize
super ( 0 , 64 , 640 , 416 )
@column_max = 2
refresh
self .index = 0
# 战斗中的情况下将窗口移至中央并将其半透明化
if $game_temp .in_battle
self .y = 64
self .height = 256
self .back_opacity = 160
end
end
#--------------------------------------------------------------------------
# ● 获取物品
#--------------------------------------------------------------------------
def item
return @data [ self .index ]
end
#--------------------------------------------------------------------------
# ● 刷新
#--------------------------------------------------------------------------
def refresh
if self .contents != nil
self .contents .dispose
self .contents = nil
end
@data = [ ]
# 添加项目
for i in 1 ...$data_items.size
if $game_party .item_number ( i) > 0
@data .push ( $data_items[ i] )
end
end
# 在战斗中以外添加武器、防具
unless $game_temp .in_battle
for i in 1 ...$data_weapons.size
if $game_party .weapon_number ( i) > 0
@data .push ( $data_weapons[ i] )
end
end
for i in 1 ...$data_armors.size
if $game_party .armor_number ( i) > 0
@data .push ( $data_armors[ i] )
end
end
end
# 如果项目数不是 0 就生成位图、重新描绘全部项目
@item_max = @data .size
if @item_max > 0
self .contents = Bitmap.new ( width - 32 , row_max * 32 )
for i in 0 ...@item_max
draw_item( i)
end
end
end
#--------------------------------------------------------------------------
# ● 描绘项目
# index : 项目编号
#--------------------------------------------------------------------------
def draw_item( index)
item = @data [ index]
case item
when RPG::Item
number = $game_party .item_number ( item.id )
when RPG::Weapon
number = $game_party .weapon_number ( item.id )
when RPG::Armor
number = $game_party .armor_number ( item.id )
end
if item.is_a ?( RPG::Item ) and
$game_party .item_can_use ?( item.id )
self .contents .font .color = normal_color
else
self .contents .font .color = disabled_color
end
x = 4 + index % 2 * ( 288 + 32 )
y = index / 2 * 32
rect = Rect.new ( x, y, self .width / @column_max - 32 , 32 )
self .contents .fill_rect ( rect, Color.new ( 0 , 0 , 0 , 0 ) )
bitmap = RPG::Cache .icon ( item.icon_name )
opacity = self .contents .font .color == normal_color ? 255 : 128
self .contents .blt ( x, y + 4 , bitmap, Rect.new ( 0 , 0 , 24 , 24 ) , opacity)
self .contents .draw_text ( x + 28 , y, 212 , 32 , item.name , 0 )
self .contents .draw_text ( x + 240 , y, 16 , 32 , ":" , 1 )
self .contents .draw_text ( x + 256 , y, 24 , 32 , number.to_s , 2 )
end
#--------------------------------------------------------------------------
# ● 刷新帮助文本
#--------------------------------------------------------------------------
def update_help
@help_window .set_text ( self .item == nil ? "" : self .item .description )
end
end
class Window_Item < Window_Selectable
#--------------------------------------------------------------------------
# ● 初始化对像
#--------------------------------------------------------------------------
def initialize
super ( 0 , 64 , 640 , 416 )
@column_max = 2
refresh
self .index = 0
# 战斗中的情况下将窗口移至中央并将其半透明化
if $game_temp .in_battle
self .y = 64
self .height = 256
self .back_opacity = 160
end
end
#--------------------------------------------------------------------------
# ● 获取物品
#--------------------------------------------------------------------------
def item
return @data [ self .index ]
end
#--------------------------------------------------------------------------
# ● 刷新
#--------------------------------------------------------------------------
def refresh
if self .contents != nil
self .contents .dispose
self .contents = nil
end
@data = [ ]
# 添加项目
for i in 1 ...$data_items.size
if $game_party .item_number ( i) > 0
@data .push ( $data_items[ i] )
end
end
# 在战斗中以外添加武器、防具
unless $game_temp .in_battle
for i in 1 ...$data_weapons.size
if $game_party .weapon_number ( i) > 0
@data .push ( $data_weapons[ i] )
end
end
for i in 1 ...$data_armors.size
if $game_party .armor_number ( i) > 0
@data .push ( $data_armors[ i] )
end
end
end
# 如果项目数不是 0 就生成位图、重新描绘全部项目
@item_max = @data .size
if @item_max > 0
self .contents = Bitmap.new ( width - 32 , row_max * 32 )
for i in 0 ...@item_max
draw_item( i)
end
end
end
#--------------------------------------------------------------------------
# ● 描绘项目
# index : 项目编号
#--------------------------------------------------------------------------
def draw_item( index)
item = @data [ index]
case item
when RPG::Item
number = $game_party .item_number ( item.id )
when RPG::Weapon
number = $game_party .weapon_number ( item.id )
when RPG::Armor
number = $game_party .armor_number ( item.id )
end
if item.is_a ?( RPG::Item ) and
$game_party .item_can_use ?( item.id )
self .contents .font .color = normal_color
else
self .contents .font .color = disabled_color
end
x = 4 + index % 2 * ( 288 + 32 )
y = index / 2 * 32
rect = Rect.new ( x, y, self .width / @column_max - 32 , 32 )
self .contents .fill_rect ( rect, Color.new ( 0 , 0 , 0 , 0 ) )
bitmap = RPG::Cache .icon ( item.icon_name )
opacity = self .contents .font .color == normal_color ? 255 : 128
self .contents .blt ( x, y + 4 , bitmap, Rect.new ( 0 , 0 , 24 , 24 ) , opacity)
self .contents .draw_text ( x + 28 , y, 212 , 32 , item.name , 0 )
self .contents .draw_text ( x + 240 , y, 16 , 32 , ":" , 1 )
self .contents .draw_text ( x + 256 , y, 24 , 32 , number.to_s , 2 )
end
#--------------------------------------------------------------------------
# ● 刷新帮助文本
#--------------------------------------------------------------------------
def update_help
@help_window .set_text ( self .item == nil ? "" : self .item .description )
end
end
这里的初始化大家应该都没问题了,然后在这里定义了一个方法item,返回当前光标所指的物品。这个方法是必要的,原因我们要到场景的地方再解释。最后就是refresh方法,用来描绘窗口内的所有内容。
refresh里面大概分为以下几个层次。
首先释放窗口内容,这样做的目的是节省内存。考虑到窗口内容可能比较多,用self.contents.clear反倒不如释放再重新生成来得直接。而且描绘的最大项目数也会随着队伍中物品的变化而变化,如果设置固定的contents大小,不便于后面的处理。
接下来,就是制作窗口内容显示数据,把要显示的物品,武器,防具添加在一个@data数组里面,这是窗口的内部信息,没必要公开化。
接下来就是描绘窗口内容,在窗口存在项目的时候,一个个描绘。
描绘单个物品的方法draw_item(i)在后面定义,i指的是物品在@data中的索引,里面的结构清晰直观,大家看看就可以了。里面那个case语句希望大家熟练使用,case语句可以对实例进行类的判定,有了这个机制,写代码会比较方便。
最后,定义刷新帮助文本的方法(Window_Selectable里面已经有说明)。
怎么样?现在你可以写出有滚动光标的窗口了。
在这里我们留下一个小练习
就是我们刚才提到的,Window_Selectable默认每一行高度是32,能否对其进行优化,让用户创建自定义行高的窗口,并且和原来的脚本不发生冲突?
4.2 一个滚动窗口的例子——真实商店
不知道大家对之前介绍游戏对象的创建还有没有印象,在这一章节里面,我们要把商店的货物在窗口中描绘出来。如果忘了前面的内容的话,还请翻看下前面的帖子哦。
4.2.1 准备工作
我们想想这个窗口要描绘出什么。肯定要描绘各种物品,它的剩余量和它的价格。我们利用Window_Selectable的滚动功能,来实现滚动处理。
4.2.2 Ruby代码
有了上面的准备我们就可以写出代码了。
class Window_Visual_ShopBuy < Window_Selectable
def initialize( shop_id)
super ( 0 ,128 ,368 ,480 -128 )
self .index = 0
@column_max = 1
@shop_current = $game_visual_shops [ shop_id]
refresh
end
# 取得当前光标的物品
def item
return @data [ self .index ]
end
def refresh
# 内容被设置就释放,重新设置
if self .contents != nil
self .contents .dispose
self .contents = nil
end
# 设置各种项目数据
@data_items = [ ]
@data_weapons = [ ]
@data_armors = [ ]
# 添加物品
for item_id in @shop_current .shop_goods_item .keys
@data_items .push ( $data_items[ item_id] )
end
# 排序按照物品ID
@data_items .sort !{ |a,b| a.id – b.id }
# 添加武器
for weapon_id in @shop_current .shop_goods_weapon .keys
@data_weapon .push ( $data_weapons[ item_id] )
end
# 排序按照武器ID
@data_weapons .sort !{ |a,b| a.id – b.id }
# 添加防具
for armor_id in @shop_current .shop_goods_armor .keys
@data_armors .push ( $data_armors[ item_id] )
end
# 排序按照物品ID
@data_armors .sort !{ |a,b| a.id – b.id }
# 合并数组
@data = @data_items + @data_weapons + @data_armors
@item_max = @data .size
if @item_max > 0
self .contents = Bitmap.new ( width - 32 , row_max * 32 )
for index in 0 …@item_max
draw_item( index)
end
end
end
end
class Window_Visual_ShopBuy < Window_Selectable
def initialize( shop_id)
super ( 0 ,128 ,368 ,480 -128 )
self .index = 0
@column_max = 1
@shop_current = $game_visual_shops [ shop_id]
refresh
end
# 取得当前光标的物品
def item
return @data [ self .index ]
end
def refresh
# 内容被设置就释放,重新设置
if self .contents != nil
self .contents .dispose
self .contents = nil
end
# 设置各种项目数据
@data_items = [ ]
@data_weapons = [ ]
@data_armors = [ ]
# 添加物品
for item_id in @shop_current .shop_goods_item .keys
@data_items .push ( $data_items[ item_id] )
end
# 排序按照物品ID
@data_items .sort !{ |a,b| a.id – b.id }
# 添加武器
for weapon_id in @shop_current .shop_goods_weapon .keys
@data_weapon .push ( $data_weapons[ item_id] )
end
# 排序按照武器ID
@data_weapons .sort !{ |a,b| a.id – b.id }
# 添加防具
for armor_id in @shop_current .shop_goods_armor .keys
@data_armors .push ( $data_armors[ item_id] )
end
# 排序按照物品ID
@data_armors .sort !{ |a,b| a.id – b.id }
# 合并数组
@data = @data_items + @data_weapons + @data_armors
@item_max = @data .size
if @item_max > 0
self .contents = Bitmap.new ( width - 32 , row_max * 32 )
for index in 0 …@item_max
draw_item( index)
end
end
end
end
由于窗口显示的是三种不同的物品,因此设置数据要花一些时间,但是基本思路跟描绘道具一样,因为商店里面的物品在变化,因此不要一开始就创建bitmap。
下面就是我们描绘物品的方法draw_item(index)了,注意,这个方法仍然定义在这个窗口内部,只不过是在这个帖子里面,写到外面了而已。
def draw_item( index)
item = @data [ index]
# 取得持有数量和库存
case item
when RPG::Item
number = $game_party .item_number ( item.id )
number_left = @current_shop .shop_goods_item [ item.id ]
when RPG::Weapon
number = $game_party .weapon_number ( item.id )
number_left = @current_shop .shop_goods_weapon [ item.id ]
when RPG::Armor
number = $game_party .armor_number ( item.id )
number_left = @current_shop .shop_goods_armor [ item.id ]
end
# 设置颜色
if item.price <= $game_party .gold and number < 99 and number_left > 0
self .contents .font .color = normal_color
else
self .contents .font .color = disabled_color
end
# 开始描绘
x = 4
y = index * 32
rect = Rect.new ( x, y, self .width - 32 , 32 )
self .contents .fill_rect ( rect, Color.new ( 0 , 0 , 0 , 0 ) )
bitmap = RPG::Cache .icon ( item.icon_name )
opacity = self .contents .font .color == normal_color ? 255 : 128
self .contents .blt ( x, y + 4 , bitmap, Rect.new ( 0 , 0 , 24 , 24 ) , opacity)
self .contents .draw_text ( x + 28 , y, 168 , 32 , item.name , 0 )
self .contents .draw_text ( x + 196 , y, 80 , 32 , item.price .to_s , 2 )
self .contents .draw_text ( x + 276 , y, 48 , 32 , “剩:”)
self .contents .draw_text ( x + 308 , y, 32 , 32 , number_left.to_s , 2 )
end
def draw_item( index)
item = @data [ index]
# 取得持有数量和库存
case item
when RPG::Item
number = $game_party .item_number ( item.id )
number_left = @current_shop .shop_goods_item [ item.id ]
when RPG::Weapon
number = $game_party .weapon_number ( item.id )
number_left = @current_shop .shop_goods_weapon [ item.id ]
when RPG::Armor
number = $game_party .armor_number ( item.id )
number_left = @current_shop .shop_goods_armor [ item.id ]
end
# 设置颜色
if item.price <= $game_party .gold and number < 99 and number_left > 0
self .contents .font .color = normal_color
else
self .contents .font .color = disabled_color
end
# 开始描绘
x = 4
y = index * 32
rect = Rect.new ( x, y, self .width - 32 , 32 )
self .contents .fill_rect ( rect, Color.new ( 0 , 0 , 0 , 0 ) )
bitmap = RPG::Cache .icon ( item.icon_name )
opacity = self .contents .font .color == normal_color ? 255 : 128
self .contents .blt ( x, y + 4 , bitmap, Rect.new ( 0 , 0 , 24 , 24 ) , opacity)
self .contents .draw_text ( x + 28 , y, 168 , 32 , item.name , 0 )
self .contents .draw_text ( x + 196 , y, 80 , 32 , item.price .to_s , 2 )
self .contents .draw_text ( x + 276 , y, 48 , 32 , “剩:”)
self .contents .draw_text ( x + 308 , y, 32 , 32 , number_left.to_s , 2 )
end
最后别忘了定义update_help,这样就大功告成了。不过,我们暂时无法检验它的对错,有兴趣的朋友可以利用场景来检验一下。
上图就是我们期待的效果,不过我们做的仅仅是左下角这个窗口哦,而且各种命令的处理还没有设定。
窗口的学习我们告一段落,接下来我们将要学习场景的使用。大家就敬请期待吧。