查看: 3644|回复: 3

[RMVA发布] VA无限地图层自动绘制脚本

[复制链接]

巫女会长

梦石
0
星屑
60
在线时间
1028 小时
注册时间
2009-10-24
回帖
3425

贵宾

发表于 2013-12-5 13:59:14 | 显示全部楼层 |阅读模式

加入我们,或者,欢迎回来。

您需要 登录 才可以下载或查看,没有账号?注册会员

×
本帖最后由 dukesward 于 2013-12-6 08:56 编辑

VA无限地图图层脚本


【简介】众所周知VA的地图图层设置很坑爹。首先,不算事件的话总共只有两层:所有的自动图块算一层,BCDE图块加起来算一层。这就使得要绘制多层复杂地图变得极端困难。当然,富有智慧的VA众毫不气馁想出了很多好办法来解决这个障碍,其中我可以列举出的有这几种:

1. 第一种也是用的最多最广泛技术含量最小的:利用事件。事件是个好东西,因为它可以在任意图快上显示一张32*32的图片,这使得传统意义的“第三层”在VA里出现了也即事件层。事件党乐此不疲的为地图添加各种仅用来显示图块的事件。可惜,这种方法虽然方便使用,但是缺点颇多。首先,很明显你只能额外添加一层,如果要第四层第五层(什么,你问要那么多干嘛?我不知道,但这是游戏制作者的权利。)就不可能了。其次,耗时耗力,你画一个32*32的图块固然容易,但是如果要画更大的呢?还得一个个地添加事件,地图上弄了一大堆事件,既繁琐又不容易修改(内存啥的还先不考虑进去)。所以事件地图党多半画的苦不堪言。
2. 稍微有点技术含量的,所谓的图层无限叠加脚本。这个脚本确实不错,它的原理是你分开画N张图,它帮你拼起来,使它看上去像一张。穿透,天气,阴影当然都能搞,反正几张图拼一块嘛。但是问题也多,首先这N张图不在一个地图里,你画的时候就得不停地调试,画一两块运行一下看看(当然你拿PS试拼也行不过你还得截图吧),两三张图还好,图一多那叫一个累。而且更麻烦的是因为地图id不同你永远不可能从A图直接走到B图的图块,虽然他们看上去根本就在一起!所以得设置事件当传送门,一不当心特别容易出bug。
3. 传说中的重写Tilemap类。众所周知Tilemap是一个不完全开放的类,你知道用法但不知道它怎么运行的,所以仅仅修改默认方法是不会改变地图画法的。这个相对最安全省事但是最考验脚本功底,因为毕竟重写不是人人能干的,当然你找现成的也行,不过我目前没见过完美的。用起来也都特别麻烦。

以上几种不外乎用的最广的,当然你有更好方法欢迎补充。以下我介绍的是我最近无聊(咦 开发的脚本,它的原理是基本上和用事件来画没什么区别(sprite,你知道的),但是修正了事件绘图中几乎所有的不足处,包括图层数限制,不能一次绘制多图块和不能绘制自动图块等等。而且理论上不限于你输入VA的图块,外部图块也可以。好了话不多说,下面我给出使用方法和几个小范例。

【使用方法】其实就是用哈希和数组输入你想要的图块位置、图块类型和图层信息等。它看上去有点让人头晕,但是只要你知道了哈希和数组怎么使用,就能很轻松的画出你想要的任何图块(肯定比手动添加事件这种土方法强得多)。

这个脚本的开头有一个数组叫NORMAL_SET,里面记录了普通图块(即非自动图块)对应的字母(不知道的话看数据库,那些A1,A2就是了)。这个数组你不用去修改它,你只要清楚你要画的图块对应哪个字母就行了。关键的就是下面的这个名叫MAP_LAYER的哈希表了。
基本的使用方法是在哈希表中输入:
id => { layer1 => [ [第一个图块组], [第二个图块组]... ], layer2 => [ [第一个图块组], [第二个图块组]... ], ... }
看上去哈希套哈希数组套数组,其实理解了很容易:首先大的哈希里 id 是你的地图id。小的哈希里layer1,layer2 这些是你要添加的图层。注意是要添加的,不是原有的图层,比如你的地图有两层,你要往上加第三层,那么layer应该是1而不是3。接着里面的大数组是你想要在对应layer里画的所有图块。所以理论上你可以添加无数个layer,并且在每个layer中加入无数个图块。好了接下来最重要也是最难理解的部分,是每个图块自己的数组。
大致的写法是:[ 图块id(要用string,id对应那个A1, A2, B,这些见数据库),起始图块对应地图上的坐标数组(画地图的时候移动鼠标就会在下方显示),图块对应图块组的坐标(这个是你要画的图块在图块组的位置),图块大小(一张32*32的就是[1, 1], 32*64的就是[1, 2]),能否通行(这边只写了全方位通行)]

举例: MAP_LAYER = { 1 => { 1 => [ [ "B", [0, 0], [1, 1], [2, 2], true]] } } 意思是你要在id为1的地图里,添加一层,在该层中绘制一个图块,图块起始位置对应地图坐标 [0, 0], 起始图块来源为B图块组坐标[1, 1]的那张,图块大小为64*64。


【范例】好了如果还一头雾水的话我们来看几个简单的范例。
首先在这张图里我想在圆圈处(也就是桌上,对应坐标[8, 4])画一个茶杯,图源为图块组C的第一行第六个,所以对应图块组的坐标是[5, 0] (注意不是[6, 0], 因为第一张是[0, 0]) 这个很简单用事件就能轻松实现,如果用脚本的话需要输入的是: 1 => { 1 => [ [ "C", [8, 4], [0, 5], [1, 1], false]] }。

截图1.png

接下来我们画个复杂点的,在墙上画个壁炉吧。就画图块组B里第16行的这个,因为这个壁炉是个96*64的图块,如果用事件画就要新建6个事件。但是在脚本里更改一下图块大小数组就行了。于是这么写:  1 => { 1 => [ [ "B", [6, 0], [0, 16], [3, 2], false]] } 就行了,注意因为是96*64所以图块大小数组是[3, 2]。瞧,多简单。

截图2.png

最后来一个复杂点的,让我们在这边的二楼画一个96*96的向楼下一样的红地毯自动图块(该图块在图块组A2中),再在自动图块上画一张图块组C里的红布桌台吧。首先要说的是,画自动图块的数组输入和一般图块有点不同。如果你在数据库把自动图块A2点开会看到是这样的(如下图)。每个图块都有那么一堆不知道怎么画进地图。别担心,对于自动图块你不用给出具体的坐标,只要给出这个元件是在哪行哪列就行了,比如我们要的红地毯在A2的第四行第三列,那就输入[3, 4]就行了。

截图3.png

于是首先画自动地图红地毯: 1 => { 1 => [ ["A2", [0, 0], [3, 4], [3, 3], true]] }, 这里你可以看到这个脚本的优点,下边的这张是不用脚本直接画,当然你说你会用shift神技,你可以试试,就会发现shift就只能画中心,边缘是解决不了的。

截图4.png

接着我们来画红布桌台,它就在C图块组的24行第一个,当然一开始数起来可能有些费力不过用多了就熟悉了。
1 => { 1 => [ ["A2", [0, 0], [3, 4], [3, 3], true]], 2 => [["C", [0, 0], [0, 23], [3, 2], false]] } 这里因为我们想要桌台在红地毯上面所以桌台的图层用了2。好了大功告成,让我们来看看效果吧!

截图5.png

【附上脚本】老规矩,在main前插入。

[pre lang="ruby" line="1"]#encoding:utf-8
=begin
#==============================================================================
# ■ 多层地图脚本 by dukesward
#-----------------------------------------------------------------------------
使用方法:

基本的使用方法是在哈希表中输入:
id => { layer1 => [ [第一个图块组], [第二个图块组]... ], layer2 =>
[ [第一个图块组], [第二个图块组]... ], ... }
举例: MAP_LAYER = { 1 => { 1 => [ [ "B", [0, 0], [1, 1], [2, 2], true]] } }
意思是你要在id为1的地图里,添加一层,在该层中绘制一个图块,图块起始位置对应地图坐标
[0, 0], 起始图块来源为B图块组坐标[1, 1]的那张,图块大小为64*64。

#==============================================================================
=end

NORMAL_SET = ["B", "C", "D", "E"]

MAP_LAYER = {

  1 => { 1 => [["B", [6, 0], [0, 16], [3, 2], false]],
         2 => [["A2", [0, 0], [3, 4], [3, 3], true]],
         3 => [["C", [0, 0], [0, 23], [3, 2], false]]
        }

}

class Game_Map
  #--------------------------------------------------------------------------
  # ● 定义实例变量
  #--------------------------------------------------------------------------
  attr_accessor   :layers                   # 地图图层表
  #--------------------------------------------------------------------------
  # ● 设置
  #--------------------------------------------------------------------------
  alias setup_old setup
  def setup(map_id)
    setup_old(map_id)
    create_layer_table
    clear_layer_table
  end
  #--------------------------------------------------------------------------
  # ● 生成图层数据表
  #--------------------------------------------------------------------------
  def create_layer_table
    @layers = Table.new(@map.data.xsize, @map.data.ysize,
    MAP_LAYER.keys.include?(@map_id) ? MAP_LAYER[@map_id].keys.size : 1)
  end
  #--------------------------------------------------------------------------
  # ● 清空图层数据表
  #--------------------------------------------------------------------------
  def clear_layer_table
    for x in 0...@layers.xsize
      for y in 0...@layers.ysize
        for z in 0...@layers.zsize
          @layers[x, y, z] = 1
        end
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 通行检查
  #     bit : 判断通行禁止与否的字节(请参照二进制运算)
  #--------------------------------------------------------------------------
  def check_passage(x, y, bit)
    all_tiles(x, y).each do |tile_id|
      flag = tileset.flags[tile_id]
      return false if !all_layer_pass(x, y)
      next if flag & 0x10 != 0            # [☆] : 不影响通行
      return true  if flag & bit == 0     # [○] : 可以通行
      return false if flag & bit == bit   # [×] : 不能通行
    end
    return false                          # 不能通行
  end
  #--------------------------------------------------------------------------
  # ● 图层检查
  #--------------------------------------------------------------------------
  def all_layer_pass(x, y)
    return true if !MAP_LAYER.keys.include?(@map_id)
    MAP_LAYER[@map_id].keys.each do |i|
      return false if @layers[x, y, i-1] == 0
    end
  end
end


class Spriteset_Map
  #--------------------------------------------------------------------------
  # ● 生成人物精灵
  #--------------------------------------------------------------------------
  alias create_old_char create_characters
  def create_characters
    create_old_char
    create_map_layer
  end
  #--------------------------------------------------------------------------
  # ● 创建地图层
  #--------------------------------------------------------------------------
  def create_map_layer
    @event_sprites = []
    if MAP_LAYER.keys.include?(@map_id)
      MAP_LAYER[@map_id].keys.each_with_index do |i|
        @event_sprites.push(Spriteset_Tile.new(@viewport1, @map_id, i))
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新画面
  #--------------------------------------------------------------------------
  alias update_old update
  def update
    update_old
    update_spriteset_tile
  end
  #--------------------------------------------------------------------------
  # ● 更新图块精灵组
  #--------------------------------------------------------------------------
  def update_spriteset_tile
    @event_sprites.each do |spriteset|
      spriteset.update_all_sprites
    end
  end
  #--------------------------------------------------------------------------
  # ● 释放
  #--------------------------------------------------------------------------
  alias dispose_old dispose
  def dispose
    dispose_old
    dispose_spriteset_tile
  end
  #--------------------------------------------------------------------------
  # ● 释放图块精灵组
  #--------------------------------------------------------------------------
  def dispose_spriteset_tile
    @event_sprites.each do |spriteset|
      spriteset.dispose_all_sprites
    end
  end
end

class Spriteset_Tile
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(viewport, id, layer)
    @tile_ary = MAP_LAYER[id][layer]
    @layer = layer
    @tile_sprites = []
    create_sprites(viewport)
  end
  #--------------------------------------------------------------------------
  # ● 生成精灵
  #--------------------------------------------------------------------------
  def create_sprites(viewport)
    @tile_ary.each do |array|
      array[0].include?("A") ? create_auto(viewport, array) :
      create_normal(viewport, array)
    end
  end
  #--------------------------------------------------------------------------
  # ● 生成普通精灵
  #--------------------------------------------------------------------------
  def create_normal(viewport, array)
    for x in 0...array[3][0]
      for y in 0...array[3][1]
        @tile_sprites.push(Sprite_Tile.new(viewport, array[0],
        [array[1][0]+x, array[1][1]+y], [array[2][0]+x, array[2][1]+y],
        @layer, 0))
        $game_map.layers[x, y, @layer-1] = array[4] ? 1 : 0
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 生成自动精灵
  #--------------------------------------------------------------------------
  def create_auto(viewport, array)
    for x in 0...array[3][0]*2
      for y in 0...array[3][1]*2
        @tile_sprites.push(Sprite_Tile.new(viewport, array[0],
        [array[1][0]+x, array[1][1]+y], [array[2][0], array[2][1]], @layer,
        [[x+1, y+1], [array[3][0], array[3][1]]]))
        $game_map.layers[x, y, @layer-1] = array[4] ? 1 : 0
      end
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新所有精灵
  #--------------------------------------------------------------------------
  def update_all_sprites
    @tile_sprites.each do |sprite|
      sprite.update
    end
  end
  #--------------------------------------------------------------------------
  # ● 释放所有精灵
  #--------------------------------------------------------------------------
  def dispose_all_sprites
    @tile_sprites.each do |sprite|
      sprite.dispose
    end
  end
end

class Sprite_Tile < Sprite_Base
  #--------------------------------------------------------------------------
  # ● 初始化对象
  #--------------------------------------------------------------------------
  def initialize(viewport, tileset, map_pos, tile_pos, layer, tag)
    super(viewport)
    @layer = layer
    @bitmap = get_tile_id(tileset)
    @map_pos = map_pos
    @tile_pos = tile_pos
    @auto_tile = tileset.include?("A")
    @depth = 0
    get_tag(tag) if tag != 0
    update
  end
  #--------------------------------------------------------------------------
  # ● 获取图块标签
  #--------------------------------------------------------------------------
  def get_tag(tag)
    @tag = tag[0]
    @prod = [tag[1][0]*2, tag[1][1]*2]
    set_auto_bitmap
  end
  #--------------------------------------------------------------------------
  # ● 更新自动元件
  #--------------------------------------------------------------------------
  def set_auto_bitmap
    @sx = (@tile_pos[0]*2-2)*32; @sy = (@tile_pos[1]*3-2)*32;
    self.bitmap = @bitmap
    assign_tile(0, 0) if @tag == [1, 1]
    assign_tile(48, 48) if @tag == @prod
    if @tag[0] == 1 && @tag[1] != 1
      assign_tile(0, 48) if @tag[1] == @prod[1]
      assign_tile(0, 16) if @tag[1] != @prod[1] && (@tag[1]-@prod[1])%2 == 0
      assign_tile(0, 32) if @tag[1] != @prod[1] && (@tag[1]-@prod[1])%2 == 1
    elsif @tag[1] == 1 && @tag[0] != 1
      assign_tile(48, 0) if @tag[0] == @prod[0]
      assign_tile(16, 0) if @tag[0] != @prod[0] && (@tag[0]-@prod[0])%2 == 0
      assign_tile(32, 0) if @tag[0] != @prod[0] && (@tag[0]-@prod[0])%2 == 1
    end
    if (@tag[0] > 1) && (@tag[1] > 1) && @tag != @prod
      assign_tile(16, 48) if @tag[1] == @prod[1] && ((@tag[0]-@prod[0])%2 == 0)
      assign_tile(32, 48) if @tag[1] == @prod[1] && ((@tag[0]-@prod[0])%2 == 1)
      assign_tile(48, 16) if @tag[0] == @prod[0] && ((@tag[0]-@prod[0])%2 == 0)
      assign_tile(48, 32) if @tag[0] == @prod[0] && ((@tag[0]-@prod[0])%2 == 1)
      if @tag[1] != @prod[1] && @tag[0] != @prod[0]
        assign_tile(16, 16) if ((@tag[0]-@prod[0])%2 == 0) &&
        ((@tag[1]-@prod[1])%2 == 0)
        assign_tile(32, 16) if ((@tag[0]-@prod[0])%2 != 0) &&
        ((@tag[1]-@prod[1])%2 == 0)
        assign_tile(16, 32) if ((@tag[0]-@prod[0])%2 == 0) &&
        ((@tag[1]-@prod[1])%2 != 0)
        assign_tile(32, 32) if ((@tag[0]-@prod[0])%2 != 0) &&
        ((@tag[1]-@prod[1])%2 != 0)
      end
    end
    self.ox = 8
    self.oy = 16
  end
  #--------------------------------------------------------------------------
  # ● 更新自动元件
  #--------------------------------------------------------------------------
  def assign_tile(x, y, depth = 0)
    self.src_rect.set(@sx+=x, @sy+=y, 16, 16)
    @depth = depth
  end
  #--------------------------------------------------------------------------
  # ● 获取图块id
  #--------------------------------------------------------------------------
  def get_tile_id(tileset)
    return tileset_normal(tileset) if NORMAL_SET.include?(tileset)
    return tileset_auto(tileset) if tileset.include?("A")
  end
  #--------------------------------------------------------------------------
  # ● 获取指定图块 ID 的图块组
  #--------------------------------------------------------------------------
  def tileset_normal(tileset)
    case tileset
    when "B"
      return Cache.tileset($game_map.tileset.tileset_names[5])
    when "C"
      return Cache.tileset($game_map.tileset.tileset_names[6])
    when "D"
      return Cache.tileset($game_map.tileset.tileset_names[7])
    when "E"
      return Cache.tileset($game_map.tileset.tileset_names[8])
    end
  end
  #--------------------------------------------------------------------------
  # ● 获取指定自动图块 ID 的图块组
  #--------------------------------------------------------------------------
  def tileset_auto(tileset)
    tile_word = tileset.split("A")[-1].to_i - 1
    return Cache.tileset(tileset) if tile_word > 4
    return Cache.tileset($game_map.tileset.tileset_names[tile_word])
  end
  #--------------------------------------------------------------------------
  # ● 更新画面
  #--------------------------------------------------------------------------
  def update
    super
    update_bitmap
    update_position
  end
  #--------------------------------------------------------------------------
  # ● 更新位图
  #--------------------------------------------------------------------------
  def update_bitmap
    @sx = (@tile_pos[0] + @tile_pos[1]/16.to_i * 8) * 32 if !@sx
    @sy = @tile_pos[1]%16 * 32 if !@sy
    self.bitmap = @bitmap
    if !@auto_tile
      self.src_rect.set(@sx, @sy, 32, 32)
      self.ox = 16
      self.oy = 32
    end
  end
  #--------------------------------------------------------------------------
  # ● 更新位置
  #--------------------------------------------------------------------------
  def update_position
    if @auto_tile
      self.x = (@map_pos[0] - $game_map.display_x) * 16 + 8
      self.y = (@map_pos[1] - $game_map.display_y) * 16 + 16
    else
      self.x = (@map_pos[0] - $game_map.display_x) * 32 + 16
      self.y = (@map_pos[1] - $game_map.display_y) * 32 + 32
    end
    self.z = 5 * (@layer - 1) + @depth
  end
  #--------------------------------------------------------------------------
  # ● 释放
  #--------------------------------------------------------------------------
  def dispose
    self.bitmap.dispose
    super
  end
end[/pre]

评分

参与人数 3星屑 +8 赞 +1 收起 理由
cinderelmini + 1 精品文章
赛露休斯 + 7 期待XP版
3106345123 + 1 精品文章

查看全部评分

进击的屌丝
中二同好会长

梦石
0
星屑
50
在线时间
628 小时
注册时间
2013-10-8
回帖
1195

贵宾

发表于 2013-12-5 14:37:01 | 显示全部楼层
感觉很厉害的说,不过这种事情其实只要使用Overlay Mapping就可以了吧?

点评

如果你说的那个就是画几个地图拼一起的,不觉得画起来很累吗  发表于 2013-12-5 22:35
吾乃不明事理之人,疯狂之人。
↑
↑
才怪咧,当时是怎么想到这么中二的句子233
回复

使用道具 举报

唯一的信徒

梦石
0
星屑
1697
在线时间
1359 小时
注册时间
2013-1-29
回帖
1537
发表于 2013-12-5 23:14:01 | 显示全部楼层
抱歉我喜欢Overlay Mapping怎么破(装逼画地图好工具

点评

这点确实是最大缺点但技术上目前来说是无法克服了。。当然很多人抱怨输入数据库的图块太少,那就做小点的图块用cache直接读,数起来可能方便点  发表于 2013-12-6 15:08
LBQ
↓ 这种方法要数坐标除非事件大于3*3否则我还是打算用事件._. | 数坐标这种事情估计没有多少人愿意做的  发表于 2013-12-6 09:15
只是提供一种新方法,不喜欢可以无视,还有人喜欢C++不喜欢ruby怎么破  发表于 2013-12-6 07:24
『我只是一个正在潜心修炼的渣乐师罢了』
Dear Time\(^o^)/~


假如上面的图片挂了的话麻烦各位去发个帖 @ 一下 orzFly 让他修复 deartime
回复

使用道具 举报

梦石
0
星屑
167
在线时间
434 小时
注册时间
2009-1-1
回帖
557
发表于 2013-12-6 14:53:38 | 显示全部楼层
能不能移植XP?或者做XP版无限地图图层脚本?
感觉XP做一些复杂的地图,3图层仍旧不够用

点评

我还是习惯XP的地图,所以会需要XP无限图层脚本,非常感谢  发表于 2013-12-6 15:13
移植倒是应该不难,如果你真的有需要我可以研究下,很久没玩xp了。  发表于 2013-12-6 15:02
最近在研究XAS
回复

使用道具 举报

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

本版积分规则

Powered by Discuz! X5.0 © 2001-2026 Discuz! Team.

在本版发帖返回顶部