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

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

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

相关推荐

Protel电路设计常用设计编辑器案例2——创建元件

#大有学问#今天介绍一下Protel常用设计编辑器的电气连接工具栏。单击主工具栏上的工具按钮或选择【查看】|【工具栏】|【配线工具栏】菜单命令可以关闭或打开【电气连接(WiringTools)】工具...

Protel调整元器件的位置(1)——移动和对齐元器件

今天介绍调整元器件位置的方法。首先介绍移动和对齐元器件的方法。在绘制电路原理图时,放置完了的电路图可能位置不太合适,需要进行移动。原理图中的所有对象都可以被移动,移动方法相似。对于元器件的移动来说又分...

电路仿真软件详谈(八),proteus电路仿真软件和protel的区别

电路仿真软件是常用工具类型之一,proteus更是电路仿真软件中的佼佼者。但是对于proteus电路仿真软件和protel,二者总是被弄混淆。例如,protel是电路仿真软件吗?proteus电路仿真...

PCB文件转换生产文件Protel 99SE_pcb格式转换

为何要将PCB文件转换为GERBER文件和钻孔数据?因为GERBER文件是一种国际标准的光绘格式文件,它包含RS-274-D和RS-274-X两种格式,其中RS-274-D称为基本GERBER格式,并...

PCB设计项目教程 -PDF_pcb设计作品

PCB设计项目教程》及相关PCB设计教材详细介绍:一、核心教材《PCB设计项目教程》该教材由徐凯、王威担任主编,于2017年由北京理工大学出版社出版。其采用“项目导向、任务驱动”的教学模式,...

最受欢迎的pcb设计软件Protel99se到底怎么样?

Protel99se是一款国内非常实用且流行的设计行业的pcb设计软件,其由pcb原理图设计和多层板电路设计两大功能组成,其最大的特点是好获取,在网上可以随便的找到,且Protel99se软件适用于w...

人人都是网络雇佣兵,一种基于路由器的ddos平台设计思路

本文灵感来自于三个方面优酷路由宝,迅雷宝这种路由器流量兑现方式Anonymous匿名者的ddos方式传统木马ddos方案先说路由宝迅雷宝,这种以用户网络为节点的CDN网络中,会传输大量的流量,我上月优...

接口性能测试工具Locust介绍_接口和性能的测试要点

接口性能测试工具其实挺多的,小型有apache的ab工具,大型的有Jmeter、Locust......这里要介绍的是Locust,相对于Jmeter进行了比较完善的封装,Locust可以就显的更自...

华硕笔记本电脑安装系统实战心得体会

故障:某某的电脑叫人安装系统至一半就蓝屏死机.拿来给我安装,发现光驱无效,不能用光盘安装.电脑启动蓝屏.解决方法:用了半天时间安装也出现类似问题.后来考虑用U盘来装.1.首先制作U盘系统,把U盘资...

dos命令systeminfo图文教程,显示操作系统配置信息msinfo32

大家好,我是老盖,首先感谢观看本文,本篇文章做的有视频,视频讲述的比较详细,也可以看我发布的视频。今天我们学习systeminfo命令,该工具显示本地或远程机器(包括服务包级别)的操作系统配置的信息,...

玩家展示现代硬件上运行的MS-DOS 拥有令人难以置信的向后兼容性

一位YouTuber展示了在现代计算机硬件上直接运行古老的MS-DOS操作系统和经典游戏的能力。这段视频由YouTuberInkbox发布,向观众展示了如何启动古老的、前Windows...

比微PE还干净还强大,带网络:USBOS V3.0超级PE装机工具20221031

期待已久的USBOSV3.0超级PE装机工具20221031又和大家见面了,用过的朋友都知道他的确很强大,对于新旧电脑的支持很好,目前为止还没有电脑不支持的,包括苹果PC。很多朋友还在用诸如大白菜、...

大童保险李晓婧:保险的本位是风险管理应在四方面进行建设

经济观察网记者姜鑫5月17日,大童保险服务宣布升级了风险管理模式,推出DOSM(DemandOriented,SolutionModel)需求导向型解决方案5.0版本。新解决方案从原有“六位...

DOS常用命令及简介_dos常用命令大全及用法

DOS是英文DiskOperatingSystem的缩写,意思是“磁盘操作系统”。我是在95年开始学的电脑,当时学校的机房里,还没有一台WINDOWS操作系统的电脑,当时都是用DOS、UC-DOS...

Windows 忘记开机密码?不用任何工具,1招轻松破解

出现忘记Windows密码的情况,概率有多大?对此,小电只能回答忘记开机密码的情况,说来就来,没有规律,也不会提前告诉你一声~而忘记Windows开机密码的时候,很多朋友都会想起可以使用u盘启动盘来破...

取消回复欢迎 发表评论: