设为首页收藏本站|繁體中文

Project1

 找回密码
 注册会员
搜索
楼主: 八云紫
打印 上一主题 下一主题

[RMVX发布] 新手教程--从0开始学RGSS2(2013-09-21 修复索引地址)

  [复制链接]

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

11
 楼主| 发表于 2010-11-8 21:07:31 | 显示全部楼层
修改存档位置

      目标: 修改存档位置, 做到自定义存档的保存位置.
      修改过程:
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, 加了点经验.)

点评

在你的脚本Main之前添加 Dir.mkdir "Save" unless File.directory? "Save" 就可以了  发表于 2010-11-12 09:14
如果不想出现这个错误的话可以在检测不到文件夹的时候自动创建文件夹。  发表于 2010-11-12 08:52

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

12
 楼主| 发表于 2010-12-18 23:38:06 | 显示全部楼层
本帖最后由 魔女真利亞 于 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
复制代码
  • 1. 父类
        一般的窗口可以分成两大类. 一个是带有光标的窗口, 还有一个就是普通的窗口. 两者的父类是不一样的. 一般带有光标的窗口都是直接或者间接的继承于 Window_Selectable . 不带有光标的窗口继承于 Window_Base. 不过需要了解的一点是,所有的窗口的最终父类只能是 Window 类.. 但是 Window 的实现是在其 dll 里的, 所以无源码.
  • 2. 初始化
        对于无光标的窗口: super 都是需要写的. 参数看父类就可以了.
        对于有光标的窗口: 除了要使用 super 以外, 还需要注意这几个变量.
            

              
    • @item_max: 选择项的总个数.
              
    • @column_max: 列数. 也就是说如果等于 2 的话, 会出现左右分开描绘选择项的效果. 具体的可以参照 Scene_Item.
              
    • @index: 当前选择项. 用于取得当前玩家选择的序号.(注意:不是选项内容, 而是序号)
              
  • 3. 刷新
        refresh 方法不是必须的. 大多数的 Window 的 refresh 方法只是在 初始化或者需要刷新的时候才调用. 而不像 update 方法那样每帧调用. refresh 方法里常用的语句就是
    1. self.contents.clear
    复制代码
    清除画布全部内容.只有这样,在接下来需要描绘的内容才不会重复的画在窗口上.
  • 4. 描绘
        窗口类的描述都是在Window_Base 里创建的一个画布上进行的. 也就是 self.contents . 常用的描绘方法参考 Window_Base .需要注意的是, 窗口内容描绘是非常占用刷新时间的. 一般一个描绘内容没帧刷新的话, FPS 就惨不忍睹了.
  • 5. 常用属性
        窗口类常用属性请查找 F1 帮助里的 Window 部分. 所有的 Window 类都可以使用里面的属性.

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

13
 楼主| 发表于 2010-12-19 09:30:52 | 显示全部楼层
本帖最后由 魔女真利亞 于 2010-12-19 09:34 编辑

回复 一箭烂YiJL 的帖子

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

点评

ls的第四条是注明,你的第四条是没明白  发表于 2011-7-24 20:43

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

14
 楼主| 发表于 2010-12-19 10:12:21 | 显示全部楼层
本帖最后由 魔女真利亞 于 2010-12-19 10:13 编辑

回复 一箭烂YiJL 的帖子

1. 好吧。我投降。仅仅只是想说明下被人遗忘的 Window。Window_Base initialize super 居然是空的,这个真没注意~~
2. 有的时候,教程没必要说的太明了。能举一反三才是最好的教程。虽然咱的是渣教程。
3. 这个是中级教程了哦。不过那个 dll 其实只是提一下罢了。没有说它是重点。

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

15
 楼主| 发表于 2010-12-19 10:24:19 | 显示全部楼层
回复 禾西 的帖子

感觉上 Window_Base 就是虚类。

好多 V

Window_Base 只是中间层。

禾西酱早~~

点评

很有礼貌似的,我也要叫声:"前辈早,打扰您了!"嘻~  发表于 2010-12-19 10:28

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

16
 楼主| 发表于 2010-12-19 10:30:12 | 显示全部楼层
回复 一箭烂YiJL 的帖子

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

3. 真的要写什么 Window_Base 啥啥方法的话,那就免了吧。那个可是 F1 的说。而且没啥意义。自己看看,领悟才是最好的~~

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

17
 楼主| 发表于 2010-12-19 10:43:36 | 显示全部楼层
回复 一箭烂YiJL 的帖子

1. 有么?有么?
2. (被吃掉了。)
3. 不要在意细节。不过真实的结果就是,真的是继承 Window , 虽然是 C类。

点评

前辈愿意交个朋友吗?  发表于 2010-12-19 10:46

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

18
 楼主| 发表于 2010-12-19 22:59:57 | 显示全部楼层
本帖最后由 八云紫 于 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)




图块

    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



参考资料:
1. Kiss姐姐的 TileMap脚本
2. 柳大的 还原Tilemap类脚本
3. 胃君 的主动元件解说

点评

明显长度为9的说...  发表于 2013-4-24 06:46
其实是之前写扩张图块的时候研究的说, 而且不是很透彻~  发表于 2010-12-20 23:37
图块闪烁么? 知道了呢~~~ Thanks~~~  发表于 2010-12-20 11:56
flash_data 在 66RPG 的不少战棋游戏里有应用,如叶子的《阿尔西斯战记》。  发表于 2010-12-20 00:55

评分

参与人数 2星屑 +12 收起 理由
DeathKing + 6 八云大人对Titemap有充分的研究啊…….
苏小脉 + 6 精品

查看全部评分


《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

19
 楼主| 发表于 2010-12-20 20:32:53 | 显示全部楼层
本帖最后由 八云紫 于 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 脚本的用法都是这样的. 只是用途不一样. 这个看默认脚本说明就可以了.



   
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 22:59

评分

参与人数 2星屑 +54 收起 理由
一箭烂YiJL + 14 流程图确实好,可是太大了~= =&quot;.
zh99998 + 40 居然真的画流程图了

查看全部评分


《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

Lv2.观梦者

神隐的主犯

梦石
0
星屑
263
在线时间
271 小时
注册时间
2008-2-22
帖子
7691

贵宾

20
 楼主| 发表于 2010-12-22 00:06:50 | 显示全部楼层
本帖最后由 八云紫 于 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异常 部分.


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. 没有了吧, 大概.

点评

在退出的时候除了用exit之外还可以用$scene = nil,从而打破了那个while语句循环,然后main脚本继续往下执行,于是就结束退出了。  发表于 2010-12-23 23:24
如果.new被执行的时候就自动执行了相应Scene的installize部分内容,其中包括建立窗口等。 然后$scene.main才真正执行了main函数啊喵!  发表于 2010-12-23 23:23
$scene.main while $scene != nil 这一句,前面的 $scene = Scene_Title.new 就是预先定义好跳转标题画面的语句。 $scene = XXX.new等于设定了下一个场景的目标。  发表于 2010-12-23 23:22

《天空之城 —— 破碎的命运》
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册会员

本版积分规则

拿上你的纸笔,建造一个属于你的梦想世界,加入吧。
 注册会员
找回密码

站长信箱:[email protected]|手机版|小黑屋|无图版|Project1游戏制作

GMT+8, 2024-5-14 23:52

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表