Project1

标题: 新手教程--从0开始学RGSS2(2013-09-21 修复索引地址) [打印本页]

作者: 八云紫    时间: 2010-11-1 14:39
标题: 新手教程--从0开始学RGSS2(2013-09-21 修复索引地址)
本帖最后由 八云紫 于 2014-1-21 13:59 编辑

前言:

1. 好吧, 渣教程, 不过还是禁止转载, 谢谢. ]

2. 如果有发现什么错误或者有更好的建议, 请跟帖说明. 当然啦, 有个目录就不怕帖子混乱了~~~ 不过其实已经混乱了~~

3. 本教程的教学方向在于 RGSS2 . 对于 RGSS3 本人没有过多的接触和了解. 所以一些细节东西等可能和 RGSS3 并不相同, 所以不具有对 RGSS3 的说明作用.

目录:

初级篇(RGSS2 基础):
01. 写在前面
02. RGSS2 与 Ruby
03. 第一个脚本
04. 变量 常量
05. 运算
06. 复合运算
07. 字符串(String)
08. 循环
09. 函数(方法)
10. 条件分歧(判断)
11. 数组(Array)
12. 哈希(Hash)
13. 类(class) by 7姐
14. 补充

中级篇(RGSS2 内部类解读):
01. Viewport 显示端口
02. Bitmap 位图 by david50407
03. Sprite 精灵 和 Plane 平面
04. Window 窗口
05. Color 色彩 和 Tone 色调
06. Tilemap 元件地图 和 图块
07. Rect 矩形
08. Font 字型 和 Table 表格
09. RGSSError RGSS错误 by ZH99998

中级篇(RPG VX解读):
01. RPG 模块
02. Game 类集
03. Window 类集
04. Sprite 和 Spriteset 类集
05. Scene 类集
06. Main
07. 自定义脚本
      ☆ 基本设计原则

高级篇:
01. 正则表达式
02. API
03. pack and unpack and API  <--What's new !

附录:
01. 关键字解析

实战篇:
01. 修改存档位置
02. Game 类应用<< 技能书脚本>>

作者: koyonuji    时间: 2010-11-1 14:57
翻天360度支持
作者: 红灯    时间: 2010-11-1 16:12
希望继续下去,可别坑了啊~好多脚本教程出了一半就没下文了。严重支持!
作者: bsyjfc    时间: 2010-11-1 16:24
提示: 作者被禁止或删除 内容自动屏蔽
作者: 冰舞蝶恋    时间: 2010-11-1 17:15
好东西,不顶不行!内啥,八云单独教教我吧,脚本白痴哭泣中,完全看不懂嘛~唉,果真是菜鸟啊。。。
作者: 狠、陌生    时间: 2010-11-1 17:17
提示: 作者被禁止或删除 内容自动屏蔽
作者: goahead    时间: 2010-11-1 17:24
提示: 作者被禁止或删除 内容自动屏蔽
作者: 壬穹雷光    时间: 2010-11-1 18:29
后排支持!!!!!!!!
作者: 511139511    时间: 2010-11-1 18:50
很希望讲一讲RGSS2特有的一些地方。
以及class def 。。。等等的用法。。。支持教程!
作者: summer92    时间: 2010-11-1 20:41
支持helloworld
作者: 八云紫    时间: 2010-11-1 21:21
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-4 23:48 编辑

变量 常量

       啰嗦一下, 如果大家想直接测试脚本, 比如之前的 Hello World , 点确定后不进入标题的话, 可以在脚本后添加一句:
  1. exit
复制代码
即可.
      进入正题吧.

      变量是在脚本里使用最多的东东了. 从字面上说变量就是会变的量. 你可以往里面放任何东东(在 Ruby 里是这样的). 可以这样比喻吧: 变量就是一个抽屉, 你可以放书, 也可以放铅笔, 都放也是可以的. 你可以在需要的地方从这个抽屉里拿出里面的东东.
      知道了这个 "抽屉" 的功能, 那么怎么去识别它呢? 你不能对着 RGSS2 说:" 那个那个抽屉里放书." 所以需要一个明确的标示符, 也可以叫名字(变量名) 来区分它们, 就像大家都有一个名字一样.

      变量名的命名规则
      知道了需要用一个名字来区分它们, 那么这个名字也需要遵守一定的规则. 想我们的名字一样, 先姓氏, 然后名字.  脚本里的变量名命名规则要遵守这三个规则:
     
1. 第一个字符必须以小写英文字母或 _(下划线)开头。
      2. 第二个字符开始可以使用英文字母、数字或 _(下划线)。
      3. 不能使用保留字作变量的名称。
      保留字是作为 Ruby 中具有特殊意义的词而被系统所「保留」,所以变量名称不能使用。以下就是系统的保留字。
     alias    def      false    nil      return   unless
     and      do       for      not      self     until
     begin    else     if       or       super    when
     break    elsif    in       redo     then     while
     case     end      module   rescue   true     yield
     class    ensure   next     retry    undef

     以上抄至 F1 帮助.
     其实在 RGSS2 里可以使用中文来命名变量. 不过个人感觉不要这么做为好.
     于是举几个例子吧:
     这些都是正确的: a , aa, aaa( = =a ), _times_ , __rgss2 ....
     这些都是错误的: 2rgss(不能数字开头), class (保留字).
     
     变量的使用
     知道如何标识一个变量后, 就可以大张旗鼓的使用它们了. 之前就说过了, 我们可以往里面放书, 也可以从里面拿书(前提是里面有). RGSS2 里对于 "放书" 这个动作叫做赋值. 于是, 我们就可以这样往一个变量的抽屉里放一本书:
  1. text = "<66RPG 使用指南>"
复制代码
text 就是之前所说的变量名字, 这个变量叫做 text , 以后想叫它的话, 直接叫 text 就好. "<66RPG 使用指南>" 书名(内容请无视). 一整句的意思就是 将 "<66RPG 使用指南>" 这个书 放进变量名是 text 的变量里去. 或者 将变量 text 赋值, 内容是 "<66RPG 使用指南>" (好吧, 很绕口).

     懂的放书后, 取书就易如反掌了. 还记得 "Hello World" 例子里的那个 p 方法么? 它的用作是输出内容. 那么, 我们就可以这样做:
  1. text = "<66RPG 使用指南>"
  2. p text
  3. exit
复制代码
变量的类型
     其实抽屉也是有分类的. 有私人的抽屉, 放日记的.(是谁都不希望日记和其他人共享吧.); 还有就是类似图书馆的书架, 谁都可以从上面拿书. 于是, 变量就可以这样分类了:
     1. 全局变量(就是 图书馆的书架), 在任何地方都可以使用的变量,  $ 加上变量名 作为新名字.
     2. 类变量(你家的抽屉, 你和你的家人都可以使用), 在 类 里可以使用, @ 加上变量名 作为新名字.
     3. 局部变量(你房间里放日记的抽屉), 只有在一个特定的地方才可以使用它, 前面什么都不加, 直接 变量名.

     如果你一时间记不住这些的话也没啥关系, 以后会慢慢的说明的.

       常量 就是不会改变的变量. 常量的名字全部都是由大写字母组成, 比如 WHL 等, 这些例子在一些脚本的设定部分尤为常见. 请不要试图去改变一个常量的值, 虽然这个在 RGSS2 里是可以做到的.
       另外, 常量默认在任何地方都可以使用的. 例如 π (小光) , 我们谁都可以使用它来计算圆面积.

作者: 不是马甲    时间: 2010-11-1 21:49
沙发  慢慢看
作者: 38571240    时间: 2010-11-1 21:56
不错。
作者: DeathKing    时间: 2010-11-1 22:29
回复 铃仙·优昙华院·因幡 的帖子

紫大人可以考虑出个DOC版,然后RDeveloper无限连载下去……
作者: 高须小龙    时间: 2010-11-2 00:02
密切关注。
作者: saturnfjh    时间: 2010-11-2 08:45
话说很多东西F1看得到,建议要学脚本的先自己看F1,铃仙姐姐讲一讲各种方法的运用(结合RMVX)也许会比较好,也能省下不少精力的说
作者: 精灵使者    时间: 2010-11-2 10:34
我打算想放到技术发布区……
作者: zh99998    时间: 2010-11-2 11:09
后排支持主人~
作者: 小角色    时间: 2010-11-2 11:14
进来做个标记(回滚)~
作者: 夕阳武士    时间: 2010-11-2 12:57
我也跟着学~
作者: 八云紫    时间: 2010-11-2 17:14
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-2 19:35 编辑


运算


      在说运算之前, 先聊聊数值. RGSS2 里的数值分为两种, 整数(或者 整型) 和 浮点数(带小数的数值). 整数 就是不带小数的数值, 比如 1, 10, 65535 等. 浮点数说直接点就是带有 小数点 的数值, 所以  1.0 , 3.141592 等都属于浮点数. 最后要说的是, 整数在计算上要比浮点数来的快捷, so 能用整数的尽量使用整数吧.

      运算 算是脚本的核心吧. 所有的一切都离不开运算, 图片的移动, 变量的增减都是需要计算. 加减乘除四则运算都没啥必要说明了吧. 举个例子:
  1. p 1 + 1             # 加法
  2. p 10 - 3            # 减法
  3. p 7 * 5             # 乘法
  4. p 25 / 4            # 除法
  5. p 7.0 * 5             # 乘法
  6. p 25.0 / 4            # 除法
复制代码
是不是有个疑问? 为什么 25 / 4 的结果是 6 而不是 6.25 ? 这里需要说明下, RGSS2 在运算的时候会保留数值类型, 只要参与运算的两个数值中, 至少有一个是浮点数的话, 那么结果就会是浮点类型. 由于 25/4 是 整数/整数, 呢吗结果就是整数, 但是整数是没有小数位的, so 就是 6 了. 最后要注意的是, RGSS2 可不会四舍五入, 它的做法是都去掉.
      问题 : 如果执意要四舍五入呢? 怎么办?
      最后的最后, 加减乘除符合数学上的运算优先级, 即 先乘除后加减, 如果需要的话, 请使用 "( )",如: 10.0 * (1 \ (10.0 - 5)

      除了加减乘除外, RGSS2 还有其他的基础运算, 比如 模(也就是 求余), 符号是 "%", 例如:
  1. p 25 % 4
  2. p 25.0 % 4
复制代码
还有一个就是 乘方, 符号是 "**" , 两个乘号. 想计算 3 的 3 乘方的话, 就是
  1. p 3 ** 3
复制代码
结果是 ⑨ 哦. 其实是 27 .

     赋值 的使用也是很普遍的, 这个之前说明过了. 也就没说好重复了.

复合运算


     如果把这些运算组合在一起就是复合运算. 常用的有 "+=" "-=" "*=" "/=" "%=" . 例如:
  1. a = 10
  2. a += 1
  3. p a
复制代码
先把 10 赋值进 变量 a 里面进, 然后 a += 1 , 就是 a 自己加上 1 , 这句等价于  a = a + 1 . 其他的 "-=" "*=" "/=" "%=" 类似就偷懒一下吧.
作者: 八云紫    时间: 2010-11-2 19:30
写在前面


致新人们:
       脚本的难易度其实是看你们的努力,  兴趣,  耐心等方面来决定的.  当然, 如果之前有学过其他的编程语言的话,  事半功倍也是常有的事情.
       本教程面向零基础新人, 咱会一点点的来慢慢解说 RGSS2 的. 如果您使用的是 RGSS(也就是 XP)的话, 也可以来看看, 它们在基本的语法啦什么的, 基本上都是一样的. 不一样的是默认脚本以及一些方法.
       所以, 有耐心的话, 请慢慢往下看吧.

致脚本大大们:
       咱知道咱在这里是有点班门弄斧了. 请你们嘴下留情. 如果有什么错误或者建议什么的,  请指正.

致大家:
      你们的支持就是咱的动力. 不要介意咱的某些语病或者表达问题, 咱语文不是很在行, 不过尽量使用最通俗的语言来表达意思.

最后:
      这个算是全文字或者有图片的教程了吧. 求视频教程的, 请出门右拐, 谢谢~~~

最后的最后:
      不定期更新~~~~ (其实是自己有一个游戏坑要填埋, 所以, 见谅).
      这个是无聊啦, 没劲啦什么的教程
RGSS2 与 Ruby

      
     说到 RGSS2, 应该要先说说 Ruby.
     Ruby 的具体信息, 请查看 百度百科 (偷懒~). 一句话概括的话, 就是: Ruby 是一个面向对象的脚本语言. 就这么简单.
     知道 Ruby 后,就可以来说说 RGSS2 了. RGSS2 其实是在 Ruby 语言的基础上衍生来的一个脚本库. 所以, 语法上有共通的地方.

第一个脚本


       好吧,  以上基本上都是一些废话. 正文从这里开始.
       想要学写脚本, 当然是从基础的语句开始啦. 那么就国际惯例, 从 Hello World 开始吧.
  1. p "Hello World"
复制代码
恩, 就这么简单.
       具体的解释一下吧, 首先来看第一个单词 "p" . p 方法(也有叫做 函数) 的用法就是输出后面的信息. "Hello World" 呢, 就是需要输出的字符串. 在 RGSS2 或者说是 Ruby 里 , 使用 " " (双引号) 包含的内容都是字符串.
       那么, 怎么使用以上脚本呢? 要么, 就需要准备 RPG VX 一只, 新建一个新工程(具体流程就偷懒了~~), 按 F11 打开脚本编辑器. 然后把这边的滚动条向下拉, 就会看到一个 Main 脚本, 就是这个:

       然后再 Main 之前点右键->插入(Ins) 一个新的空白脚本, 将以上的脚本写进去, 就是这样子:

       请注意一下那个 "Hello World" 的颜色了, 如果不是红色的话, 请检查一下有没有写错.
       最后是应用->确定->F12 就可以看到结果了. 如果不出错的话, 应该是这样:

      点确定后就来到熟悉的标题界面了.
作者: 八云紫    时间: 2010-11-2 20:42
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-9 07:04 编辑

字符串


      好早好早之前就使用了这么一个例子  p "Hello World" 其中的 "Hello World" 指的就是字符串.
      字符串就是由零个或多个字符组成的有限序列. 字符是什么就不需要解释了吧.
      在 RGSS2 里, 使用 "" (双引号) 来表示里面的是一个字符串, 例如 "Hello World" 什么的.

      字符串其实也可以参与计算的, 不过只限于 加 和 乘. 字符串的加法就是两个字符串连接, 例如:
  1. p "Hello World" + " Reisen"
复制代码
输出就是 "Hello World Reisen" .
      乘法在数学上就是连续的加, 所以 乘法可以将字符串复制几次. 例如:
  1. p "Hello" * 5
复制代码
这样就出现了5个 Hello 了.

     "\" 这个符号在 字符串里有一个特殊的作用, 那就是转意. 常用的有 "\n" (换行) "\t"(制表符, 也就是键盘上的那个 Tab键) "\s" (空格) 和 "\0" (空, 什么也不放, 可是一个字符串的结束标志, 和句子的结束标志句号一样) "\\" 就是 "\"
举个例子:
  1. print "Hello World \n" * 2
  2. print 'Hello World \n' * 2
  3. p "Hello World \n" * 2
  4. print "Hello\sWorld"
  5. print "Hello\\World"
  6. print "Hello\tWorld"
  7. print "Hello\0World"
复制代码
注意1: 这里使用了一个新的方法 print . 使用它来输出字符串. 如果使用 p 来输出的话, 那些转意字符就会被原样输出.
       注意2: 看到第二个语句了么? 这里使用了 ' '(单引号) 而不是双引号来声明字符串. 使用 单引号的作用在于它使得里面的转意字符会被当做原样处理, 就是说, 什么也不做.
       注意3: 最后一个字符串不要理所当然的以为输出的是 "Hello World", "\0" 不是空格的说. 另外, 每个字符串都是以 \0 结束的.只是没有显示出来. 所以, 这里只输出了 "Hello" 后面的内容都被忽略了.

       内嵌表达式 也是字符串的一个特性. 有的时候我们需要动态的生成一个字符串, 而不是在写脚本的时候去直接指定它们. 比如 VX 里的数值输入. 因为我们不可预见玩家会输入什么数值. 所以需要使用内嵌表达式. 惯例, 一个例子:
  1. game = "VX"
  2. print "RPG #{game}"      
  3. game = "XP"
  4. print "RPG #{game}"
复制代码
这样就可以取得一个动态的字符串了.
       里面的 #{ } 用来将一个 变量 或者语句的结果 直接嵌入到字符串中. 同样的, 如果声明的字符串使用 ' ' 单引号的话, 内嵌表达式 也是无效的.
另一篇教程 : http://rpg.blue/forum.php?mod=vi ... &fromuid=107872
作者: 八云紫    时间: 2010-11-3 12:18
一晚上和一大早上的看帖两只有小小的 7 个. 悲剧~~~
作者: 夕阳武士    时间: 2010-11-3 13:01
回沉影前辈:
与"管理层"无关,我们只是一个自由成立的教程社团.
作者: 八云紫    时间: 2010-11-3 18:53
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-3 19:39 编辑

函数(方法)

      
      先来说说函数(或者叫方法)吧. 可以这么说吧, 函数就是一些具有特定功能的语句的组合.  通俗的认为, 函数就类似一个加工厂一样, 我们提供给它原料, 那么它在不出错的前提下就会完成某种功能, 然后可能返回一个成品给我们.

      RGSS2 中, 这样定义一个函数:
  1. def 函数名[(参数列表)]
  2.    函数体
  3.    [return ...]
  4. end
复制代码
def 属于关键字, 表示定义函数的开始. 函数名遵守于变量名一样的规则. 后面的 参数列表 表示这个函数工厂需要什么原料. 特别的, 当函数不需要这个参数列表的时候, 可以连着 "( )" 一起不写. 函数体里写这个函数的实现过程, 例如工厂是怎么依次处理原料的. return 是 这个函数的返回关键字, 表示这个函数返回一个什么成果给我们, 也就是 返回值 了. end 与 def 对应, 不能少.

     举个例子吧,
  1. def add(x, y)
  2.    return x + y
  3. end
复制代码
这个叫做 add 的函数需要两个原料, 分别是 x, y . 函数的功能是将两个原料相加, 返回相加后的结果. 值得注意的是 参数列表使用 "," 逗号来分隔每个需要的参数.

     定义完函数后, 就可以使用它们了. 使用方法非常简单, 直接 函数名(需要的参数) 即可. 如果函数有返回值的话, RGSS2 执行好这个函数后, 就会返回那个返回值. 依旧是上一个例子:
  1. a = add(1, 2)
  2. p a
复制代码
不过要注意的是, 函数后的参数列表的个数一定要和定义的时候的个数一样. 否则就会出现错误.

     什么情况下可以使用函数? 这个估计会多人都会问到. 一口气把函数里的内容全部都柔和在一起不是更好?
     函数的使用可以减少一些不必要的麻烦. 比如 某个功能 需要在多个地方执行多次, 但是实现的脚本都是一样的话, 使用函数是最好的办法. 这样如果需要修改的话, 不需要一个地方一个地方的去找, 然后修改. 这样写脚本的效率就会提高很多. 虽然使用函数会照成额外的脚本开销.

     那么, 我们继续吧. 来说说参数. 其实参数可以在定义的时候直接指定一个默认值, 这样的调用上就可以忽略这个参数了. 例如
  1. def add(x, y = 1)
  2.    return x + y
  3. end
  4. p add(1, 1)
  5. p add(1)
  6. p add(1, 2)
复制代码
可以看到两个函数的返回值都是一样的. 所以, 在使用函数的时候, 如果忽略某一个默认参数, 那么脚本就会带入设定好的值, 比如这个例子的 y.
      不过主要注意的话, 默认参数只能定义在最后. 也就是说, 默认参数的后面只能是默认参数. add(x , y = 1, z) 这么定义是错误的.

    函数是可以重定义. 这个是外带脚本的编写原理. 两个同名(相同的函数名)的函数在相同的定义环境下, 后定义的函数会取代前一个函数, 多个的情况也是类似.
  1. def add(x, y)
  2.    return x + y
  3. end
  4. p add(1, 1)

  5. def add(x, y)
  6.   return x - y
  7. end
  8. p add(1, 1)
复制代码
可以看到第一个定义的 add 被覆盖掉了.

      在写外带脚本的时候, 需要保留前一个, 也就是默认的脚本的功能的话, 可以使用 alias .用法如下 :
  1. alias 新函数名 旧函数名
复制代码
这里含义是, 给旧函数名取一个别名, 小名什么的. 在后面就可以使用这个别名来表示这个函数. 例如:
  1. def show
  2.    p 1
  3. end
  4. show
  5. #
  6. alias old_show show
  7. def show
  8.    old_show
  9.    p 2
  10. end
  11. show
复制代码
第6句就是给第一个函数show取别名, 这样在后面重定义这个函数的时候, 就不会直接影响到原来的函数了.
写外带的脚本的时候, 使用 alias 是一个好习惯.
作者: Rachel_Alucard    时间: 2010-11-4 20:07
提示: 作者被禁止或删除 内容自动屏蔽
作者: Rachel_Alucard    时间: 2010-11-4 20:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: Rachel_Alucard    时间: 2010-11-4 23:47
提示: 作者被禁止或删除 内容自动屏蔽
作者: Rachel_Alucard    时间: 2010-11-5 19:46
提示: 作者被禁止或删除 内容自动屏蔽
作者: 六祈    时间: 2010-11-6 00:20
本帖最后由 六祈 于 2010-11-6 00:26 编辑

【酱油括号】
嘛,关于类的部分由愚者来写了
以前有人问,67你是RGSS党还是2党?你用的VX一定是2吧?
好吧,愚者绝对不是2,愚者其实是Ruby党,所以诸君看这篇教程的时候一定要小心
普通字色部分是初级【如果你是想学点RGSS2基础】
红色部分为高危Ruby部分【适合已经有基础的同学们】
【酱油完毕】
类:一个抽象的概念,举个例子,张三是人【类】,李四是人【类】,但是人这个【类】,在这个世界上是找不到一个对象来表达的

在Ruby中,定义一个类用class,如下:
  1. class Person
  2. end
复制代码
然后就可以创建张三和李四了~
  1. z3 = Person.new
  2. l4 = Person.new
复制代码
咦,为啥张三李四啥都没有…那咱们开始添加姓名和年龄这些属性吧
  1. class Person
  2. attr_accessor :name, :age
  3. def initialize(name,age)
  4. @name = name
  5. @age = age
  6. end
  7. end
复制代码
解说:
首先attr_accessor :name这行,attr_accessor是类方法,参数为一个或多个符号,加了这句就可以通过z3.name读取@name变量,用z3.name = xxx来设置@name变量了
其次def initialize(name,age)这行,initialize是Person.new时调用,里面放置一些对象的基础设置【initialize被称作钩子方法,类似的还有inherited方法在类被继承时调用】
第三点@name = name这行,@name是实例变量(为什么叫实例变量?因为是实例所拥有的,亦即z3和l4各自有一个@name而且不冲突),而name是通过参数传递进来的局部变量。

咱们重新创建张三和李四【这俩货一直在投胎么…大雾】
  1. z3 = Person.new('张三',20)
  2. l4 = Person.new('李四',18)
复制代码
喵,创建完毕~来试一试~
  1. p “那年#{l4.name}#{l4.age}岁,#{z3.name}#{z3.age}岁,他们相爱了”
复制代码
如果没出意外的话,会出来一段很狗血的剧情:【白字慎重】
那年李四18岁,张三20岁,他们相爱了
什么!你看不懂#{}???往回翻前面的教程吧……XD自重

咦,张三和李四怎么光有名字和年龄啊,你们俩还会干点别的么?
于是咱们得给Person类定义方法
  1. class Person
  2. def do_sth
  3. p '干!'
  4. end
  5. end
  6. z3.do_sth   #'干!'
复制代码
好吧这回张三会骂街了…大家以后可以创建一打的张三李四帮你去骂人哟~【大雾……】
当然你也可以培育他们去做很多的事情内个。。。【说多了】
[line]2[/line]
以下开始大规模红字部分,大家慎重…
首先说变量:
实例变量:以@开头,由实例对象所拥有
类变量:以@@开头,【愚者以为叫做共享变量会更可靠一点】意指由一个类产生的所有实例所共享的变量,举个例子,地球上的所有生物都拥有同一个地球~而地球却只有一个,大家都要爱护呀~
全局变量:以$开头,这个在RGSS2里遍地都是,比如$game_party啊$game_variables啊等等等

类也是对象:在Ruby中一切皆为对象,所以类也是对象,是Class类的对象,比如A = Class.new,然后使用A.new就可以产生A的对象了~【请使用常量命名类或者模块】
那么类既然是对象,有没有方法和实例变量?答案是有的:
  1. class A
  2. def self.output
  3. p '我是小A不是小三'
  4. @a = 1 if @a == nil
  5. p (@a+=1)
  6. end
  7. end
  8. A.output
复制代码
这里用def self.output定义的就是类方法【也可以使用def A.output,如果这样写的话注意不要写错类名】
类方法与类变量不同,并不是共享的,而是类作为对象时的方法。
而在类方法内的@xxx变量即是类作为对象的实例变量


然后说继承
在RGSS2脚本中大量的使用了类的继承
比如Window_Command是继承Window_Selectable,而Window_Selectable则是继承Window_Base
继承链为Window_Command < Window_Selectable < Window_Base【这里不是RGSS2代码,请勿在编辑器里这样写…后果自负】
这样的好处是,如果几个类同时要用同样的一个方法【代码相同】,那么只要在它们的父类里定义即可~【子类在找不到方法时,会从继承链上回溯查找方法定义】

关于super:
在方法里写上super会调用继承链上一级的同名方法【如果没有继续回溯直到找到第一个为止】
而super有两种写法:
super【不带括号】:会将参数传递给父类的同名方法
super()【带括号】:则参数必须手动传递

关于Module的混入改变继承链:
  1. class Class_B
  2. def initiazlie
  3. p 'B'
  4. end
  5. end
  6. class Class_A < Class_B
  7. def initialize
  8. super
  9. p 'A'
  10. end
  11. end
复制代码
这时候继承链为Class_A < Class_B,输入Class_A.new会依次输出'B','A'
  1. module A
  2. def initialize
  3. super
  4. p 'module A'
  5. end
  6. end
  7. module B
  8. def initialize
  9. super
  10. p 'module B'
  11. end
  12. end
  13. class Class_A
  14. include A
  15. include B
  16. end
复制代码
这时的继承链为Class_A < B < A < Class_B【混入的Module会插入继承链,并且后混入的Module更靠近子类】
这时输入Class_A.new会依次输出'A','module A','module B','B'【很神奇不是么~】

作者: yoo    时间: 2010-11-6 05:02
看的头都大了。。。不容易啊这东西。
作者: 壬穹雷光    时间: 2010-11-6 17:59
回复 Rachel_Alucard 的帖子

八云大大可以说明一下哈希是什么吗?有什么作用?
作者: 八云紫    时间: 2010-11-6 19:23
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-6 19:25 编辑

回复 壬穹雷光 的帖子

哈希表依靠的其实是类似函数的映射关系.  某个 Key 通过某种运算 f(Key) , 可以映射到唯一的一个 Value 上. 如果不能的话, 需要将这个 hash 进行重排, 消去冲突.

另外一个就是哈希算法, 把任意长度的输入通过算法, 变换成固定长度的输出.


作用的话, 就是类似 数组 了. 数组的 映射关系是  偏移序号 => 值 .  这样其实可以看成是 特殊的 hash 表.

作者: Rachel_Alucard    时间: 2010-11-6 21:33
提示: 作者被禁止或删除 内容自动屏蔽
作者: 八云紫    时间: 2010-11-7 22:19
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-7 23:04 编辑

RPG 模块

      RPG 模块 只在 *.rvdata 这些数据库文件里使用过. 具体的每个子模块的属性什么的, 都可以查看 F1 帮助电子档. 这里就不细说了.
      这里具体先说说一个主意事项. 就是在 VX 里 , RPG 模块的所有 initialize 初始化方法都是无效的. 为什么? 看脚本的 Scene_Title 就知道了. 脚本里事先读取了 *.rvdata 文件. 所以, 脚本里使用的 $data_XXX 变量都是事先声明好的. so , 脚本里是不会调用 initialize 方法的. 那么? 怎么有效的添加一个属性呢?? 其实可以在那个属性的读取方法上的做文章. 比如
  1. def cp
  2.    @cp = 10  if @cp == nil
  3.    return @cp
  4. end
复制代码
这样就可以防止出现 NoMethodError .

各个文件对应的资料:


文件名                           数据类型 对应的 RPG 模块                   变量类型                     对应脚本里的变量


Actors.rvdata                 角色数据  RPG::Actor .                         数组                                 $data_actors


Classes.rvdata                职业数据  RPG::Class .                          数组                                 $data_classes


Skills.rvdata                    技能数据  RPG::Skill .                            数组                                 $data_skills


Items.rvdata                   物品数据  RPG::Item .                          数组                                 $data_items


Weapons.rvdata             武器数据  RPG::Weapon .                    数组                                $data_weapons


Armors.rvdata                防具数据  RPG::Armor .                        数组                                $data_armors


Enemies.rvdata               敌人数据  RPG::Enemy.                       数组                                $data_enemies


Troops.rvdata                 敌人队伍数据   RPG::Troop.                 数组                               $data_troops


States.rvdata                  状态数据   RPG::State.                         数组                                $data_states


Animations.rvdata           动画数据   RPG::Animation.                  数组                                $data_animations


CommonEvents.rvdata   公共事件数据   RPG::CommonEvent.   数组                                $data_common_events


System.rvdata                系统数据   RPG::System.                      单个类实例                      $data_system


Areas.rvdata                   区域数据   RPG::Area.                           Hash                                $data_areas


MapXXX.rvdata               地图数据 RPG::Map.                             单个类实例                       $game_map.map


MapInfos.rvdata             地图信息数据,  RPG::MapInfo.            Hash                                 需手动加载


Scripts.rvdata                 脚本文件 数组 对应三个值 [可能是ID, 脚本名字, 脚本内容]          $RGSS_SCRIPTS



作者: 八云紫    时间: 2010-11-8 18:12
本帖最后由 铃仙·优昙华院·因幡 于 2010-11-8 19:20 编辑

Game 类集

      VX 里处理所有游戏数据的类集合. 本身 Game 类的写法没什么具体要求.
      注意的是, Game 类都不处理图像数据.


01. Game_Temp($game_temp)
      和名字的意思一样, 处理缓存的. 在每次游戏开始的时候, 都会重新生成. 不会写如存档.


02. Game_System($game_system)
      处理系统数据的类. 处理诸如 计时器, 版本ID, 遇敌标志等. 会写入存档.  


03. Game_Message($game_message)
      处理信息的类. 包括 对话设定, 头像, 对话内容, 选项等属性. 会写入存档.


04. Game_Switches($game_switches)
      处理开关的类. 其实开关就是一个数组 @data . 对这个数组的读取写入就是对开关的操作.  
      比如打开第 10 号开关: $game_switches[10] = true .
      不过需要注意的是, 默认脚本限制开关ID在 5000 之内. 脚本 31 行有说明


05. Game_Variables($game_variables)
      处理变量. 与 Game_Switches 写法相同.


06. Game_SelfSwitches($game_self_switches)
      处理独立开关的类. 本质上是一个 Hash . 其 Key 结构是这样的一个数组:
  1. [地图ID, 事件ID, 开关ID]
复制代码
其中的 开关ID 就是 "A" "B" "C" "D" 这四个. 想扩展的话, 可以修改这里.


07. Game_Screen($game_map.screen(地图), $game_troop.screen(战斗))
      可以看成是处理画面效果数据的类. 画面震动, 画面亮度, 显示事件图片, 淡入淡出等等效果都是这个类处理的.


08. Game_Picture($game_map.screen.pictures(地图), $game_troop.screen(战斗).pictures)
     处理图片数据的类. 需要注意的是, 无论是在地图或者是战斗, 图片的 ID 都是在 1 到 20 之间. 一共 40 张图片. 这个和 XP 不同.(VX)


09. Game_Battler
     所有战斗者的父类(超类). 处理 HP MP , 不死身标志等数据.


10. Game_BattleAction
     处理行动的类. 包括战斗者使用的技能 ID , 物品 ID, 速度等数据. 在  Game_Battler 内部使用.


11. Game_Actor
     处理单个我方角色的类. 一切我方的数据都包含在这个类里. 默认是 Game_Actors 外壳的数据.


12. Game_Actors($game_actors)
     处理角色的外壳类. 和 Game_Switches 类似写法. 真正使用的是 Game_Actor 数据.


13. Game_Enemy
     处理敌人数据的类。在 Game_Troop 的 内部使用.


14. Game_Unit
     处理单位数据的类. 是 Game_Troop 和 Game_Party 的超类.


15. Game_Party($game_party)
     处理队伍数据的类. 一些比较常见的用法:
  1. $game_party.gain_gold # 获得金钱
  2. $game_party.steps # 步数
  3. $game_party.gain_item# 获得物品
复制代码


16. Game_Troop($game_troop)
     处理敌人队伍数据的类. 具体的看脚本注释就可以了.


17. Game_Map($game_map)
     处理地图数据的类. 几乎所有的地图数据都包含在这里, 比如事件, 远景图, 公共事件等. 比较常见的有:
  1. $game_map.need_refresh # 刷新地图
  2. $game_map.map  # 地图数据
复制代码
等.


18. Game_CommonEvent
     处理公共事件的类. 可以看成是 公共事件数据 的外壳类, 真正的数据在 $data_common_events 里.


19. Game_Character
     处理地图上角色数据的类. 和 Game_Actor 不同的是, Game_Character 处理的是地图上行走图的数据. 是 Game_Player  Game_Event 和 Game_Vehicle 的超类.


20. Game_Event
     事件类. 所有事件的处理都是在这个类中. 包括事件页切换, 事件 ID, 事件启动判断等.


21. Game_Vehicle
     交通工具类. 需要注意的是, 每张地图都是有交通工具的, 只是在没有设定的情况下, 交通工具的坐标是 (-1, -1)


22. Game_Player($game_player)
     处理地图上的角色的类. 我们控制的角色其实就是这个类在管理.


23. Game_Interpreter
     事件命令解析类. 一切事件的核心类. 处理事件的解析和执行. 所有的事件在这里都可以找到.




存档结构
VX 的存档数据结构如下:
01. 我方角色的数组, 结构是 [ 行走图文件名,  行走图序号]
02. 刷新次数的计数值, 其实可以认为是游戏时间(这个值除以 FPS 值就是游戏时间)
03. 正在播放的 BGM
04. 正在播放的 BGS
05. $game_system
06. $game_message
07. $game_switches
08. $game_variables
09. $game_self_switches
10. $game_actors
11. $game_party
12. $game_troop
13. $game_map
14. $game_player
作者: 八云紫    时间: 2010-11-8 21:07
修改存档位置

      目标: 修改存档位置, 做到自定义存档的保存位置.
      修改过程:
01. 明确修改目的.
      默认的存档文件是保存在根目录下的(也就是和 Game.exe 同一个目录), 现在的目标是保存在 Save 文件夹里. 然后再读取的时候也可以找到.
02. 当前已知
     存档的文件名是 SaveXXX.rvdata , 保存在根目录. 并且在进入标题的时候, 有存档的情况下, 光标会停留在 "继续游戏" 的选项上.
03. 开始修改
     先全局搜索 Save . 可以找到一些信息:

先关注 Scene_File(158) , 点进去后可以看到注释: 生成文件名称. 那么就是这里了, 修改成
  1. return "Save/Save#{file_index + 1}.rvdata"
复制代码
之前的 Save 是文件夹的名称. 可任意.

     然后注意, Scene_Title(131), 可以看到这里是查找存档的,  那么就继续改:
  1. @continue_enabled = (Dir.glob('Save/Save*.rvdata').size > 0)
复制代码
OK.
04. 测试.
    测试开始. 进入存档, 保存. 然后就出现:
  1. 无法找到文件 No such file or directory - Save/Save1.rvdata.
复制代码
好吧, 出问题了. 找不到文件.
    其实不要被错误提示迷惑了. 找不到文件是因为我们没有创建 Save 这个文件夹. 新建后测试正常.

附上工程:
修改存档位置.rar (242.82 KB, 下载次数: 305)
(那啥, 问了防止 SSD, 加了点经验.)
作者: DeathKing    时间: 2010-11-8 22:23
本帖最后由 DeathKing 于 2010-11-8 22:38 编辑

有关方法的一些补充:http://rpg.blue/thread-142147-1-1.html
反射机制用于查错很有用,稍后将会补上。反射机制(元编程)也是我很乐意讨论的。
[line]1[/line]
老板的MSN Space上面有一些讨论的文章,可以查阅一下:
1、正则表达式:http://szsu.spaces.live.com/blog/cns!D57D0E50BE1820CF!225.entry
2、论 Ruby 顶层及 Object、Kernel 的关系:http://szsu.spaces.live.com/blog/cns!D57D0E50BE1820CF!185.entry
3、Ruby 对象的引用、克隆、污染和冻结:http://szsu.spaces.live.com/blog/cns!D57D0E50BE1820CF!170.entry
作者: 一瞬间的幻觉    时间: 2010-11-11 04:07
good thing! i want zhichi you !
作者: LL206    时间: 2010-11-22 17:35
支持啊,一直想学脚本
作者: pchome35    时间: 2010-12-18 13:12
淺顯易懂~      非常支持++
作者: 八云紫    时间: 2010-12-18 23:38
本帖最后由 魔女真利亞 于 2010-12-19 10:12 编辑

Window 类集

基本语法:

  1. class Window_XXX < Window_Base
  2.   def initialize
  3.      super(x, y, w, h)
  4.      ...
  5.   end
  6.   def refresh
  7.     ...
  8.   end
  9. end
复制代码

作者: 一箭烂YiJL    时间: 2010-12-19 08:26
回复 魔女真利亞 的帖子

1.是Window_XXX < Window_Base,不是Window_XXX < Window
2.update会不断进行,refresh只是靠update辅助。(通常update需要一些条件才进行refresh)
3.initialize是初始化,即最初建立window的动作,比update还要早
4.可著名Window_XXX是子类。
5.super是传输给父类的方法,详细可参考:
http://rpg.blue/thread-162597-1-1.html
注:此贴无恶意,我在此也多多鼓励有能力的前辈多写教程,造福6R!
作者: 八云紫    时间: 2010-12-19 09:30
本帖最后由 魔女真利亞 于 2010-12-19 09:34 编辑

回复 一箭烂YiJL 的帖子

1. Window 是最终的超类或者父类。这个你看 Window_Base 就知道了。所以我用了一个最终。
2. refresh 是另一个意义上的 update , 只是 refresh 没必要每帧都调用。
3. +1
4. 每明白
5. super 不是说是传输给父类的方法。super 的本意是调用父类的同名方法。 super 后的参数就是父类同名方法的参数。

作者: zh99998    时间: 2010-12-19 09:46
关于窗口的越级继承

在选择写窗口的时候经常会不想要冗余的Window_Selectable#update
比如,我想写一个带光标的窗体,但是控制光标的更新是自己写的,而不想用Window_Selectable的,但是因为是个窗体,所以还是得必须调用Window_Base#update或者Window#update
于是可以这么实现
class Window_Base
  alias update_base update
end
class Window_XXX < Window_Selectable
  def update
   ******
   update_base
  end
end
作者: 一箭烂YiJL    时间: 2010-12-19 09:56
回复 魔女真利亞 的帖子

1.这知道,Window_Selectable也属于Window_Base,
Window_Base属于Window,Window属于dll,
那么在脚本库里最终的就是Window_Base
而Window_Base里的super是没有参数的,
所以能用super(x, y, w, h)其实有Window_Base。

2.你说窗口的两大类(也不是很大)~,Window_Selectable继承Window_Base(这句你有说),
既然Window_Base已是脚本库里最终的,何不强调Window_Base呢?
然后后来在添加Window_Selectable、Window_Command、Window_Item这些常用父类。

3.既然dll难以修改,那么我认为要重点放在Window_Base,
这帖是从0学起,我觉得一下子把重点点到dll实在是勉强。
作者: 八云紫    时间: 2010-12-19 10:12
本帖最后由 魔女真利亞 于 2010-12-19 10:13 编辑

回复 一箭烂YiJL 的帖子

1. 好吧。我投降。仅仅只是想说明下被人遗忘的 Window。Window_Base initialize super 居然是空的,这个真没注意~~
2. 有的时候,教程没必要说的太明了。能举一反三才是最好的教程。虽然咱的是渣教程。
3. 这个是中级教程了哦。不过那个 dll 其实只是提一下罢了。没有说它是重点。
作者: 禾西    时间: 2010-12-19 10:20
從 0 學起就應該從最基礎的學起吧?Window_Base其實提供了很多非必要的功能,對于了解 Window 的實質沒有好處。你甚至可以把 Window_Base 當作一個抽象類,而我認為這一層甚至可以被拋棄——因為他唯一的重點只是提供了windowskin的統一管理而已。
作者: 八云紫    时间: 2010-12-19 10:24
回复 禾西 的帖子

感觉上 Window_Base 就是虚类。

好多 V

Window_Base 只是中间层。

禾西酱早~~
作者: 一箭烂YiJL    时间: 2010-12-19 10:24
本帖最后由 一箭烂YiJL 于 2010-12-19 10:25 编辑

回复 魔女真利亞 的帖子

和战宣明条约:(什么条约分明,拿来搞气氛的~)

4.可著名Window_XXX是子类。

这是说承继人家的那个是子类,(叫你可以著名一下~)

super是调用父类动作的一个方法,super后面的参数有传输的作用而已。。。
(↑很废的废话↑)

我还是认为重点要放在Window_Base这个脚本Window最终父类,不是dll里的Window类。

希望这是争吵的最后一贴,不希望这是技术交流的最后一楼。
作者: 八云紫    时间: 2010-12-19 10:30
回复 一箭烂YiJL 的帖子

1. 有火药味么??
2. Ruby 里 super 的解释是:
super将调用被当前方法覆盖的父类中的同名方法。若省略括号和参数时,将会把当前方法的参数原封不动地传递给父类中的同名方法。

3. 真的要写什么 Window_Base 啥啥方法的话,那就免了吧。那个可是 F1 的说。而且没啥意义。自己看看,领悟才是最好的~~
作者: 一箭烂YiJL    时间: 2010-12-19 10:38
回复 魔女真利亞 的帖子

1.一点点
2.我漏了必须同名函数。(怎么能这么大意?)
3.不用什么啥啥方法,这时些:"脚本库里最中Window父类的类是Window_Base"
然后像上面用红笔写。然后又用红笔写"Window_Selectable也是继承Window_Base的。"
很简单的就完善了豆腐渣教程(不是工程~)。
作者: 八云紫    时间: 2010-12-19 10:43
回复 一箭烂YiJL 的帖子

1. 有么?有么?
2. (被吃掉了。)
3. 不要在意细节。不过真实的结果就是,真的是继承 Window , 虽然是 C类。
作者: 六祈    时间: 2010-12-19 18:35
本帖最后由 六祈 于 2010-12-19 20:06 编辑
关于super:
在方法里写上super会调用继承链上一级的同名方法【如果没有继续回溯直到找到第一个为止】
而super有两种写法:
super【不带括号】:会将参数传递给父类的同名方法
super()【带括号】:则参数必须手动传递

引自愚者的31楼
八云说的没错,在脚本里Window_Xxxxx系列的最终父类是Window_Base,而Window_Base是继承自Window,Window被写在dll中。

话说即使上一级父类的同名方法是个空白,这个super最好还是加上。相对alias的改名而言,module混入的【装饰器模式】操作方法更安全【个人理解,欢迎扔砖】

@xxx:不要在意细节嘛,八云最初说Window_XXX分为两系,各自的最终父类不同,这是不对的,在F11中可见Window_xxx系列都是继承自Window_Base,而F1里也很明确说明Window_Base是继承自Window类;而你所说xxx类【属于】xxx类,一般oop里我们不会这么说,【继承自】与【属于】区别还是有区别的
作者: 八云紫    时间: 2010-12-19 22:59
本帖最后由 八云紫 于 2010-12-20 13:58 编辑

Tilemap 类

     Tilemap 元件地图类, 用于处理地图的描绘以及管理地图的类. 其属性说明如下:
1. 方法
    Tilemap 类方法不多, 仅有4个:
       ● new([Viewport]).
            用于生成类实例. Viewport 为显示端口.
       ● dispose . dispose? . update
            释放, 判断是否释放. 更新.
2. 属性
    ● bitmaps
     长度为 8 的数组. 用于存放地图图块素材的属性. 其对应关系如下:
     
序号 对应图块序号 对应图块 序号对应图块
0 TileA1 1 TileA2 2 TileA3
3 TileA4 4 TileA5 5 TileAB
6 TileC 7 TileD 8 TileE

    ● map_data
    地图数据. Table 表格类. 通常是从 MapXXXX.rvdata 里获取的. 读取的方法简写为:
  1. load_data(sprintf("Data/Map%03d.rvdata", map_id)).data
复制代码
其大小规定为: 地图长 * 地图宽 * 3 . 这里的 3 层说明如下:
    0.  A层. 存放 A类图块的 ID 号.
    1.  辅助层: 如果某个 ID 具有草木茂盛属性的话, 这层的数据会和 0层的数据正好相差 48 . 是否有其他功能有待研究
    2. BCDE 层: 用于存放 BCDE 层的 ID 数据.
    ● flash_data  
    F1 的说明是 "引用仿真游戏等中显示可以移动的范围时所用的闪烁信息" , 默认脚本没有使用这个功能. 该功能多数是在 SLG 脚本中使用到(感觉 紫苏酱 补充。) 数组,对于地图上的 x y 坐标。  
    ● passages  
    数组. 地图图块ID对应的通行度数组. 目前的发现是: 用于显示标记有 "☆" 图块的ID的时候, 将会显示在最上层.
    ● 剩下的属性  
    不多说了. 看 F1 一目了然.
注意: F1 里有提到
组成元件地图的每个精灵之 Z 坐标都是固定的。
  1. 显示在角色之下的元件,其 Z 坐标为 0。
  2. 显示在角色之上的元件,其 Z 坐标为 200. (RGSS2)

[line]1[/line]

图块

    VX 的图块粗略的可以分成两大类, 包含自动原件的 A图块和普通的 B C D E 四个图块. 每个图块都有它自己的ID, 利用 ID 来决定地图的某个坐标需要描绘什么图块, 以及判断该点的属性(柜台属性, 草木茂盛属性什么的).
    对于图块ID, 胃君 给出了一个表格, 这里我来引用下:
Tile_ID编号规则
0-1024       B/C/D/E,每张256个元件
1536-1664    A5,共128个元件
2048-2816    A1,共16个元件,每个元件占48个编号,不同编号含义见样式一,样式三
2816-4352    A2,共32个元件,每个元件占48个编号,不同编号含义见样式一
4352-5888    A3,共32个元件,每个元件占48个编号,不同编号含义见样式二
5888-        A4,共16个元件,每个元件占48个编号,不同编号含义见样式一,样式二
更加具体的说明, 请参照帖子末尾的参考资料3

    接下来是判断属性.
    通行度判断: 利用 Game_Map 里的 @passages 来获取对应图块ID的通行度. 不要这里需要注意一下, 由于每个坐标上都有3层, 所以在判断的时候需要遍历这 3 层, 次序是由大到小依次判断.
       这里列举出四个重要的通行度判断数据:
用途数据
人物0x01
小型船0x02
大型船0x04
飞船降落点0x08

     回到通行度判断, 设定我们需要判断的数据是 flag (其实就是 0X01 等). 在依次取得地图 ID 所对应的通行度数据的时候, 可以这么判断:
     1. 如果其值等于 0x10 的时候, 也就是我们设定的 "☆" , 直接跳过.
     2. 如果其值与 flag 做与运算的话, 等于 0x00 (其实就是0)的话, 就是 "o" , 可以通行.
     3. 如果其值与 flag 做与运算的话, 等于 flag (flag 自己本身)的话, 就是 "x" , 不可通行.


    从这里可以看到图块的优先级问题:
    1. 标记有 "☆" 默认显示在最上面, 并且不参与通行判断.
    2. B C D E 四个图块最先参与判断, 只有在 B C D E 图块不存在的场合("☆" 参照第一条), 在调用 1层和 0层来判断.

   柜台属性和草木繁茂属性判断:
   柜台属性 引用的数据时来自于第1层的., 对比数据为 0x40, 与运算之后等于 0x40 的话, 就是说明有柜台属性.
   草木繁茂属性: 和 柜台属性 类似, 不过引用的是第 0 层数据, 对比数据为 0x80
[line]2[/line]

参考资料:
1. Kiss姐姐的 TileMap脚本
2. 柳大的 还原Tilemap类脚本
3. 胃君 的主动元件解说
作者: zh99998    时间: 2010-12-20 19:38
喵咱来捣乱了><
● map_data
    地图数据. Table 表格类. 通常是从 MapXXXX.rvdata 里获取的. 读取的方法简写为:
  1. load_data(sprintf("Data/Map%03d.rvdata", map_id)).data
复制代码


sprintf是个内置方法,把一个对象按指定的格式进行格式化,然后返回格式化之后的字符串,所谓【指定格式】跟C语言里常用的printf的格式相同,没有用过C的话这里讲解一下,%03d是说把一个数字以十进制形式,补满三位数
作者: 八云紫    时间: 2010-12-20 20:32
本帖最后由 八云紫 于 2010-12-21 09:43 编辑

Scene 类集

基础语法:

  1. class Scene_XXX < Scene_Base
  2.    def initialize([...])
  3.       ...
  4.    end
  5.    def start
  6.      super
  7.      ...
  8.    end
  9.    def update
  10.      super
  11.      ...
  12.    end
  13.    def terminate
  14.      super
  15.      ...
  16.    end
  17. end
复制代码
.
★ initialize
    初始化方法, 不是必须要的, 不过需要外部的某个值的时候, 可以使用该方法来传值.
★ start
    开始, 处理从进入这个场景开始, 到第一个刷新(update) 之间的部分. 常用来初始化窗口, 或者变量什么的. 作用和 initialize 类似.
★ update
    刷新. 每帧调用一次. 一般用于窗口的刷新, 或者必要的时候变量的刷新.
★ terminate
    释放. 用处类似 dispose .只是最好不要手动去调用. 常用于释放窗口什么的.

    其中的 start update terminate 都是必须需要的. 不写的话, 会默认调用父类 Scene_Base 的同名方法, 然后就是 Scene_Base 的同名方法都是空的, 仅仅只是预留下一个名字.
    其他的 Scene 脚本的用法都是这样的. 只是用途不一样. 这个看默认脚本说明就可以了.

[line]1[/line]
   
Scene 类运行原理

    说到 Scene 的运行原理, 需要从 main 脚本的某一段说起. 脚本段如下:
  1. $scene = Scene_Title.new
  2. $scene.main while $scene != nil
复制代码
$scene 全局变量表示的是当前的场景, 也就是说我们在窗口里显示的那个场景. 在初始化好 $scene 后, 只要 $scene 不等于 nil, 那么将会无限的调用 $scene 的 main 方法. 也就是说, 一直循环调用 Scene 类的 main 方法.
    那么, main 方法在哪里呢? 之前说明基本语法的时候, 也没有提到过 main 方法的说?
    其实 main 方法是写在 Scene_Base 里的. 那么, 我们来看看内容:
  1. #--------------------------------------------------------------------------
  2.   # ● 主处理
  3.   #--------------------------------------------------------------------------
  4.   def main
  5.     start                         # 开始处理
  6.     perform_transition            # 执行渐变
  7.     post_start                    # 开始后处理
  8.     Input.update                  # 更新输入讯息
  9.     loop do
  10.       Graphics.update             # 更新游戏画面
  11.       Input.update                # 更新输入讯息
  12.       update                      # 更新画面
  13.       break if $scene != self     # 切换画面时中断循环
  14.     end
  15.     Graphics.update
  16.     pre_terminate                 # 结束前处理
  17.     Graphics.freeze               # 准备渐变
  18.     terminate                     # 结束处理
  19.   end
复制代码
具体的含义, 注释都说明了.
    我们来看看大概的一个流程:
   

    main 方法里的核心部分就是 loop do ... end 的了. 这个可以理解成这个场景的主循环主循环. 它一共做了四件事情:
    1. Graphics.update 负责图像部分的刷新, 其实也包含有窗口的消息循环刷新.
    2. Input.update 输入模块刷新, 用于检测键盘的状态.
    3. update Scene类的刷新方法. 自定义的.
    4. break if $scene != self   如果当前场景 $scene 不是自己本身的话, 就跳出循环.
   
作者: 不是马甲    时间: 2010-12-20 21:52
本帖最后由 不是马甲 于 2010-12-20 21:52 编辑

呀呀  支持一下   竟看不懂 晕
作者: 一箭烂YiJL    时间: 2010-12-20 22:10
zh为什么把这一帖弄来了技术讨论区呢?
作者: DeathKing    时间: 2010-12-20 22:15
未完成的标准库参考手册,近期完成迭代部分:
http://rpg.blue/thread-162871-1-1.html
作者: 八云紫    时间: 2010-12-22 00:06
本帖最后由 八云紫 于 2010-12-23 00:18 编辑

Main

    Main 的含义和它的名字是一样的意思, "主要的". Main脚本在 VX 的脚本库, 乃至 XP 里都是一个很重要的脚本.
    让我们先来看看 Main 的内容.
  1. #==============================================================================
  2. # ■ Main
  3. #------------------------------------------------------------------------------
  4. #  各定义结束后,从这里开始实际处理。
  5. #==============================================================================

  6. # 一些常用的字体
  7. Font.default_name = ["SimHei", "黑体", "DFKai-SB", "標楷體", "Verdana", "Arial Unicode MS"]
  8. begin
  9.   Graphics.freeze
  10.   $scene = Scene_Title.new
  11.   $scene.main while $scene != nil
  12.   Graphics.transition(30)
  13. rescue Errno::ENOENT
  14.   filename = $!.message.sub("无此文件或文件夹 - ", "")
  15.   print("无法找到文件 #{filename}.")
  16. end
复制代码
重要的部分其实都已经说过了. 忘记的话, 就查查 F1 吧.
       Main 脚本里一个重点在于 begin .. rescue.. end 结构. 它的用处是当  begin .. rescue 中级部分出现异常(或者说是错误)的时候, 可以被我们捕获到. 这里捕获的是 "文件找不到" 这个异常. 这就是问什么当我们的素材缺失的时候, RM 会提醒我们什么文件找不到的错误.  具体的部分, 请看 RGSS2异常 部分.
[line]2[/line]
VX 脚本的运行顺序

      Main 的位置标志着 RM 脚本的运行起点. 这里就简单的说说 RM 的脚本运行顺序吧. (其实我自己也不知道是不是这样, 不过八九不离十就是了 > <)
      解说之前要说明一下几点:
      1. 脚本的运行都是从脚本的第一个的第一行开始的.
      2. 遇到 class(类) def(方法) module(模块) 都不会主动运行, 除非你去调用它.
      3. 脚本都是按顺序执行的, 前提是满足执行条件.

      知道这些之后就可以开始说明脚本运行顺序了.
     1. 从右边脚本列表的第一个脚本的第一行开始执行.(是的, 很拗口.)
     2. 如果遇到 class 的话, 那么 RM 就知道这里是定义类的, 于是它将这个类名保存起来.
     3. 如果这个类有父类, 那么 RM 就会去之前的纪录里去查看有没有这个类, 有的话, 就指定它们的继承关系, 没有的话, 就会提示找不到这个类.
     4. def 方法 和 module 模块也是类似的.
     5. 默认的脚本在 Main 脚本之前都是 类 或者 模块 定义的, 所以在执行到 Main 之前都仅仅是扫描.
     6. 到达 Main 脚本后, 真正的执行就是从这里开始了.
     7. 由 Main 脚本的调用关系可以延伸到之前所定义的所有类.
     8. 当我们选择游戏结束的时候(也就是使用脚本来结束, 这里不包含 exit 和 点击右上角的那个红叉), 才会继续从 Main 脚本里脱离出来继续执行后面的内容.

     其实顺序大致就是这样的. 这里呢, 我们就可以注意到一些细节:
     1. 在 类 里面的 方法定义是不限制顺序的, 也就是说, 我们可以先使用, 然后再去定义这个方法的内容. 但是早整个脚本库里, 只能是使用到这个类之前的所有资源. 因为它之后的类啦什么的, 都是没有扫描记录的. 所以, RM 并不知道的它之后的内容是什么.
     2. 由于 Main 里的内容既不是 class module 也不是 def 定义, 它仅仅是一段脚本, 于是满足调用条件, 所以, 真正的脚本开端就是 Main 脚本.
     3. 除非使用到 exit 或者那个红叉来关闭 RM , 否则 Main 后面的内容也是会执行的. 就算是有错误, RM 也是会提醒. 所以, 在某个脚本不需要的情况下, 最好还是批量注释掉.
     4. 没有了吧, 大概.


作者: 铃仙·优昙华院·因幡    时间: 2010-12-23 00:17
消灭烦人的连贴~
作者: 八云紫    时间: 2010-12-23 00:18
本帖最后由 八云紫 于 2011-1-9 21:12 编辑

Window 窗口

    其实这个教程想了好久, 不知道该怎么写. 于是就照搬 F1 了, 只是更加进一步说明一下. 或者给点例子什么的.

    Window 窗口类
    游戏窗口的类。在内部由多重的精灵所组成。

    类方法
        Window.new([viewport])
        生成一个窗口物件。必要时指定一个显示端口(Viewport 显示端口类)。

       方法
       ★ dispose
       释放窗口。若窗口已释放则什么都不做。

       ★ disposed?
       当窗口已释放则返回 true。

       ★ update
       刷新光标的闪烁和暂停标记的动画。原则上每一画格调用一次。
       除非有使用光标或者是暂停标记, 其他的情况下, 没有必要调用这个方法. 起不到任何作用.

       属性
       ★ windowskin
       引用作为窗口外观的位图(Bitmap 位图类)。
       只能够使用 VX 的窗口外观。(RGSS2)
       VX 所使用的窗口素材与 XP 相比有很大的不同, 这个看素材就知道了.(其实是没有安装 XP , 也不好细说, 以后增加吧. 或者去看看有没有人写过这个~)
     
       ★ contents
       引用作为窗口内容的位图(Bitmap点阵图类)
       默认的 contents  的初始值其实是这个的:
  1. contents = Bitmap.new(0, 0, 1, 1)
复制代码
这个值分别在 Window_Base 和 Window_Selectable 的 create_contents 方法里被重新赋值.


       ★ viewport (RGSS2)
       引用与窗口关联的显示端口(Viewport显示端口类)。
       这里要非常的注意一个问题, Window 类里使用的viewport 其实就是在初始化的时候指定的那个. 如果之前的那个 viewport 被修改的话, 这里也会被修改.
  1. v = Viewport.new(0, 0, 100, 100)
  2. w = Window.new(v)
  3. p w.viewport.rect
  4. v.rect.x = 10
  5. p w.viewport.rect
复制代码


       ★ cursor_rect
       游标的矩形(Rect矩形类)以相对坐标 (-16, -16) 设置窗口的左上角。
       这里注意一下, 真实的测试效果和 F1 的说明不一致, 不知道是不是我的测试出问题了.
  1. w = Window.new()
  2. p w.cursor_rect
复制代码
得到的结果是 (0, 0, 0, 0) . 不是F1 里的 -16, -16.


       ★ active
       光标闪烁的状态。true 表示闪烁。

       ★ visible
       窗口可见的状态。true 表示可见。

       ★ pause
       暂停标记可见的状态。true 表示可见。

       ★ x
       窗口的 X 坐标。
      
       ★ y        
       窗口的 Y 坐标。

       ★ width
       窗口的宽度。

       ★ height
       窗口的高度。

       ★ z
       窗口的 Z 坐标。数值愈大的平面愈靠近玩家。Z 坐标相同的,最后生成的对象则会最靠近玩家。
       窗口背景和内容的 Z 坐标在 RGSS2 中不能使用不同的 Z 坐标。(RGSS2)

       ★ ox
       窗口内容的 X 坐标。滚动窗口时修改此数值。

       ★ oy
       窗口内容的 Y 坐标。滚动窗口时修改此数值。
       以上两个属性和 Sprite 的同名属性作用一样. 在不移动窗口位置的情况下, 仅仅是移动窗口的内容. 这个可以理解成 当角色在地图上行走的时候, 地图需要适应性的滚动, 但是 RM 的窗口的位置依旧不变一样.

       ★ opacity
       窗口的不透明度(0~255)。超出此范围的数值会自动修正。
       opacity 针对的是整个Window

       ★ back_opacity
       窗口背景的不透明度(0~255)。超出此范围的数值会自动修正。

       ★ contents_opacity
       窗口内容的不透明度(0~255)。超出此范围的数值会自动修正。

       ★ openness (RGSS2)
       窗口的开启程度(0~255)。超出此范围的数值会自动修正。
       将此数值从 0(完全关闭)至 255(完全开启)之间变换,可以产生窗口打开和关闭的动画效果。 openness 若低于 255,则不会显示其内容。默认值 255。
       Window 的内容只有在 openness  等于 255 的时候才能显示出来.
作者: 偶尔杀人越货    时间: 2010-12-23 14:54
本帖最后由 偶尔杀人越货 于 2010-12-23 14:57 编辑

帮你消灭一楼,等再出点帮你做个电子书,带图的那种。
作者: 八云紫    时间: 2010-12-23 21:09
本帖最后由 八云紫 于 2010-12-23 22:18 编辑

Game 类应用<< 技能书脚本>>
  ★ 要实现的功能
  名字是 << 技能书脚本>> , 那么, 脚本的功能就是使用一个物品(技能书), 让被使用的角色可以学习到物品里指定的技能.

  ★ 初期问题
  1. 既然是物品, 那么就存在一个方法来实现这个物品的使用.
  2. 既然是技能书, 那么就应该有一个对应关系, 就是 物品 => 技能 . 也就说什么物品可以学习到什么技能.
  3. 物品可以使用, 那么技能也应该需要学习.
  4. 需要知道这个物品时给谁使用.

  ★ 初期运行步骤构思
  1. 使用物品
  2. 从使用的物品中取得某个资料
  3. 将这个资料通过某种方式转换成技能资料
  4. 获得被使用者的资料
  5. 让这个使用者学习到这个技能.

  ★ 初期资料收集
  1. 找到能实现 使用物品 的方法.
   于是全局搜索 "使用物品". 找到一些满足条件的.

    我们一个个来排除:
     ● Sound: 这个一定不是了. 看名字就知道是处理声音的.
     ● Game_Battler: 计算的方法, 也不是.
     ● Scene_Item 和 Scene_Skill : 看起来好像就是这里, 点进去一看, 其实是场景跳转什么的.
     ● Scene_Battle : 有两处. 我们想来看看第一个.
  1. when 2  # 使用物品
  2.       execute_action_item
复制代码
可能性很高. 于是继续查找 execute_action_item 这个方法.
  1. #--------------------------------------------------------------------------
  2.   # ● 执行战斗行动:使用物品
  3.   #--------------------------------------------------------------------------
  4.   def execute_action_item
  5.     item = @active_battler.action.item
  6.     text = sprintf(Vocab::UseItem, @active_battler.name, item.name)
  7.     @message_window.add_instant_text(text)
  8.     targets = @active_battler.action.make_targets
  9.     display_animation(targets, item.animation_id)
  10.     $game_party.consume_item(item)
  11.     $game_temp.common_event_id = item.common_event_id
  12.     for target in targets
  13.       target.item_effect(@active_battler, item)
  14.       display_action_effects(target, item)
  15.     end
  16.   end
复制代码
于是就找到了. 就是那个 target.item_effect(@active_battler, item). item_effect就是实现使用物品的方法.

  使用同样的方法可以查找到学习技能的方法: learn_skill.
  
  ★ 开始修改脚本
  1. 我们先从 item_effect 入手. 因为需要修改默认脚本的方法, 所以 alias 的作用很重要.
  1. alias old_item_2_skill_item_effect item_effect
复制代码
2. 然后写一个 item_effect 方法.
  1. def item_effect(user, item)
  2.     old_item_2_skill_item_effect(user, item)
  3.     item_id = item.id
  4.     skill_id = Item2Skill::Item_to_Skill[item_id]
  5.     return if skill_id == nil
  6.     skill_id.each do |id|
  7.       user.learn_skill(id)
  8.     end
  9.   end
复制代码
这里可以将 old_item_2_skill_item_effect 理解成旧的 item_effect. 调用 old_item_2_skill_item_effect 也就是调用 旧的 item_effect. 所以参数是一样的.
    3. 主要到  Item2Skill 这个模块了吗? 它的作用就是将 物品和技能联系在一样. 于是可以这么定义  Item2Skill 模块.
  1. module Item2Skill
  2.   Item_to_Skill = {
  3.     21 => [2],
  4.   }
  5. end
复制代码
第一个 21 是物品的ID号, 后面的 2 是要学习的技能ID. 使用数组的理由是可以添加多个技能ID.

    ★ 测试
    测试就不说了. 有错误改就是了.
  
作者: 霜舞风尘    时间: 2010-12-24 10:38
万分的支持!!!!:hug:
作者: ypkkjt    时间: 2010-12-24 18:39
要好好学习了
作者: zh99998    时间: 2010-12-29 17:42
嘛现在主人网络杯具了咱来扯几句 ><

RGSSError RGSS错误

在试图访问一个无效(释放掉了或者尚未生成)的Sprite/Window/Plane/Tilemap时会抛出此异常

sp = Sprite.new
sp.dispose
sp.x = 1 #=>RGSSError

另一个比较常见的情况时尚未生成
例如

class Window_Test < Window_Base
  def initialize
    self.z = 100
    super(0,0,544,416)
  end
end
Window_Test.new #RGSSError
由于调用Window_Test.new,系统执行Window_Test的initialize
然后执行了self.z=,这时这个Window尚未生成(在Window顶层类的initialize里生成,需要用super调用上去),所以产生了错误
正确的写法应该是
class Window_Test < Window_Base
  def initialize
    super(0,0,544,416)
    self.z = 100
  end
end
作者: yanglibin0409    时间: 2010-12-30 08:31
支持,能不能整合成压缩包呢??我网页老打不开,卡死
作者: 木葬枫    时间: 2011-1-1 03:04
坐等杀人大叔整合的电子书
作者: l65591397    时间: 2011-1-1 11:45
提示: 作者被禁止或删除 内容自动屏蔽
作者: 九明剑魂    时间: 2011-1-1 11:48
相当不错,要好好研究一下。
作者: 八云紫    时间: 2011-1-9 20:52
本帖最后由 八云紫 于 2011-1-10 17:33 编辑

Color 色彩 和 Tone 色调

       Color 色彩
       说到颜色, 需要从我们的眼睛说起.
       人的视觉系统有3类与颜色相关的锥细胞. 我们就是使用这三类的锥细胞对颜色做出反应的. 这个就是三基色的理论基础. 这样就可以让计算机的颜色表示方法减少到3总颜色值. 于是, 两种颜色的三基色值相同的话, 他们在视觉上就是等价可互换的.
       另外一个颜色系统. 可以区分成两类, 加色法和减色法.
       加色法是指使用三基色相加来获取颜色. 我们 VX 里的颜色系统就是使用加色法的. 加色法中的三基色分别是: 红色, 绿色, 蓝色. 我们可以将这三个颜色值按照一定的比例投影到黑色的屏幕上获取颜色.
       减色法与加色法相反. 它是从白色的光中去除某些颜色来获取最终的颜色的. 减色法在 打印机, 绘图仪等工具上使用. 减色法里的三基色分别是: 青色, 品红色, 黄色.

       回到 VX . VX 的颜色使用的加色法. 于是得到三基色 RGB . 于是我们就可以用这个来取得我们需要的颜色, 比如品红:
  1. magenta = Color.new(255, 0, 255)
复制代码
单纯的使用 RGB 的话, 有的场合时不能达到要求的. 于是我们就引入 四色系统(RGBA) . 前三个 RGB 和前面说的一样. A 是指 α(alpha)通道. 也就是透明度(正确叫法是 不透明度, 透明度是咱习惯叫法). 例如我们需要一个半透明的品红:
  1. magenta = Color.new(255, 0, 255, 128)
复制代码
Tone 色调
        Tone 类和 Color 类很类似. 只是用的地方有点区别就是了. Tone 类里相对于 Color 的 alpha 的位置, 是灰度值, 也就是指纯白、纯黑以及两者中的一系列从黑到白的过渡色。
        Tone 类用来指定精灵(Sprite) 和 Viewport 的整体颜色倾向.
     
作者: zh99998    时间: 2011-1-9 20:56
补充Color和Tone的一个区别,第四个可选参数
Color的第四个可选参数是透明度,Tone的是灰度
作者: 一箭烂YiJL    时间: 2011-1-9 20:58
回复 八云紫 的帖子

关于颜色:
加色法,就是光学三原色?
减色法,就是从三原色中调出来的另外的三种颜色?
作者: 八云紫    时间: 2011-1-9 21:04
回复 一箭烂YiJL 的帖子

减色法的三基色换成 RGB 的值, 可以很容易的看出猫腻:
品红 Magenta (255,     0, 255)
黄    Yellow     (255, 255,     0)
青    Cyan      (0,     255, 255)
作者: 一箭烂YiJL    时间: 2011-1-9 21:36
回复 八云紫 的帖子

那么这种说法就是说加色法和减色法都可以互相翻译啦?
光学三原色是RGB,而色的三原色是RBY(红蓝黄)
有个说法就是RGB加起来是白色(光),RBY加起来是黑色。
那么其实Tone的灰阶度技术在内部是不是强制加黄色做到的呢?
还有可以补充Color的alpha和Tone的gray(灰度值)最大为255,超过时自动修正。
还有一件事,Color的alpha不是"不透明度"吗?
作者: 铃仙·优昙华院·因幡    时间: 2011-1-9 21:40
回复 一箭烂YiJL 的帖子

"不透明度" 和 透明度 有区别么??

有的话, API 的Color 和 Ruby 的Color 也是有区别的咯.

不要钻牛角尖啦. 再说什么超过 255 修正什么的, F1 里都是有的.(其实是我偷懒)
作者: 一箭烂YiJL    时间: 2011-1-9 21:46
回复 铃仙·优昙华院·因幡 的帖子

Color的alpha默认为255,
Color的alpha为0时,就完全透明了。

可是Tone的gray默认为0,
Tone的gray为255是就是黑色了。
作者: 八云紫    时间: 2011-1-9 21:54
本帖最后由 八云紫 于 2011-1-9 22:02 编辑

回复 一箭烂YiJL 的帖子

Tone 和 color 只能说是类似而不能等同

作者: 铃仙·优昙华院·因幡    时间: 2011-1-9 22:03
本帖最后由 铃仙·优昙华院·因幡 于 2011-1-10 10:23 编辑

Rect 矩形

     好吧, 这个其实很不知道怎么组合的, 就单独写一个吧.
   
     RGSS2(Ruby)矩形的矩形使用一个四元组来表示: ( 矩形左上角的 X 坐标, 矩形左上角的 Y 坐标, 宽度, 高度 ) 来表示.
     我们可以创建这么一个矩形: 左上角坐标为( 10, 10) , 宽 20, 高 30 的矩形:
  1. a = Rect.new(10, 10, 20, 30)
复制代码
这里需要注意一个问题: RGSS2 里的 Rect 每个元素都是整数, 也就是说, 就算是使用小数, 也会被取整. 比如:
  1. a = Rect.new(10.0, 10.0, 20.0, 30.0)
  2. p a
复制代码
至于 Ruby 就不知道了. 不过 Ruby 默认有 Rect 类么???

     [line]1[/line]
     提高:
     Window 里也有 Rect 结构, 定义是:
  1. typedef struct tagRECT
  2. {
  3.     LONG    left;
  4.     LONG    top;
  5.     LONG    right;
  6.     LONG    bottom;
  7. } RECT, *PRECT, NEAR *NPRECT, FAR *LPRECT;
复制代码
Window 的 Rect 四元组应该是(矩形左上角的 X 坐标, 矩形左上角的 Y 坐标, 矩形右下角的 X 坐标, 矩形右下角的 Y 坐标)

     如果需要在 RGSS2 里的矩形切换到 Window 的矩形的话, 应该这样:
  1. rgss2_rect = Rect.new(0, 0, 10, 10)

  2. window_rect = [rgss2_rect.x, rgss2_rect.y, rgss2_rect.x + rgss2_rect.width, rgss2_rect.y + rgss2_rect.height].pack("l*")
复制代码

作者: Zhen~败类    时间: 2011-1-10 04:35
这不就是RPG Maker VX的帮助文档么?
作者: 一箭烂YiJL    时间: 2011-1-10 13:30
矩形类也可以在bitmap里套一个框,
然后提取里边的图像出来。
此贴只是提供了一个用法,比如:
  1. rect = Rect.new(20, 20, 10, 10)
  2. bitmap = Cache.load_bitmap("路径", "图片名称")
  3. self.contents.blt(272, 156, bitmap, rect)
复制代码

作者: 八云紫    时间: 2011-1-10 15:40
征求一些简单,有代表性一样的小脚本请求,咱是想写一些范例什么的。

请大家帮忙想想的说。意见采纳奖励500分~~~
作者: david50407    时间: 2011-1-10 21:56
回复 八云紫 的帖子

Bitmap都还没解说@@
做个七彩霓虹灯的特效吧 (用Tone)
作者: 八云紫    时间: 2011-1-10 21:57
回复 david50407 的帖子

Bitmap 无从下手的说~~~
作者: david50407    时间: 2011-1-10 23:41
本帖最后由 david50407 于 2011-1-10 23:52 编辑

Bitmap 位图

  这次换我来给各位解说了 恩。
  位图,简单来说就是图像,在画面上显示位图,必须使用精灵(Sprite 精灵类)之类的对象。
  当然我们的图像是要能够有多种变化的,所以Bitmap类提供了两种生成的方式:
  1. # 由文件载入并生成
  2. b = Bitmap.new(filename)
  3. # 生成新的位图
  4. b2 = Bitmap.new(width, height)
复制代码
喔,对了,生成位图之后是会保留在内存的,如果不需要的话要记得释放唷~
  1. # 释放位图
  2. b.dispose
  3. # 侦测已释放位图与否 当位图已释放则返回 true
  4. b.disposed?
复制代码


  说位图 MS 不大容易明白,把他想成是绘图板吧,所谓绘图板就是画画的地方,一个美好的园地(你扯过头了 喂!)
  做为一个绘图板,就要具备绘图的功能(这不是废话吗?)。在这里,RGSS2 已经准备好了一些绘图的方法了:
1)绘制矩形 fill_rect
  1. # 绘制矩形 将区域 (x, y, width, height) 填满指定颜色 color(Color 色彩类)
  2. b.fill_rect(x, y, width, height, color)
  3. # fill_rect 能吃 rect (Rect 矩形类) 的唷~
  4. b.fill_rect(rect, color)
复制代码
2)绘制渐层矩形 gradient_fill_rect
  1. # 绘制渐层 将区域 (x, y, width, height) 填满从颜色 color1(Color 色彩类)至 color2(Color 色彩类)的渐层色彩
  2. b.gradient_fill_rect(x, y, width, height, color1, color2)
  3. # 能加上 vertical 来设置是否为纵向渐层 (true 为纵向)
  4. b.gradient_fill_rect(x, y, width, height, color1, color2, vertical)
  5. # 当然也吃 rect (Rect 矩形类) 噜~
  6. b.gradient_fill_rect(rect, color1, color2)
  7. b.gradient_fill_rect(rect, color1, color2, vertical)
复制代码
3)绘制文字来了~~ draw_text (此处理需要花费时间,因此不建议每画格重绘一次文字。)
  1. # 绘制文字 在位图区域 (x, y, width, height) 或矩形 rect  (Rect 矩形类) 中描绘字符串 str 。
  2. b.draw_text(x, y, width, height, str)
  3. # 能加上 align (0:靠左对齐,1:居中对齐,2:靠右对齐) 来指定对齐方式
  4. b.draw_text(x, y, width, height, str, align)
  5. # 惯例... 吃 rect (Rect 矩形类)
  6. draw_text(rect, str)
  7. draw_text(rect, str, align)
复制代码
text_size 则能获取绘制文字时所需的矩形大小(不包含斜体倾斜的文字部分)
  1. # text_size 获取绘制文字 str (String 字符串) 所需矩形
  2. rect = b.text_size(str)
  3. # 合起来用就是
  4. b.draw_text(b.text_size(str), str)
  5. # 是不是方便多了呢?
复制代码
4)啊咧 涂错了怎办? clear 和 clear_rect 来帮你解决这个问题
  1. # clear 清除整个位图
  2. b.clear

  3. # clear_rect 清除位图区域 (x, y, width, height) 或矩形rect (Rect 矩形类)
  4. b.clear_rect(x, y, width, height)
  5. b.claer_rect(rect)
复制代码


  接下来是一些进阶应用:
1)绘制像素点 set_pixel
  1. # 绘制像素点 设定指定像素点 (x, y) 的色彩 color(Color 色彩类)。
  2. b.set_pixel(x, y, color)
复制代码
2)有繪製當然有獲取像素點的顏色 get_pixel
  1. # 获取指定像素点 (x, y) 的色彩(Color 色彩类)。
  2. b.get_pixel(x, y)
复制代码
3)不知道各位写报告的时候是不是也像我一样善用"拷贝 黏贴"呢?
  拷贝位图 blt
  1. # 拷贝位图 拷贝位图 src_bitmap 的矩形 src_rect 部分到指定的座标 (x, y)
  2. b.blt(x, y, src_bitmap, src_rect)
  3. # 能指定 opacity 透明度 (范围 0~255)
  4. b.blt(x, y, src_bitmap, src_rect, opacity)
复制代码
变形的拷贝 stretch_blt
  1. # 变形的拷贝 拷贝位图 src_bitmap 的矩形 src_rect 部分到指定的矩形中 dest_rect (可能导致变形)
  2. b.stretch_blt(dest_rect, src_bitmap, src_rect)
  3. b.stretch_blt(dest_rect, src_bitmap, src_rect, opacity)
复制代码
4)模糊 blur
  1. # 模糊 对位图执行模糊效果 此处理需要花费时间
  2. b.blur
复制代码
放射型模糊 radial_blur
  1. # 放射型模糊
  2. # angle 指定 0~360 的角度,角度愈大,效果愈圆润
  3. # division 指定 2~100 的分界数,分界数愈大,效果愈平滑 此处理需要花费大量时间
  4. b.radial_blur(angle, division)
复制代码
5)色相变换 hue_change 此处理需要花费时间,另外色彩转换的误差,反复转换可能会导致色彩损失。
  1. # 色相变换 在 360 度内变换位图的色相。
  2. b.hue_change(hue)
复制代码


  再来又是些基本的东西了...
  1. b.width #获取位图宽度
  2. b.height #获取位图高度
  3. b.rect # 获取位图矩形(Rect 矩形类)
  4. b.font # 指定或取得draw_text方法描绘字符串时所使用的字体。(Font 字型类)。
复制代码
虽然 Bitmap 看起来很复杂,却又是整个游戏中不可或缺的元素之一啊。
但是只要精熟这恼人的东西之后,就天不怕地不怕了唷~
(PS: 刚刚被 DZ 脑残到... 重新写 呜呜呜~~)

作者: xchzh100    时间: 2011-1-14 23:09
好人啊!!大好人啊!!!太好了!!!世上真有真情人在!!
作者: IamI    时间: 2011-1-15 09:43
我可以预约么(¬_¬)
作者: 蕾米莉亚·斯卡雷特    时间: 2011-1-24 23:06
Viewport 显示区域

    Viewport 这个东东其实理解起来不难. 比如 显示器的可以显示的区域相对于 Windows 桌面来说就是一个 Viewport . 在 VX 里,  大部分的图片是不需要也不可能需要显示整张图的内容的, 例如 行走图. 这样的情况下就需要使用到 Viewport 了, 截取 行走图 里的一部分来显示, 其他的部分不显示.
    Viewport 的意思通俗点来说就是 需要显示的区域. 图片, 精灵, 窗口什么的都可以设定这样的一个显示区域来显示我们需要显示的内容.
    下面的代码可以来帮助我们来理解这个显示区域的含义.
    1. 创建一个空的 Scene 场景.
  1. class Scene_Viewport < Scene_Base
  2.   def start
  3.     @sprite = Sprite.new
  4.     @sprite.bitmap = Bitmap.new("Graphics/Pictures/143555.jpg")
  5.     @sprite.y = -200
  6.   end
  7.   def update
  8.   end
  9.   def terminate
  10.     @sprite.bitmap.dispose
  11.     @sprite.dispose
  12.   end
  13. end
复制代码
结果是这样的,

    毫无疑问的, 我们知道 Sprite 将这个图片全部都显示到 VX 的窗口上了. 不过这里其实也可以看到整个 VX 窗口也是一个 Viewport.
    2. 使用 Viewport 来显示我们需要的部分.
    将上面的那个脚本段修改一下:
  1.   def start
  2.     @viewport = Viewport.new(0, 0, 400, 400)
  3.     @sprite = Sprite.new(@viewport)
  4.     @sprite.bitmap = Bitmap.new("Graphics/Pictures/143555.jpg")
  5.     @sprite.y = -150
  6.   end
复制代码
测试结果为:
   
    看出区别了么? 由于添加了 Viewport 的限制, 显示图片的时候, 只能显示 400 * 400 大小的区域. 不过这里需要注意一点:添加 Viewport 的限制后, 原来的 Sprite 的坐标是相对于 Viewport 来定义的, 并不是相对于 窗口. 这就意味着, 从 Sprite 的角度来看, Viewport 的指标才是指标原点.
[line]2[/line]
      Viewport 的方法, 属性什么的, 看看 F1 就可以了. 不过需要说明一下 这两个属性 ox, oy .
      ox, oy 这两个坐标可以理解成上面的那个例子里, Sprite 的坐标. 修改 ox oy 的值, 其实就是修改 Sprite 的 x y 的值. 只是 Viewport 的内部不一定是 Sprite .


作者: 一箭烂YiJL    时间: 2011-1-25 00:43
回复 蕾米莉亚·斯卡雷特 的帖子

又是一个马甲?
这里不知是补充还是说明了,
Viewport在控制游戏的整个Sprite(活动块)里扮演了一个不少的地位,
就像在"Spriteset_Battle"和"Spriteset_Map"里面,里面的几个Viewport,(有@viewport1、@viewport2.....)
他们控制了整个各自不用的活动块所看的范围(比如计时器、地图、人物......),
一旦我们用了
Graphics.resize_screen(640, 480)
来加大画面,
在地图和战斗里整个视窗就会留有黑边,这是因为首先战斗场景和地图场景再调用Spriteset_(Battle/Map)
然后@viewport1、@viewport2.....都是没有超过640和480,所以地图元件无法显示在544和416之外。
只要这是把所有viewport类都变为640,480这样就没有黑边,
不过战斗和对话窗口窗口位置会不配合位置而很怪(这是当然的~哈哈哈~)。
作者: 星尘泪    时间: 2011-1-25 06:11
提示: 作者被禁止或删除 内容自动屏蔽
作者: sq333333    时间: 2011-1-26 13:46
支持支持
作者: mxhxl    时间: 2011-2-8 19:21
好东西,顶一个啊!大家顶上去呵~~
作者: 个快快    时间: 2011-2-11 16:37
不错啊,我看了一半了
作者: 铃仙·优昙华院·因幡    时间: 2011-2-11 20:43
Sprite 精灵 和 Plane 平面


Sprite 精灵
       在 RGSS2 里, 显示图片其实有两个方法: 一个是使用 Bitmap 自己的方法 blt  或者是 stretch_blt 来显示, 但是缺点是, 不能再每帧里都 blt. 那样只能让 VX 的 FPS 降低到个位数, 因为这两个方法的执行时间都是很长的. 另一个方法就是用 Sprite 来显示.

       Bitmap 如果指的是图片数据的话, 那么 Sprite 就是画布. Sprite 的理念和 blt 不一样. blt 是在需要的时候再描绘. 所以每次调用这个方法会消耗大量时间. Sprite 实现将图片加载进 Sprite 里, 在需要的时候显示就可以了, 描绘过程比 blt 要效率的多.

      Sprite 的方法有几个, F1 有介绍, 这里就不多说了.

     具体的使用方法, 请参考 Viewport 类 的例子部分.

[line]2[/line]

Plane 平面
     Plane 平面 和 Sprite 精灵 大部分是一致的, 少了几个特定的方法.
     Plane 平面 和 Sprite 精灵 的最大区别在于 如果要显示的图片小于显示区域的话, Plane 平面 会将该图片填充满显示区域, 但是 Sprite 不会. 例子如下:
   
     Plane 平面:
  1. class Scene_Plane < Scene_Base
  2.   def start
  3.     @v = Viewport.new(0, 0, 400, 400)
  4.     @sprite = Plane.new(@v)
  5.     @sprite.bitmap = Bitmap.new("Graphics/Pictures/1.png")
  6.   end
  7.   def update
  8.   end
  9.   def terminate
  10.     @sprite.bitmap.dispose
  11.     @sprite.dispose
  12.   end
  13. end
复制代码
截图:


换成是 Sprite 的话, 效果就是这样:
  1. class Scene_Plane < Scene_Base
  2.   def start
  3.     @v = Viewport.new(0, 0, 400, 400)
  4.     @sprite = Sprite.new(@v)
  5.     @sprite.bitmap = Bitmap.new("Graphics/Pictures/1.png")
  6.   end
  7.   def update
  8.   end
  9.   def terminate
  10.     @sprite.bitmap.dispose
  11.     @sprite.dispose
  12.   end
  13. end
复制代码
截图:


另外, Plane 只能通过 Viewport 来调节 显示坐标.
作者: 龙须菜    时间: 2011-2-12 10:11
支持LZ!!!!!!!!!!!
作者: zh99998    时间: 2011-2-19 09:42
啊拉..99楼了呢

那个ox oy能详解下吗
作者: 蕾米莉亚·斯卡雷特    时间: 2011-2-19 10:58
    具有 ox oy 属性的类共有三个, 分别是 Viewport, Sprite 和 Plane . 不过三者的用法都是一样的.

    如果说把 x, y 这两个坐标看成是相对于窗口来说的话, ox, oy 这两个就是相对于图片上的坐标来说的. 这就意味着, (x, y) 这个坐标系的坐标原点在于窗口的最左上. 而 (ox, oy) 的坐标系原点在于图片的最右上. 两者是不一样的. (ox, oy) 不是对于窗口而言的.

   两个的另一个区别在于, (x, y) 的值决定了这个类(Viewport, Sprite 和 Plane 中的某个)在窗口上的显示位置, 也就是显示坐标. 而 (ox, oy) 的含义是需要显示的图片部分的起始坐标. 但是两者的显示区域的定义都是相同的, 都是由 Viewport 的 Width 和 Height 来决定.

   (ox, oy) 坐标的两一个作用是定义这个精灵(Sprite类) 在旋转时所绕的点.

   -----------------------------------------------------------------------------------------

   (ox, oy) 坐标的用法, 和 Bitmap 的 blt 是一样的, 在一定程度上两者等价(不讨论效率) 比如:

  1. window = Window_Base.new(0, 0, 400, 400) # 建立一个坐标在 (0, 0) 的 宽高都是 400px 的窗口
  2. window.back_opacity = 0 # 取消窗口背景
  3. bitmap = Bitmap.new("1.jpg") # 加载 1.jpg 这张图片
  4. rect = Rect.new(30, 40, 300, 300) # 定义一个矩形, 用于指明后面需要截取的图片区域
  5. window.contents.blt(10, 20, bitmap, rect, 150) # 在相对于窗口的左上角为原点的坐标系的 (10, 20) 位置, 显示 1.jpg 这
  6.                          # 张图片的(30, 40) 这个相对于图片的左上角, 宽高都是 300px 的区域.
复制代码
与这个功能类似的 Sprite 的写法是:

  1. viewport = Viewport.new(0, 0, 300, 300)
  2. sprite = Sprite.new(viewport)
  3. sprite.bitmap = Bitmap.new("1.jpg")
  4. sprite.ox = 30
  5. sprite.oy = 40
  6. sprite.opacity  = 150
复制代码
当然 Sprite 的 src_rect  属性也是可以使用的.
注意一下: 下面的这个脚本虽然在这里和上面的脚本显示的情况是一样的, 但是这里有一个区别.
  1. viewport = Viewport.new(0, 0, 300, 300)
  2. sprite = Sprite.new(viewport)
  3. sprite.bitmap = Bitmap.new("1.jpg")
  4. sprite.x = -30
  5. sprite.y = -40
  6. sprite.opacity  = 150
复制代码
如果在这两个脚本的最后一句添加
  1. sprite.angle = 45
复制代码
也就是旋转的话, 就可以看出区别了. 前一个的旋转点就是我们定义的 (30, 40) 位置, 也就是 (ox, oy) .
后一个虽然在没有旋转之前是正常的, 但是在旋转之后就不一样了. 理由在于, 后一个仅仅是定义了一个 (x, y) . (ox, oy) 依旧是默认的 ( 0, 0 ), 也就是图片的左上角. 两者的旋转点式不一样的.  




欢迎光临 Project1 (https://rpg.blue/) Powered by Discuz! X3.1