百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

RUBY与游戏引擎|RMXP脚本解析(四)

cac55 2024-11-13 09:34 17 浏览 0 评论

写在开头

其实我已经注意到了这个系列反响平平,但是我之前说过会一直写下去。并且,我还是认为这个系列能对读者的编程能力和解读代码的能力起到很好的帮助作用。但是我实在嘴笨,也不知道该怎么形容,而且阅读此系列确实需要读者做一些额外的工作(比如说自行查看RMXP软件本体和学习Ruby,即使忽略头条受众群体的特殊性,这对读者也是很难的事情),应该很少有人能为了一篇文章做到这些。

即使如此,该做的还是要做,我会一直写下去的、

前言

顺着Scene_Title的实例化脉络走下去,我们这次本来是要解析实例化@command_window的Window_Command类,但由于Window_Command类的父类是Window_Selectable,而Window_XX类还有个超级类Window_Base(它的父类是RGSS中的Window),所以我们就先从Window_Base讲起,一步一步讲到Window_Command。

Window_Base类代码解析

#==============================================================================

# ■ Window_Base

#------------------------------------------------------------------------------

#  游戏中全部窗口的超级类。

#==============================================================================

class Window_Base < Window #Window是RGSS的内部类

  #--------------------------------------------------------------------------

  # ● 初始化对像

  #     x      : 窗口的 X 坐标

  #     y      : 窗口的 Y 坐标

  #     width  : 窗口的宽

  #     height : 窗口的宽

  #--------------------------------------------------------------------------

  def initialize(x, y, width, height) 

    super()

    @windowskin_name = $game_system.windowskin_name #读取窗口皮肤的文件名为data文件中定义的文件名

    self.windowskin = RPG::Cache.windowskin(@windowskin_name) #这个模块是RGSS内部模块,负责将图片载入缓存

    self.x = x

    self.y = y

    self.width = width

    self.height = height

    self.z = 100 

  end

  #--------------------------------------------------------------------------

  # ● 释放

  #--------------------------------------------------------------------------

  def dispose

    # 如果窗口的内容已经被设置就被释放

    if self.contents != nil

      self.contents.dispose

    end #按道理self.contents是Window父类的属性,类型为Bitmap,Bitmap类的dispose可以在为nil的情况下调用,那为什么又要有这样的设置呢?

    super

  end

  #--------------------------------------------------------------------------

  # ● 获取文字色

  #     n : 文字色编号 (0~7)

  #--------------------------------------------------------------------------

  def text_color(n)

    case n

    when 0

      return Color.new(255, 255, 255, 255) #Color是RGSS的内部类,其初始化方法为Color.new(red, green, blue[, alpha]) 

    when 1

      return Color.new(128, 128, 255, 255)

    when 2

      return Color.new(255, 128, 128, 255)

    when 3

      return Color.new(128, 255, 128, 255)

    when 4

      return Color.new(128, 255, 255, 255)

    when 5

      return Color.new(255, 128, 255, 255)

    when 6

      return Color.new(255, 255, 128, 255)

    when 7

      return Color.new(192, 192, 192, 255)

    else

      normal_color #下面的方法

    end

  end

  #--------------------------------------------------------------------------

  # ● 获取普通文字色

  #--------------------------------------------------------------------------

  def normal_color

    return Color.new(255, 255, 255, 255)

  end

  #--------------------------------------------------------------------------

  # ● 获取无效文字色

  #--------------------------------------------------------------------------

  def disabled_color

    return Color.new(255, 255, 255, 128)

  end

  #--------------------------------------------------------------------------

  # ● 获取系统文字色

  #--------------------------------------------------------------------------

  def system_color

    return Color.new(192, 224, 255, 255)

  end

  #--------------------------------------------------------------------------

  # ● 获取危机文字色

  #--------------------------------------------------------------------------

  def crisis_color

    return Color.new(255, 255, 64, 255)

  end

  #--------------------------------------------------------------------------

  # ● 获取战斗不能文字色

  #--------------------------------------------------------------------------

  def knockout_color

    return Color.new(255, 64, 0)

  end

  #--------------------------------------------------------------------------

  # ● 刷新画面

  #--------------------------------------------------------------------------

  def update

    super #调用父类的同名方法,前进一帧

    # 如果窗口的外观被变更了、再设置

    if $game_system.windowskin_name != @windowskin_name #实例变量是旧的皮肤

      @windowskin_name = $game_system.windowskin_name #重新赋值

      self.windowskin = RPG::Cache.windowskin(@windowskin_name) #载入新皮肤到Cache

    end

  end

  #--------------------------------------------------------------------------

  # ● 图形的描绘

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #--------------------------------------------------------------------------

  def draw_actor_graphic(actor, x, y) #我最佩服作者在这算这么多 #描绘角色图片

    bitmap = RPG::Cache.character(actor.character_name, actor.character_hue) #RPG::Cache.character(filename, hue),其中hue为角色色相变化值(就是导入图片的时候下面有的色相条)

    cw = bitmap.width / 4 #character_width

    ch = bitmap.height / 4 #character_height

    src_rect = Rect.new(0, 0, cw, ch) #Rect为RGSS自带的类,负责生成矩形。Rect.new(x, y, width, height) 

    self.contents.blt(x - cw / 2, y - ch, bitmap, src_rect) #Bitmap类的方法blt(x, y, src_bitmap, src_rect[, opacity]) ,传送矩形src_rect到bitmap被指定的坐标上

    #通俗的理解是,我们先创建了角色位图bitmap,然后把矩形src_rect生成在bitmap的指定坐标,然后再把由矩形切片之后的位图移到blt函数指定的位置,到self.content这块画板上。

    #这里的self是调用本方法的对象。因为Window_Base是所有窗口类的超级类,所以他们都可以使用这个方法描绘角色

  end

  #--------------------------------------------------------------------------

  # ● 名称的描绘

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #--------------------------------------------------------------------------

  def draw_actor_name(actor, x, y)

    self.contents.font.color = normal_color #(Bitmap.font=Font).color

    self.contents.draw_text(x, y, 120, 32, actor.name) #Bitmap.draw_text(x, y, width, height, str[, align]) 在Rect.new(x,y,120,32)的矩形中描绘actor.name 

    #RM文本在矩形中的描绘机制可以参考帮助手册,比较长我们这里就不说了。

  end

  #--------------------------------------------------------------------------

  # ● 职业的描绘

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #--------------------------------------------------------------------------

  def draw_actor_class(actor, x, y) #同上

    self.contents.font.color = normal_color

    self.contents.draw_text(x, y, 236, 32, actor.class_name)

  end

  #--------------------------------------------------------------------------

  # ● 等级的描绘

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #--------------------------------------------------------------------------

  def draw_actor_level(actor, x, y)

    self.contents.font.color = system_color

    self.contents.draw_text(x, y, 32, 32, "Lv")

    self.contents.font.color = normal_color

    self.contents.draw_text(x + 32, y, 24, 32, actor.level.to_s, 2) #to_s方法是Ruby类中的一个特殊的方法,可以类比做__str__

  end

  #--------------------------------------------------------------------------

  # ● 生成描绘用的状态字符串

  #     actor       : 角色 #actor是否该改成battler?

  #     width       : 描画目标的宽度

  #     need_normal : [正常] 是否为必须 (true / false)

  #--------------------------------------------------------------------------

  def make_battler_state_text(battler, width, need_normal) #有时候觉得不用声明类型的语言的函数/方法挺烦人的...自己写又觉得很爽

    # 获取括号的宽

    brackets_width = self.contents.text_size("[]").width #Bitmap.text_size(str)方法取得描绘字符串的矩形,这里得到其宽度

    # 生成状态名字符串

    text = ""

    for i in battler.states #猜测battler也是RPG::Actor的对象 这个循环取出状态值最高的状态作为角色当前状态

      if $data_states[i].rating >= 1 #分析State.rxdata我们知道$data_state内是一系列RPG::State类对象 #RPG::State.rating的意义在帮助手册中定义不明,大概是表示程度的一个0...10的数字

#现在我知道他是数据库里的“定量”,数字越高显示越优先,数字小于1就不显示

        if text == "" #如果当前无状态

          text = $data_states[i].name #状态就是这个程度大于1的状态

        else

          new_text = text + "/" + $data_states[i].name #如果text有值就附加现在的状态到一个新的字符串里

          text_width = self.contents.text_size(new_text).width #然后获得其宽度

          if text_width > width - brackets_width #如果装不下了(也就是其宽度加上括号的宽度大于目标宽度)

            break #直接退出

          end

          text = new_text #如果装得下就不省略后面的字符串 #所以其实只要你的状态字符串够短就可以看到一串状态了,不过对于RMXP来说这个设置有点...画蛇添足?

        end

      end

    end

    # 状态名空的字符串是 "[正常]" 的情况下

    if text == ""

      if need_normal #此值为真则为正常,也就是没有任何状态的时候为正常 #其实RMXP数据库里的状态没有“正常”

        text = "[正常]"

      end

    else

      # 加上括号

      text = "[" + text + "]"

    end

    # 返回完成后的文字类

    return text

  end

  #--------------------------------------------------------------------------

  # ● 描绘状态

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #     width : 描画目标的宽

  #--------------------------------------------------------------------------

  def draw_actor_state(actor, x, y, width = 120) #既然这里是固定的那为什么生成字符串的时候还要输入,后来想想可能是为了统一宽度,但这样干脆设个常量不就行了

    text = make_battler_state_text(actor, width, true) #生成状态字符串,状态储存在actor里。 

    self.contents.font.color = actor.hp == 0 ? knockout_color : normal_color #在hp为0时使用特殊的颜色描绘,其他情况采用普通颜色(knockout_color为战斗不能颜色)

    self.contents.draw_text(x, y, width, 32, text) #在指定位置绘制文本 #更改32不仅会影响字体的显示还会影响字体的位置,想一下之前RMXP是怎么绘制角色图片的

  end

  #--------------------------------------------------------------------------

  # ● 描绘 EXP

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #--------------------------------------------------------------------------

  def draw_actor_exp(actor, x, y) #虽然是很早之前的代码了但是还是要bb一句...如果要翻新不知道会要谁的老命呢?RMVX程序的嘛...

    self.contents.font.color = system_color #self.content像是画笔,又像是画板,让人想起MFC程序里的一堆句柄

    self.contents.draw_text(x, y, 24, 32, "E") #系统文字E,表示Exp

    self.contents.font.color = normal_color

    self.contents.draw_text(x + 24, y, 84, 32, actor.exp_s, 2) #普通文字,actor.exp_s表示当前exp

    self.contents.draw_text(x + 108, y, 12, 32, "/", 1) 

    self.contents.draw_text(x + 120, y, 84, 32, actor.next_exp_s) #普通文字,actor.next_exp_s表示到下一级的exp

  end

  #--------------------------------------------------------------------------

  # ● 描绘 HP

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #     width : 描画目标的宽

  #--------------------------------------------------------------------------

  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) #$data_system里存储的是RMXP数据库里的系统一栏的设定。$data_system.words.hp应为用语->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 #两个三元运算,hp为0时采用行动不能颜色,小于1/4时采用危险颜色,其余时候采用正常颜色

    self.contents.draw_text(hp_x, y, 48, 32, actor.hp.to_s, 2) #2为对齐,右对齐 

    # 描绘 MaxHP

    if flag #flag为真才描绘MaxHP 事实上这种分别对齐的方法还挺可取的

      self.contents.font.color = normal_color #/MaxHP无论何时都是正常颜色

      self.contents.draw_text(hp_x + 48, y, 12, 32, "/", 1) #1为居中对齐

      self.contents.draw_text(hp_x + 60, y, 48, 32, actor.maxhp.to_s) #不指定align则为左对齐(上三句都在说水平情况,垂直则默认居中)

    end

  end

  #--------------------------------------------------------------------------

  # ● 描绘 SP

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #     width : 描画目标的宽

  #--------------------------------------------------------------------------

  def draw_actor_sp(actor, x, y, width = 144) #同上

    # 描绘字符串 "SP" 

    self.contents.font.color = system_color

    self.contents.draw_text(x, y, 32, 32, $data_system.words.sp)

    # 计算描绘 MaxSP 所需的空间

    if width - 32 >= 108

      sp_x = x + width - 108

      flag = true

    elsif width - 32 >= 48

      sp_x = x + width - 48

      flag = false

    end

    # 描绘 SP

    self.contents.font.color = actor.sp == 0 ? knockout_color :

      actor.sp <= actor.maxsp / 4 ? crisis_color : normal_color

    self.contents.draw_text(sp_x, y, 48, 32, actor.sp.to_s, 2)

    # 描绘 MaxSP

    if flag

      self.contents.font.color = normal_color

      self.contents.draw_text(sp_x + 48, y, 12, 32, "/", 1)

      self.contents.draw_text(sp_x + 60, y, 48, 32, actor.maxsp.to_s)

    end

  end

  #--------------------------------------------------------------------------

  # ● 描绘能力值

  #     actor : 角色

  #     x     : 描画目标 X 坐标

  #     y     : 描画目标 Y 坐标

  #     type  : 能力值种类 (0~6)

  #--------------------------------------------------------------------------

  def draw_actor_parameter(actor, x, y, type)

    case type 

    when 0

      parameter_name = $data_system.words.atk

      parameter_value = actor.atk #攻击

    when 1

      parameter_name = $data_system.words.pdef

      parameter_value = actor.pdef #物防

    when 2

      parameter_name = $data_system.words.mdef

      parameter_value = actor.mdef #魔防

    when 3

      parameter_name = $data_system.words.str

      parameter_value = actor.str #力量

    when 4

      parameter_name = $data_system.words.dex

      parameter_value = actor.dex #灵巧

    when 5

      parameter_name = $data_system.words.agi

      parameter_value = actor.agi #速度

    when 6

      parameter_name = $data_system.words.int

      parameter_value = actor.int #魔力

    end

    self.contents.font.color = system_color 

    self.contents.draw_text(x, y, 120, 32, parameter_name)

    self.contents.font.color = normal_color

    self.contents.draw_text(x + 120, y, 36, 32, parameter_value.to_s, 2)

  end #分别用两种颜色描绘能力名与能力值

  #--------------------------------------------------------------------------

  # ● 描绘物品名

  #     item : 物品

  #     x    : 描画目标 X 坐标

  #     y    : 描画目标 Y 坐标

  #--------------------------------------------------------------------------

  def draw_item_name(item, x, y) #目前有一个疑问,那就是特技也算item吗? 如果他们是不同的两个类但对于此方法有相同的属性 那么应该是可以共用的

    if item == nil #如果传入的物品对象不存在 返回

      return

    end

    bitmap = RPG::Cache.icon(item.icon_name) #将物品的图标图片载入Cache

    self.contents.blt(x, y + 4, bitmap, Rect.new(0, 0, 24, 24)) #图标一致24*24

    self.contents.font.color = normal_color

    self.contents.draw_text(x + 28, y, 212, 32, item.name)

  end

end

关于位图(src_bitmap)与矩形(src_rect)

可能有读者没有看懂RMXP里位图与矩形的关系——为什么矩形要比位图小?为什么角色行走图上有这么多角色,而显示里只显示了一个?为什么要把矩形放在位图上?

事实上,可以把矩形看成一个蒙版图层,位图看做背景。当矩形被Bitmap.blt方法放在bitmap(上层图片)的指定位置上,矩形选定的区域将被裁剪,最终RMXP只会描绘出被位图被矩形选中的部分(可以把矩形在背景位图的区域当作另一层画布)。然后blt将这个矩形裁剪过的bitmap放在调用blt方法的Bitmap(也就是背景位图)的(x,y)坐标上。(一定要注意Rect.new中的坐标是在上层图片的坐标,blt方法里的坐标才是在背景位图上的坐标,不要搞混,我测试过了)

就拿Window_Base类的draw_actor_graphic来举例,我拿阿尔西斯的行走图做示范。

关于叠加多个状态

测试了一下确实是可以,不过估计按照紫苑的翻译没几个能出现这种叠加的情况。没有看战斗处理状态的代码,但是我想这里只是省略显示并没有省略结算。

更新 关于RPG::State.rating

在写Game_Battler解析时翻数据库帮助发现的,它其实是数据库状态里的一个可选数值【定量】,如图:

总结

关于绘图的部分,受制于窗口大小其实可以有更多选择,幸好RMXP已经是很老的一版RM了。

相关推荐

正点原子开拓者FPGA开发板资料连载第四十章 SD卡图片显示实验

1)实验平台:正点原子开拓者FPGA开发板2)摘自《开拓者FPGA开发指南》关注官方微信号公众号,获取更多资料:正点原子3)全套实验源码+手册+视频下载地址:http://www.openedv.c...

东芝存储改名为铠侠了,铠侠microSD卡128GB全网首测

作为一个数码爱好者,平时总爱把玩各种科技数码产品,最近又迷上了口袋云台相机,大疆OsmoPocket、飞宇口袋相机、SnoppaVmate口袋相机什么的,不过这类产品由于设计的机身体积很小(毕竟为...

SD存储卡卡面上奇奇怪怪的图标,你知道几个?

现在对高像素照片、连拍、4K甚至8K的需求越来越多,对存储卡的传输速度、容量等,要求也越来越多了。但是,看到SD存储卡卡面上奇奇怪怪的图标,让人非常迷惑。这篇文章让你简单认识这些图标和奇奇怪怪的数字。...

拍摄4K视频上选!铠侠 EXCERIA PLUS microSD卡

大家好,我是波导终结者。今天跟大家分享的是铠侠的EXCERIAPLUS极至光速microSDXCUHS-1存储卡,名字有点长,但是不用担心,我会帮大家梳理好存储卡的选购建议。有不少刚入门的朋友...

高速稳定,一卡多用:铠侠极至光速microSD存储卡评测

Hello,大家好,我是小胖子。半个月前收到了KIOXIA铠侠寄来的一张256GB的TF卡,用了大半个月,让我们看看这款产品表现如何吧。其实很多人并不太了解铠侠,问我铠侠是什么品牌,好不好。其实,东芝...

读速205MB/s、V30规格,雷克沙SILVER系列存储卡再添新成员

IT之家6月19日消息,雷克沙今日推出3款SILVER系列SD/microSD存储卡新品,支持4K60fps录像。据介绍,该系列存储卡均符合V30标准,其中micr...

相机、无人机拍视频,选择SD存储卡有什么需要知道的?

本文章不涉及产品推荐导购行为,致力于给到小白带来基础知识。相机一般使用SD卡,无人机一般使用microSD卡(也叫TF卡),使用的标准和图标标识是一样的。相机、无人机拍视频,选择SD存储卡有什么需要知...

PNY推出适用Switch 2的microSD Express卡,读取速度高达890MB/s

任天堂Switch2开始预订,其比前代产品变得更加昂贵,各种配件的价格都高于预期,这也包括转向microSDExpress存储。此时,PNY推出了新款microSDExpress闪存卡。新款mi...

SD卡迎来25周年:全球售出120亿张,容量翻50万倍

IT之家5月21日消息,科技媒体betanews今天(5月21日)发布博文,报道称SD卡迎来了25周年的生日。自2000年首款SD存储卡问世以来,已走过25个年头...

微单相机买一款什么样的SD卡才够用?写入速度更为关键

最近,评价君朋友发现自己的卡拍摄视频时候总断流,于是感觉写入速度应该是不够的,打算换卡,评价君正好跟他说道说道。目前的SD存储卡,很多只标注读取速度,比如95MB/s,80MB/s等等,而没有写写入速...

金士顿Canvas Go!Plus 系列存储卡评测

前言2020年,金士顿推出了CanvasGo!Plus系列存储卡,凭借其优秀的读写速度和稳定性获得了广大用户的认可。时隔5年,金士顿推出了其全新升级产品:SDG4/SDCG4,可选容量覆盖64GB...

TF卡速度等级|MK米客方德(tf卡速度等级图)

TF卡(TransFlash卡,又称MicroSD卡)是一种常见的便携式存储媒体,广泛用于智能手机、相机、平板电脑等设备中。TF卡的性能通常由速度等级来衡量,这些等级反映了TF卡的数据传输速度。拓优星...

关于SD卡,看这张表就够了(sd卡的作用)

这里是溢图科技(原“相机笔记”)。这两天有不少存储产品促销,随之而来的就是关于SD卡的一些提问。文章以前已经写过很多了,这里主要给大家看一张表格:上面就是SD卡协会官方制作的“族谱”,明确给出了不同版...

轻量化储存的首选——凯侠极致光速256G microSD存储卡实测

对于摄影师而言,我们经常会接触到相关存储设备,像照片拍摄中给相机安装的SD卡,视频录制中外录高规格画面的SSD等,都属于专业的存储介质,被应用于商业拍摄、电影级别拍摄之中。而针对生活中我们日常用于拍摄...

首发1569元,读取速度可达250MB/s,闪迪推出最新2TB至尊超极速存储卡

近日,闪迪(SanDisk)正式发布了其最新的2TB至尊超极速microSDXCUHS-I存储卡。据悉,这款存储卡的读取速度可达250MB/s,写入速度则达到150MB/s。这意味着用户在处理高分辨...

取消回复欢迎 发表评论: