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

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

cac55 2024-11-13 09:34 19 浏览 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了。

相关推荐

Mac电脑强制删除任何软件方法-含自启动应用

对于打工者来说,进入企业上班使用的电脑大概率是会被监控起来,比如各种流行的数据防泄漏DLP,奇安信天擎,甚至360安全卫士,这些安全软件你想卸载是非常困难的,甚至卸载后它自己又安装回来了,并且还在你不...

Linux基础知识 | 文件与目录大全讲解

1.linux文件权限与目录配置1.文件属性Linux一般将文件可存取的身份分为三个类别,分别是owner/group/others,且三种身份各read/write/execute等权限文...

文件保护不妥协:2025 年 10 款顶级加密工具推荐

数据安全无小事,2025年这10款加密工具凭借独特功能脱颖而出,从个人到企业场景全覆盖,第一款为Ping32,其余为国外英文软件。1.Ping32企业级加密核心工具,支持200+文件格...

省心省力 一个软件搞定系统维护_省心安装在哪里能找到

◆系统类似于我们居住的房间,需要经常打理才能保持清洁、高效。虽然它本身也自带一些清理和优化的工具,但借助于好用的第三方工具来执行这方面的任务,会更让人省心省力。下面笔者就为大家介绍一款集多项功能于一身...

JAVA程序员常用的几个工具类_java程序员一般用什么软件写程序

好的工具做起事来常常事半功倍,下面介绍几个开发中常用到的工具类,收藏一下,也许后面真的会用到。字符串处理:org.apache.commons.lang.StringUtilsisBlank(Char...

手工解决Windows10的若干难题_windows10系统卡顿怎么解决

【电脑报在线】很多朋友已经开始使用Win10,估计还只是测试版本的原因,使用过程中难免会出现一些问题,这里介绍解决一些解决难题的技巧。技巧1:让ProjectSpartan“重归正途”从10074...

System32文件夹千万不能删除,看完这篇你就知道为什么了

C:\Windows\System32目录是Windows操作系统的关键部分,重要的系统文件存储在该目录中。网上的一些恶作剧者可能会告诉你删除它,但你不应该尝试去操作,如果你尝试的话,我们会告诉你会发...

Windows.old 文件夹:系统备份的解析与安全删除指南

Windows.old是Windows系统升级(如Win10升Win11)或重装时,系统自动在C盘创建的备份文件夹,其核心作用是保留旧系统的文件、程序与配置,为“回退旧系统”提供保...

遇到疑难杂症?Windows 10回收站问题巧解决

回收站是Windows10的一个重要组件。然而,我们在使用过程中,可能会遇到一些问题。例如,不论回收站里有没有文件,都显示同一个图标,让人无法判别回收站的空和满的真实情况;没有了像Windows7...

卸载软件怎么彻底删掉?简单几个步骤彻底卸载,电脑小白看过来

日常工作学习生活中,我们需要在安装一些软件程序,但随着软件的更新迭代速度,很多时候我们需要重新下载安装新的程序,这时就需要将旧的一些软件程序进行卸载。但是卸载软件虽然很简单,但是很多小伙伴们表示卸载不...

用不上就删!如何完全卸载OneDrive?

作为Windows10自带的云盘,OneDrive为资料的自动备份和同步提供了方便。然而,从隐私或其他方面考虑,有些人不愿意使用OneDrive。但Windows10本身不提供直接卸载OneDri...

【Linux知识】Linux下快速删除大量文件/文件夹方法

在Linux下,如果需要快速删除大量文件或文件夹,可以使用如下方法:使用rm命令删除文件:可以使用rm命令删除文件,例如:rm-rf/path/to/directory/*这个命令会递...

清理系统不用第三方工具_清理系统垃圾用什么软件

清理优化系统一定要借助于优化工具吗?其实,手动优化系统也没有那么神秘,掌握了方法和技巧,系统清理也是一件简单和随心的事。一方面要为每一个可能产生累赘的文件找到清理的方法,另一方面要寻找能够提高工作效率...

系统小技巧:软件卸载不了?这里办法多

在正常情况下,我们都是通过软件程序组中的卸载图标,或利用控制面板中的“程序和功能”模块来卸载软件的。但有时,我们也会发现利用卸载图标无法卸载软件或者卸载图标干脆丢失找不到了,甚至控制面板中卸载软件的功...

麒麟系统无法删除文件夹_麒麟系统删除文件权限不够

删除文件夹方法例:sudorm-rf文件夹名称。删除文件方法例:sudorm-r文件名包括扩展名。如果没有权限,给文件夹加一下权限再删。加最高权限chmod775文件名加可执行权限...

取消回复欢迎 发表评论: