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

Project1

 找回密码
 注册会员
搜索
查看: 7680|回复: 40

[原创发布] 地图截图工具v1.3(猫儿的RMXP工具包第九弹)更新!

[复制链接]

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
22385
在线时间
2335 小时
注册时间
2005-10-15
帖子
1160

开拓者

发表于 2017-10-8 01:02:55 | 显示全部楼层 |阅读模式

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

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

x
本帖最后由 SailCat 于 2017-11-6 01:24 编辑

好像看到有人写过,但是为了给地图截个图自写一个Tilemap,是不是有点太小题大做了……想过渲染Tilemap有多麻烦吗?

于是写了下面这个。Tilemap是什么能吃吗……
500X500的地图亲测可用。

单框脚本,俺就不发测试工程了,免得积分清零了导致各位下载不能。
更新:
v1.3  
再加速PNG处理(加速约6倍,果然优化无上限)
支持M(纯地图)、U(ULDS)、T(攻略)、A(航拍)四种截图模式
全脚本加注释(详细程度可比默认系统,700行脚本有1/3是注释……)

v1.2  
优化缓存,现支持RM理论上限(500X500)的导出;优化进度显示功能

v1.1  
加速PNG处理



执行效率(在M模式下):
90X90以下的地图秒过,250X250大约需要6秒,500X500大约需要25秒(Surface Book i7-6600M CPU测试数据)
我的204张地图的大坑,M模式下47秒全部输出完,其他模式1分多一点点。

RUBY 代码复制
  1. #==============================================================================
  2. # ■ Map_Snapshot 1.3 (Build 171106)
  3. #------------------------------------------------------------------------------
  4. #  地图截图工具 by SailCat @ Project1
  5. #   该程序能将地图转换为PNG图片格式,包括三层图块和用图块ID绘制的事件,遮挡正确。
  6. #   使用方法:
  7. #   1. 插入本脚本到Game_Temp(不是Main)之前。
  8. #   2. 在插入脚本的下方,输入代码:
  9. #      ms = Map_Snapshot.new
  10. #      ms.set_scale(8) # 将地图缩小8倍,可以设定的值有1、2、4、8,默认为1
  11. #      ms.set_mode("U") # 设置输出模式为 ULDS分层模式
  12. #        支持的模式有"M"普通地图、"U"ULDS分层模式、"T"攻略模式、"A"航拍模式
  13. #        M模式:默认的模式、渲染F5、F6、F7层的全部和F8层的地图元件事件
  14. #        U模式:内容同M模式,但将一张地图渲染成主角上层和主角下层的两张截图
  15. #        T模式:在M模式的基础上,再加上不移动的事件,事件取第1页、初始位置
  16. #        A模式:在M模式的基础上,再加上所有的非空事件,事件取第1页、初始位置
  17. #        注:使用U模式会将地图放大率强制设为1:1
  18. #      ms.snap(6) # 转换Map006到图片
  19. #      ms.snaps(1..19) # 转换Map001到Map019到图片
  20. #      ms.snaps([1, 3, 7, 15]) # 转换Map001, Map003, Map007, Map015到图片
  21. #      ms.snap_all # 转换工程中所有地图到图片
  22. #      exit # 转换工程后退出
  23. #   3. 注释掉700行以下的内容可以屏蔽此工具,正常测试游戏。
  24. #   4. 该工具不修改工程内含文件,故执行完后不需要退出重新打开工程。
  25. #==============================================================================
  26.  
  27. #==============================================================================
  28. # ■ Scene_Reporter
  29. #------------------------------------------------------------------------------
  30. #  通用工具处理进度显示模块。
  31. #==============================================================================
  32. module Scene_Reporter
  33.   @sprite = nil
  34.   @message = ""
  35.   @value = ""
  36.   #--------------------------------------------------------------------------
  37.   # ● 更新显示
  38.   #     message: 主要信息(滚屏)
  39.   #     value: 次要信息(不滚屏)
  40.   #--------------------------------------------------------------------------  
  41.   def self.update(message = "", value = "")
  42.     # 初期化精灵
  43.     if @sprite == nil
  44.       @sprite = Sprite.new
  45.       @sprite.bitmap = Bitmap.new(640, 480)
  46.       @sprite.bitmap.font.name = "楷体"
  47.       @sprite.bitmap.font.size = 20
  48.       @start = Time.now
  49.       @elapse = -1
  50.     end
  51.     # 信息和所显信息完全相同的情况下,直接返回
  52.     return if (message == "" or @message == message) and @value == value
  53.     # 主信息不相同的情况下,滚屏
  54.     if message != "" and @message != message
  55.       @sprite.bitmap.blt(0, 0, @sprite.bitmap, Rect.new(0, 32, 640, 448))
  56.       @message = message
  57.       @elapse = 0
  58.     end
  59.     # 显示已用时间
  60.     elapse = Integer(Time.now - @start)
  61.     if @elapse < elapse
  62.       @elapse = elapse
  63.       min = @elapse / 60
  64.       sec = @elapse % 60
  65.       @sprite.bitmap.fill_rect(580, 0, 60, 32, Color.new(0, 0, 0))
  66.       @sprite.bitmap.draw_text(580, 0, 60, 32, sprintf("%2d:%02d", min, sec))
  67.     end
  68.     # 显示信息
  69.     @sprite.bitmap.fill_rect(0, 448, 640, 32, Color.new(0, 0, 0))
  70.     @sprite.bitmap.draw_text(0, 448, 640, 32, @message + value)
  71.     @value = value
  72.     # 更新画面
  73.     Graphics.update
  74.   end
  75.   #--------------------------------------------------------------------------
  76.   # ● 结束处理
  77.   #     message: 最后的信息
  78.   #--------------------------------------------------------------------------  
  79.   def self.close(message)
  80.     # 更新最后的信息
  81.     Scene_Reporter.update(message)
  82.     # 等待按键结束
  83.     loop do
  84.       Graphics.update
  85.       Input.update
  86.       # 按下 C 键或 B 键的情况下
  87.       if Input.trigger?(Input::C) or Input.trigger?(Input::B)
  88.         break
  89.       end
  90.     end
  91.     # 释放精灵
  92.     if @sprite != nil
  93.       @sprite.bitmap.dispose
  94.       @sprite.dispose
  95.     end
  96.   end
  97. end
  98. #==============================================================================
  99. # ■ PNG
  100. #------------------------------------------------------------------------------
  101. #    PNG文件输出模块 by SailCat
  102. #    鸣谢:SixRC(取内存地址相关代码)
  103. #    用法:bitmap.output_png(filename)
  104. #     filename: 输出的文件名,可含路径
  105. #==============================================================================
  106. module PNG
  107.   #--------------------------------------------------------------------------
  108.   # ● API 声明
  109.   #--------------------------------------------------------------------------  
  110.   CopyMemory_pi = Win32API.new('kernel32', 'RtlMoveMemory', 'pii', 'i')
  111.   CopyMemory_ii = Win32API.new('kernel32', 'RtlMoveMemory', 'iii', 'i')
  112.   CopyMemory_ip = Win32API.new('kernel32', 'RtlMoveMemory', 'ipi', 'i')
  113.   CallWindowProc_p = Win32API.new('user32.dll','CallWindowProc','pppii','i')
  114.   CallWindowProc_i = Win32API.new('user32.dll','CallWindowProc','ppiii','i')
  115.   #--------------------------------------------------------------------------
  116.   # ● API 调用代码
  117.   #--------------------------------------------------------------------------  
  118.   RGBAConvert = [
  119.     0x55, 0x8B, 0xEC, 0x8B, 0x45, 0x08, 0x33, 0xC9,
  120.     0xEB, 0x0D, 0x8B, 0x10, 0x0F, 0xCA, 0xC1, 0xCA,
  121.     0x08, 0x89, 0x10, 0x41, 0x83, 0xC0, 0x04, 0x3B,
  122.     0x4D, 0x0C, 0x72, 0xEE, 0xC9, 0xC2, 0x10, 0x00].pack('C*')
  123.   GetAddress = [
  124.     0x8B, 0x74, 0x24, 0x08, 0x8B, 0x36, 0x8B, 0x76,
  125.     0x08, 0x8B, 0x76, 0x10, 0x8B, 0x7C, 0x24, 0x04,
  126.     0x89, 0x37, 0xC2, 0x10, 0x00].pack("C*")
  127.   #--------------------------------------------------------------------------
  128.   # ● PNG 常量
  129.   #--------------------------------------------------------------------------  
  130.   PNG_HEADER = "\211\120\116\107\015\012\032\012\000\000\000\015"
  131.   PNG_IHDR_HEAD = "\111\110\104\122"
  132.   PNG_IHDR_TAIL = "\010\006\000\000\000"
  133.   PNG_IEND_CHUNK = "\000\000\000\000\111\105\116\104\256\102\140\202"
  134.   #--------------------------------------------------------------------------
  135.   # ● 生成 PNG 文件情报头数据块(IHDR)
  136.   #--------------------------------------------------------------------------  
  137.   def make_png_ihdr
  138.     data = PNG_IHDR_HEAD + [width, height].pack("N*") + PNG_IHDR_TAIL
  139.     return data + [Zlib.crc32(data)].pack("N")
  140.   end
  141.   #--------------------------------------------------------------------------
  142.   # ● 保存 PNG 文件
  143.   #     filename: 保存的文件名
  144.   #--------------------------------------------------------------------------  
  145.   def output_png(filename)
  146.     # 检查并建立路径
  147.     dir = filename.split("/")
  148.     for i in 0...dir.size - 1
  149.       unless dir == "."
  150.         add_dir = dir[0..i].join("/")
  151.         Dir.mkdir(add_dir) rescue nil
  152.       end
  153.     end
  154.     # 依次写入 PNG 文件内容
  155.     file = File.open(filename,"wb")
  156.     file.write(PNG_HEADER)
  157.     file.write(make_png_ihdr)
  158.     file.write(make_png_idat)
  159.     file.write(PNG_IEND_CHUNK)
  160.     file.close
  161.     Scene_Reporter.update("", "完成")
  162.   end
  163.   #--------------------------------------------------------------------------
  164.   # ● 使用临时文件生成 PNG 图像数据(IDAT)
  165.   #--------------------------------------------------------------------------  
  166.   def make_png_idat
  167.     # 数据头
  168.     header = "IDAT"
  169.     # 输出图像信息为临时文件并获得校验码
  170.     adler = [make_data_file].pack("N")
  171.     # 读取临时文件
  172.     data = "\170\332"
  173.     fsize = File.size("temp.gz")
  174.     File.open("temp.gz", "rb") do |f|
  175.       f.pos = 10
  176.       data.concat(f.read(fsize - 18))
  177.       data.concat(adler)
  178.     end
  179.     # 删除临时文件
  180.     File.delete("temp.gz")
  181.     # 附加校验码
  182.     crc = [Zlib.crc32(header + data)].pack("N")
  183.     size = [data.length].pack("N")
  184.     # 返回数据
  185.     return size + header + data + crc
  186.   end
  187.   #--------------------------------------------------------------------------
  188.   # ● 将内存数据转储为文件,并返回adler32校验值
  189.   #--------------------------------------------------------------------------  
  190.   def make_data_file
  191.     # 建立缓存
  192.     cache = "\000" * 1
  193.     # 保存缓存原有结构信息
  194.     cache_info = [cache.size].pack("L")+ [cache].pack("p")
  195.     cache_len = cache.id * 2 + 8; cache_addr = cache_len + 4
  196.     # 将缓存的指针覆盖使之指向 Bitmap 的图像数据
  197.     byte_size = 4 * width * height
  198.     bitmap_info = [byte_size].pack("L") + [address].pack("L")
  199.     CopyMemory_ip.call(cache_len, bitmap_info, 8)
  200.     # 进度汇报线程
  201.     t = Thread.new do
  202.       loop do; sleep(0.5); Scene_Reporter.update("", "转换格式..."); end
  203.     end
  204.     # BGRA 转 RGBA
  205.     CallWindowProc_p.call(RGBAConvert, cache, width * height, 0, 0)
  206.     # 结束进度汇报
  207.     Thread.kill(t)
  208.     # 准备写入文件
  209.     x = 4 * width; y = [address + byte_size]; null = "\000"
  210.     # adler32 校验码初期化
  211.     adler = Zlib.adler32
  212.     # 将缓存的大小改为单行位图
  213.     CopyMemory_ip.call(cache_len, [x].pack("L"), 4)
  214.     # 打开临时文件
  215.     Zlib::GzipWriter.open("temp.gz", 8) do |gz|
  216.       # 进度汇报线程
  217.       t = Thread.new do
  218.         loop do
  219.           sleep(0.5)
  220.           Scene_Reporter.update("", sprintf("压缩图片...%d%%",
  221.             (byte_size - y[0] + @address) * 100 / byte_size))
  222.         end
  223.       end
  224.       # 逐行上移
  225.       while y[0] > @address
  226.         y[0] -= x
  227.         # 更改缓存指针
  228.         CopyMemory_ip.call(cache_addr, y.pack("L"), 4)
  229.         # 写入文件数据
  230.         gz.write(null)
  231.         adler = Zlib.adler32(null, adler)
  232.         gz.write(cache)
  233.         adler = Zlib.adler32(cache, adler)
  234.       end
  235.       # 结束进度汇报
  236.       Thread.kill(t)
  237.       # 关闭临时文件
  238.       gz.close
  239.     end
  240.     # 恢复指针原始结构信息以便释放
  241.     CopyMemory_ip.call(cache_len, cache_info, 8)
  242.     # 返回校验码
  243.     return adler
  244.   end
  245.   #--------------------------------------------------------------------------
  246.   # ● 位图的数据内存地址
  247.   #--------------------------------------------------------------------------  
  248.   def address
  249.     if @address == nil
  250.       buffer = "xxxx"
  251.       CallWindowProc_i.call(GetAddress, buffer, object_id * 2 + 16, 0, 0)
  252.       @address = buffer.unpack("L")[0]
  253.     end
  254.     return @address
  255.   end
  256. end
  257. #==============================================================================
  258. # ■ Bitmap
  259. #------------------------------------------------------------------------------
  260. #  内部位图类
  261. #==============================================================================
  262. class Bitmap
  263.   #--------------------------------------------------------------------------
  264.   # ● 导入 PNG 模块
  265.   #--------------------------------------------------------------------------  
  266.   include PNG
  267.   #--------------------------------------------------------------------------
  268.   # ● 合成
  269.   #     rect: 合成的矩形
  270.   #     src_bitmap: 合成的位图
  271.   #     src_rect: 合成位图的传送元矩形
  272.   #     opacity: 不透明度
  273.   #     blend_type: 合成方式(1: 加法、2: 减法)
  274.   #--------------------------------------------------------------------------  
  275.   def blend_blt(rect, src_bitmap, src_rect, opacity, blend_type)
  276.     # 纯透明的情况下 不合成
  277.     return if opacity == 0
  278.     # 建立临时位图
  279.     temp_bitmap = Bitmap.new(rect.width, rect.height)
  280.     # 将源位图合成到临时位图,考虑缩放
  281.     if rect.width != src_rect.width or rect.height != src_rect.height
  282.       temp_bitmap.stretch_blt(temp_bitmap.rect, src_bitmap, src_rect, opacity)
  283.     else
  284.       temp_bitmap.blt(0, 0, src_bitmap, src_rect, opacity)
  285.     end
  286.     # 逐点合成
  287.     for i in 0...rect.width
  288.       for j in 0...rect.height
  289.         # 取色
  290.         c1 = self.get_pixel(rect.x + i, rect.y + j)
  291.         c2 = temp_bitmap.get_pixel(i, j)
  292.         # 源位图此点无色的情况下,继续
  293.         next if c2.alpha == 0
  294.         # 加法
  295.         if blend_type == 1
  296.           c1.red += c2.red * c2.alpha / 255 * opacity / 255
  297.           c1.green += c2.green * c2.alpha / 255 * opacity / 255
  298.           c1.blue += c2.blue * c2.alpha / 255 * opacity / 255
  299.         # 减法
  300.         else
  301.           c1.red = [0, c1.red - c2.red * c2.alpha / 255 * opacity / 255].max
  302.           c1.green = [0, c1.green - c2.green * c2.alpha / 255 * opacity / 255].max
  303.           c1.blue = [0, c1.blue - c2.blue * c2.alpha / 255 * opacity / 255].max
  304.         end
  305.         # 设色
  306.         self.set_pixel(rect.x + i, rect.y + j, c1)
  307.       end
  308.     end
  309.     # 释放临时位图
  310.     temp_bitmap.dispose
  311.   end
  312. end
  313. #==============================================================================
  314. # ■ Map_Snapshot
  315. #------------------------------------------------------------------------------
  316. #    地图截图渲染引擎
  317. #==============================================================================
  318. class Map_Snapshot
  319.   #--------------------------------------------------------------------------
  320.   # ● 常量
  321.   #--------------------------------------------------------------------------  
  322.   AUTOTILE_EXPAND = [
  323.     555752218, 555752196, 555746586, 555746564, 186653466, 186653444,
  324.     186647834, 186647812, 554310426, 554310404, 554304794, 554304772,
  325.     185211674, 185211652, 185206042, 185206020, 522066200, 522061080,
  326.     186521880, 186516760, 353636110, 185863950, 352980750, 185208590,
  327.     589438236, 587865372, 589438212, 587865348, 757868326, 757868292,
  328.     757859622, 757859588, 589176088, 757862158, 319950092, 185732364,
  329.     387322128, 386535696, 791554344, 791554308, 724182308, 724174116,
  330.     387059980, 724176140, 791292196, 791548176, 791286028, 117833984
  331.   ] # 自动元件的展开方式
  332.   #--------------------------------------------------------------------------
  333.   # ● 初期化
  334.   #--------------------------------------------------------------------------  
  335.   def initialize
  336.     @tilesets = load_data("Data/Tilesets.rxdata")
  337.     @mapnames = load_data("Data/MapInfos.rxdata")
  338.     @mapnames.each {|k, v| @mapnames[k] = v.name.gsub(/[\\\/:*?|<>]/,"_")}
  339.     @map_data = nil
  340.     @output_bitmap = nil
  341.     @tile_bitmap = nil
  342.     @autotiles = [nil, nil, nil, nil, nil, nil, nil]
  343.     @width = 0
  344.     @height = 0
  345.     @map_id = 0
  346.     @scale = 1
  347.     @mode = "M"
  348.   end
  349.   #--------------------------------------------------------------------------
  350.   # ● 设置缩放
  351.   #     scale: 缩放比(1/1、1/2、1/4或1/8)
  352.   #--------------------------------------------------------------------------  
  353.   def set_scale(scale)
  354.     @scale = scale if [1, 2, 4, 8].include?(scale)
  355.   end
  356.   #--------------------------------------------------------------------------
  357.   # ● 设置渲染模式
  358.   #     mode: 渲染模式(M、U、A、T)
  359.   #--------------------------------------------------------------------------  
  360.   def set_mode(mode)
  361.     @mode = mode if ["M", "U", "A", "T"].include?(mode)
  362.   end
  363.   #--------------------------------------------------------------------------
  364.   # ● 地图截图
  365.   #     map_id: 地图ID
  366.   #--------------------------------------------------------------------------  
  367.   def snap(map_id)
  368.     # 如果是ULDS模式,放大率强制为1
  369.     set_scale(1) if @mode == "U"
  370.     # 加载地图数据
  371.     map_name = sprintf("Data/Map%03d.rxdata", map_id)
  372.     if FileTest.exist?(map_name)
  373.       @map_id = map_id
  374.       @map_data = load_data(map_name)
  375.       @width = @map_data.width
  376.       @height = @map_data.height
  377.       @tileset = @tilesets[@map_data.tileset_id]
  378.       @tile_bitmap = RPG::Cache.tileset(@tileset.tileset_name)
  379.       @tile_rect = Rect.new(0, 0, 32, 32)
  380.       # 用于输出的位图
  381.       @output_bitmap = Bitmap.new(@width * 32 / @scale, @height * 32 / @scale)
  382.       for i in 0..6
  383.         @autotiles[i] = RPG::Cache.autotile(@tileset.autotile_names[i])
  384.       end
  385.       @auto_stretch = {}
  386.       # 渲染
  387.       snapshot
  388.       # 输出截图
  389.       filename = sprintf("MapSnapshots/%03d-%s.png", map_id, @mapnames[map_id])
  390.       @output_bitmap.output_png(filename)
  391.       # 如果是ULDS模式,渲染第二张图
  392.       if @mode == "U"
  393.         @output_bitmap.clear
  394.         snapshot(true)
  395.         f2 = sprintf("MapSnapshots/%03d-%s[U].png", map_id, @mapnames[map_id])
  396.         @output_bitmap.output_png(f2)
  397.       end
  398.       # 释放位图
  399.       @output_bitmap.dispose
  400.       @auto_stretch.each_value {|v| v.dispose}
  401.       for i in 0..6
  402.         @autotiles[i].dispose
  403.       end
  404.       @autotiles = [nil, nil, nil, nil, nil, nil, nil]
  405.       @tile_bitmap.dispose
  406.       @tile_bitmap = nil
  407.     end
  408.   end
  409.   #--------------------------------------------------------------------------
  410.   # ● 渲染地图
  411.   #     ulds: ULDS第二层渲染专用
  412.   #--------------------------------------------------------------------------  
  413.   def snapshot(ulds = false)
  414.     # 筛选需要渲染的事件集
  415.     @effect_events = @map_data.events.select do |k, v|
  416.       # 事件的地图元件有效的情况下,总是渲染
  417.       effective = (v.pages[0].graphic.tile_id > 0)
  418.       # 分层模式的情况下
  419.       if @mode == "U"
  420.         # 事件还需要满足优先级条件才渲染
  421.         effective &= (v.pages[0].always_on_top == ulds)
  422.       # 攻略模式的情况下
  423.       elsif @mode == "T"
  424.         # 事件不移动且朝向固定也需渲染
  425.         effective |= (v.pages[0].graphic.character_name != "" and
  426.           v.pages[0].move_type == 0)
  427.       # 航拍模式的情况下
  428.       elsif @mode == "A"
  429.         # 所有非空事件一律渲染
  430.         effective |= (v.pages[0].graphic.character_name != "")
  431.       end
  432.       # 筛选
  433.       effective
  434.     end
  435.     # 纯地图模式的情况下,直接按格快速渲染
  436.     return direct_paint if @mode == "M"
  437.     # 分层渲染底图
  438.     unless ulds
  439.       # 渲染没有优先级的图块
  440.       for k in 0...3
  441.         for j in 0...@height
  442.           for i in 0...@width
  443.             # 取得图块
  444.             tile_id = @map_data.data[i, j, k]
  445.             # 优先级为0的情况下
  446.             if tile_id > 0 and @tileset.priorities[tile_id] == 0
  447.               paint(i, j, tile_id)
  448.             end
  449.           end
  450.         end
  451.         # 每层汇报进度
  452.         Scene_Reporter.update(sprintf("正在生成截图Map%03d.png...", @map_id),
  453.           sprintf("渲染下层...%d%%", (k + 1) * 33))
  454.       end
  455.       # 渲染不在最前显示的图块事件
  456.       @effect_events.each do |f|
  457.         e = f[1]; g = e.pages[0].graphic
  458.         if g.tile_id > 0 and not e.pages[0].always_on_top
  459.           paint(e.x, e.y, g.tile_id, g.character_hue, g.opacity, g.blend_type)
  460.         end
  461.       end
  462.       # 分层模式的情况下,渲染结束
  463.       return if @mode == "U"
  464.     end
  465.     # 分层渲染顶图
  466.     # 计算剩余所有图块的 Z 坐标
  467.     z_indexes = {}
  468.     for k in 0...3
  469.       for j in 0...@height
  470.         for i in 0...@width
  471.           # 取得图块
  472.           tile_id = @map_data.data[i, j, k]
  473.           # 优先级不为0的情况下
  474.           if tile_id > 0 and @tileset.priorities[tile_id] > 0
  475.             z = (@tileset.priorities[tile_id] + j) * 32 + 32
  476.             z_indexes[z] = (z_indexes[z] || []).push([i, j, tile_id])
  477.           end
  478.         end
  479.       end
  480.     end
  481.     # 计算所有需要渲染的事件的 Z 坐标
  482.     @effect_events.each do |f|
  483.       e = f[1]; g = e.pages[0].graphic
  484.       # 事件的 Z 坐标
  485.       z = e.pages[0].always_on_top ? 32768 : e.y * 32 + 32
  486.       # 是普通事件的情况下
  487.       if g.tile_id == 0 or e.pages[0].always_on_top
  488.         z_indexes[z] = (z_indexes[z] || []).push([e.x, e.y, g])
  489.       end
  490.     end
  491.     # 按 Z 坐标开始绘制
  492.     l = n = z_indexes.keys.size
  493.     z_indexes.keys.sort.each do |k|
  494.       v = z_indexes[k]
  495.       while not v.empty?
  496.         tile = v.shift
  497.         x = tile[0]; y = tile[1]; g = tile[2]
  498.         # 如果此处是图块
  499.         if g.is_a?(Numeric)
  500.           paint(x, y, g)
  501.         # 如果此处是事件
  502.         elsif g.tile_id > 0
  503.           paint(x, y, g.tile_id, g.character_hue, g.opacity, g.blend_type)
  504.         else
  505.           paint_character(x, y, g)
  506.         end
  507.       end
  508.       n -= 1
  509.       # 已绘制20层的情况下,汇报进度
  510.       if n % 20 == 0
  511.         Scene_Reporter.update("", sprintf("渲染上层...%d%%",(l - n) * 100 / l))
  512.       end
  513.     end
  514.   end
  515.   #--------------------------------------------------------------------------
  516.   # ● 快速逐格渲染(M模式用)
  517.   #--------------------------------------------------------------------------  
  518.   def direct_paint
  519.     # 变换有效事件列表
  520.     events = @effect_events.inject({}) do |s, f|
  521.       e = f[1]
  522.       s[[e.x, e.y]] = [e.pages[0].graphic, e.pages[0].always_on_top]
  523.       s
  524.     end
  525.     # 逐格绘制
  526.     for j in 0...@height
  527.       for i in 0...@width
  528.         # 获得此处的三层图块
  529.         tiles = [@map_data.data[i, j, 0],
  530.           @map_data.data[i, j, 1], @map_data.data[i, j, 2]]
  531.         # 获得此处三层图块的 Z 坐标
  532.         z_indexes = [@tileset.priorities[tiles[0]] * 32,
  533.           @tileset.priorities[tiles[1]] * 32 + 1,
  534.           @tileset.priorities[tiles[2]] * 32 + 2]
  535.         # 如果此处有事件,压入图块数据
  536.         if events.has_key?([i, j])
  537.           tiles.push(events[[i, j]][0].tile_id)
  538.           z_indexes.push(events[[i, j]][1] ? 999 : 3)
  539.         end
  540.         # 从最小值 Z 坐标开始绘制
  541.         while tiles.size > 0
  542.           z_index = z_indexes.min
  543.           index = z_indexes.index(z_index)
  544.           tile_id = tiles[index]
  545.           # 图块是事件的情况下,按事件图块的合成方式绘成
  546.           if z_index == 3 or z_index == 999
  547.             opacity = events[[i, j]][0].opacity
  548.             hue = events[[i, j]][0].character_hue
  549.             blend_type = events[[i, j]][0].blend_type
  550.           else
  551.             # 基本绘成
  552.             hue = blend_type = 0
  553.             opacity = 255
  554.           end
  555.           paint(i, j, tile_id, hue, opacity, blend_type) if tile_id > 0
  556.           tiles.delete_at(index)
  557.           z_indexes.delete_at(index)
  558.         end
  559.       end
  560.       # 每绘制 20 行汇报一次进度
  561.       if j % 20 == 0 or j == @height - 1
  562.         Scene_Reporter.update(sprintf("正在生成截图Map%03d.png...", @map_id),
  563.         sprintf("渲染地图...%d%%", (j + 1) * 100 / @height))
  564.       end
  565.     end
  566.   end
  567.   #--------------------------------------------------------------------------
  568.   # ● 绘制格子
  569.   #     x, y: 地图的坐标
  570.   #     tile_id: 元件 ID
  571.   #     hue_change: 色相
  572.   #     opacity: 不透明度
  573.   #     blend_type: 合成方式 (0: 普通、1: 加法、2: 减法)
  574.   #--------------------------------------------------------------------------  
  575.   def paint(x, y, tile_id, hue_change = 0, opacity = 255, blend_type = 0)
  576.     # 透明的图块不需要绘制
  577.     return if opacity == 0
  578.     # 绘制的原点
  579.     ox = x * 32 / @scale
  580.     oy = y * 32 / @scale
  581.     # 是普通元件的情况下
  582.     if tile_id >= 384
  583.       # 取得源位图
  584.       if hue_change != 0
  585.         (src = @tile_bitmap.clone).hue_change(hue)
  586.       else
  587.         src = @tile_bitmap
  588.       end
  589.       # 传送矩形
  590.       tile_id -= 384
  591.       src_rect = Rect.new((tile_id % 8) * 32, (tile_id / 8) * 32, 32, 32)
  592.       # 绘制合成
  593.       if blend_type == 0
  594.         if @scale == 1
  595.           @output_bitmap.blt(ox, oy, src, src_rect, opacity)
  596.         else
  597.           @output_bitmap.stretch_blt(Rect.new(ox, oy,
  598.             32 / @scale, 32 / @scale), src, rect, opacity)
  599.         end
  600.       else
  601.         @output_bitmap.blend_blt(Rect.new(ox, oy,
  602.           32 / @scale, 32 / @scale), src, rect, opacity, blend_type)
  603.       end
  604.       # 色相有差异的情况下要释放
  605.       src.dispose if hue_change != 0
  606.     # 不是普通元件的情况下(即自动元件)
  607.     else
  608.       # 获得自动元件的展开样式
  609.       autotile = @autotiles[tile_id / 48 - 1]
  610.       tile_form = AUTOTILE_EXPAND[tile_id % 48]
  611.       tile_lu = tile_form & 0xff
  612.       tile_ru = (tile_form & 0xff00) >> 8
  613.       tile_ld = (tile_form & 0xff0000) >> 16
  614.       tile_rd = (tile_form & 0xff000000) >> 24
  615.       # 缩放比为1的情况下,直接拷贝原件
  616.       if @scale == 1
  617.         @output_bitmap.blt(ox, oy, autotile, auto_rect(tile_lu), 255)
  618.         @output_bitmap.blt(ox + 16, oy, autotile, auto_rect(tile_ru), 255)
  619.         @output_bitmap.blt(ox, oy + 16, autotile, auto_rect(tile_ld), 255)
  620.         @output_bitmap.blt(ox + 16, oy + 16, autotile, auto_rect(tile_rd), 255)
  621.       # 缩放比不为1的情况下
  622.       else
  623.         s = 32 / @scale
  624.         temp_bitmap = Bitmap.new(32, 32)
  625.         temp_bitmap.blt(0, 0, autotile, auto_rect(tile_lu), 255)
  626.         temp_bitmap.blt(16, 0, autotile, auto_rect(tile_ru), 255)
  627.         temp_bitmap.blt(0, 16, autotile, auto_rect(tile_ld), 255)
  628.         temp_bitmap.blt(16, 16, autotile, auto_rect(tile_rd), 255)
  629.         @output_bitmap.stretch_blt(Rect.new(ox, oy, s, s),
  630.           temp_bitmap, @tile_rect, 255)
  631.         temp_bitmap.dispose
  632.       end
  633.     end
  634.   end
  635.   #--------------------------------------------------------------------------
  636.   # ● 绘制事件
  637.   #     x, y: 地图的坐标
  638.   #     g: 事件的图像(RPG::Event::Graphic)
  639.   #--------------------------------------------------------------------------  
  640.   def paint_character(x, y, g)
  641.     # 透明的角色不需要绘制
  642.     return if g.opacity == 0
  643.     # 绘制的原点
  644.     bitmap = RPG::Cache.character(g.character_name, g.character_hue)
  645.     rect = char_rect(bitmap.width, bitmap.height, g.direction, g.pattern)
  646.     ox = (x * 32 - rect.width / 2 + 16) / @scale
  647.     oy = (y * 32 - rect.height + 32) / @scale
  648.     w = rect.width / @scale
  649.     h = rect.height / @scale
  650.     # 普通叠加的情况下,传送位图
  651.     if g.blend_type == 0
  652.       if @scale == 1
  653.         @output_bitmap.blt(ox, oy, bitmap, rect, g.opacity)
  654.       else
  655.         @output_bitmap.stretch_blt(Rect.new(ox, oy, w, h), bitmap, rect,
  656.           g.opacity)
  657.       end
  658.     else
  659.       @output_bitmap.blend_blt(Rect.new(ox, oy, w, h), bitmap, rect,
  660.         g.opacity, g.blend_type)
  661.     end
  662.   end
  663.   #--------------------------------------------------------------------------
  664.   # ● 事件的传送矩形
  665.   #     width, height: 事件的位图宽高
  666.   #     direction: 事件的朝向
  667.   #     pattern: 事件的模式
  668.   #--------------------------------------------------------------------------  
  669.   def char_rect(width, height, direction, pattern)
  670.     Rect.new(pattern * width / 4, (direction - 2) * height / 8,
  671.       width / 4, height / 4)
  672.   end
  673.   #--------------------------------------------------------------------------
  674.   # ● 自动元件的传送矩形
  675.   #     region: 自动元件的展开方式(0-47)
  676.   #--------------------------------------------------------------------------  
  677.   def auto_rect(region)
  678.     Rect.new((region % 6) * 16, (region / 6) * 16, 16, 16)
  679.   end
  680.   #--------------------------------------------------------------------------
  681.   # ● 连续截图
  682.   #     range: 截图的地图ID列表
  683.   #--------------------------------------------------------------------------  
  684.   def snaps(range)
  685.     Scene_Reporter.update("准备就绪")
  686.     if range.is_a?(Range) or range.is_a?(Array)
  687.       range.each do |i|
  688.         snap(i)
  689.       end
  690.     end
  691.     Scene_Reporter.close("完成,按B或C键继续")
  692.   end
  693.   #--------------------------------------------------------------------------
  694.   # ● 全部截图
  695.   #--------------------------------------------------------------------------  
  696.   def snap_all
  697.     snaps(1..999)
  698.   end
  699. end
  700. ms = Map_Snapshot.new
  701. ms.set_scale(1)
  702. ms.set_mode("T")
  703. ms.snap_all
  704. exit


运行时截图:
p1.png

截图样例:
019-Serpent Shrine - Floor 2_海蛇祠 2层.png
152-Camien Wasteland Fortress_卡米安荒原要塞.png
099-Mt. Leest - Hidden Cave B2F_利斯特山 秘密洞穴地下2层.png
128-Locrata Castle_洛克塔王城.png

评分

参与人数 4星屑 +200 +4 收起 理由
KFLYlzzq + 1 精品文章
RyanBern + 100 + 1 精品文章pplus
hys111111 + 100 + 1 精品文章
89444640 + 1 我很赞同

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。

Lv5.捕梦者

梦石
0
星屑
34825
在线时间
4145 小时
注册时间
2007-12-15
帖子
9970
发表于 2017-10-8 06:24:20 | 显示全部楼层
本帖最后由 89444640 于 2017-10-8 06:27 编辑

非常感谢,测试了一下,用目前做过的最大的地图,250*256。输出正常,不过这个大小- -b所有地图都这么渲染……累死人。
01.jpg

一些需要氛围的小型图可能会以弄一下。这种大的就拉倒吧

点评

其实输出的 PNG 可以用外部程序再压缩一下……  发表于 2017-10-8 13:35
另外,大地图你可以调整那个set_scale,比如设定为4甚至8,如果是为了做攻略什么的话。感谢测试。  发表于 2017-10-8 10:04
渲染bitmap用不了多少时间,主要是在压缩PNG上面。我太久不来6R,回头找找有没有更高效的PNG算法(现在用的还是07年轮回者的)  发表于 2017-10-8 10:02
回复 支持 反对

使用道具 举报

Lv4.逐梦者 (版主)

梦石
0
星屑
9467
在线时间
5073 小时
注册时间
2013-6-21
帖子
3580

开拓者贵宾剧作品鉴家

发表于 2017-10-8 10:45:05 | 显示全部楼层
记得当年我写过的一个,怕是猫大说的“有人专门写了 Tilemap”应该不是我。因为我的脑回路应该也是根据数据绘制 bitmap 这种的,同样担心地图大了会不会爆炸。
具体的难点应该有两个:
1. 自动元件。因为当时不清楚自动元件的排号规律,所以一个个试导致搞了好久才知道是怎么回事(虽然现在已经100%忘记了)。当时找这个规律非常痛苦。
2. 输出 PNG。这个当然是仁者见仁智者见智了。当时我受到路路大大的提示,使用了外接 DLL。感觉上能好一些吧。

帖子在这,似乎可以参考一下:https://rpg.blue/forum.php?mod=viewthread&tid=374269

评分

参与人数 1+1 收起 理由
89444640 + 1 这个我也存下~谢谢

查看全部评分

回复 支持 反对

使用道具 举报

Lv5.捕梦者

梦石
0
星屑
34825
在线时间
4145 小时
注册时间
2007-12-15
帖子
9970
发表于 2017-10-8 14:51:18 | 显示全部楼层
本帖最后由 89444640 于 2017-10-8 15:04 编辑

请教几个与这个脚本密切相关问题,有时雾图形会在读存档时候消失,如何确保雾图形不消失?并行处理不可取过分消耗系统。
输出时候无法输出事件位置,个别事件为宝箱,其实加个高光提示一下更好,能否继续改进可以输出事件位置,便于在事件位置加高光提示。


做了一下实验,以前没画过这种效果,只用更改不透明度+叠加方式,用橡皮擦出来的。 43454.png
图像中的人物位置是固定的,如果无法输出,只能凭印象来处理,小型图还好,大型地图较为困难。
可否考虑事件位置描绘处一个高饱和度的方块?





点评

辛苦辛苦~静候佳音  发表于 2017-10-8 15:11
事件和雾已经在更新中了,主要是叠加原则(因为我不手写Tilemap),不过也就是体力活……  发表于 2017-10-8 15:09
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
22385
在线时间
2335 小时
注册时间
2005-10-15
帖子
1160

开拓者

 楼主| 发表于 2017-10-8 15:11:07 | 显示全部楼层
RyanBern 发表于 2017-10-8 10:45
记得当年我写过的一个,怕是猫大说的“有人专门写了 Tilemap”应该不是我。因为我的脑回路应该也是根据数据 ...

自动元件早就被我拆透了…… 000-Test.jpg

把上述图片灌入tileset,然后双击自动元件展开小窗口,你会发现自动元件就没有一点秘密了……

然后顺便发现了xp的自动元件从原理上优于va系……

点评

这么一说我想起来了,XP自动原件确实7个,造成只能用切换场景换不同自动原件。好多都只好放到地图中了,绘制起来特别麻烦--b  发表于 2017-10-9 08:31
@89444640 不是太小了是太少了,每地图限定7个,VA的自动元件还没XP大,架不住人家数量多……  发表于 2017-10-8 17:43
但是XP的自动原件太小了,本质上每个单元才16*16,很多草皮、转角之类的都得画到地图上,只能静态,无法做成有4帧循环播放的动态。好歹32才好画  发表于 2017-10-8 17:13
学到了,自动元件展开。之前一直没注意过这个功能  发表于 2017-10-8 16:28

评分

参与人数 1+1 收起 理由
RyanBern + 1 精品文章

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
22385
在线时间
2335 小时
注册时间
2005-10-15
帖子
1160

开拓者

 楼主| 发表于 2017-10-9 13:12:45 | 显示全部楼层
更新顶贴
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
0
星屑
13559
在线时间
2753 小时
注册时间
2014-10-4
帖子
756

R考场第七期纪念奖

发表于 2017-10-9 21:12:43 | 显示全部楼层
bitmap=>png 简版
从来没有最啊...

点评

实验失败 确实32位只有4G寻址空间 能申请出来的更是少的可怜 一次性取确实行不通 暂无想法  发表于 2017-10-10 01:24
bitmap转png最花时间的部分在于BGRA->RGBA,其他拷贝内存都快的很。  发表于 2017-10-9 21:26
你确定这个能做16000X16000的bitmap?500X500的地图其截图就有这么大 我和Zip::Deflate.deflate折腾了半天结论是out of memory,只能用临时文件+自算alerc32才行  发表于 2017-10-9 21:24
回复 支持 反对

使用道具 举报

Lv4.逐梦者

梦石
0
星屑
13559
在线时间
2753 小时
注册时间
2014-10-4
帖子
756

R考场第七期纪念奖

发表于 2017-10-10 05:57:52 | 显示全部楼层
本帖最后由 SixRC 于 2017-10-16 20:20 编辑

关于内存的问题,想到解决方案了,比较满意
本地测试15000*15000只需10秒 笔记本 你可以测试
理论上只要bitmap能生成 就能生成png
似乎超过1G rm就会炸掉 16000*16000绝对是上限了
除非不生成一个大的bitmap
流程是开两次exe
第一次生成bitmap对象 并且保存程序句柄 位图数据起始段址 位图宽高 到文件
第二次是判断那个文件是否存在 存在则
读取第一次程序的内存 然后分次取出数据到 gz

下面是代码
code

随便找张图
第一次运行等txt出现
第二次运行就会保存png
txt请自己删除..测试原因没有自动删它
睡觉去了






点评

嗯 我修改好了 开始我直接打开看没问题 没有用rm读取 所以不知道有问题 是我的错 有其余问题请提  发表于 2017-10-11 12:54
我明天会更改测试的  发表于 2017-10-11 00:21
要adler32校验码的话 直接算一下就好 每个xy数组里的循环都会取出一段interval高度的数据并处理好放到temp2里  发表于 2017-10-11 00:14
不清楚gz结构 不过本地测试确实是成功的 关于cwp 你可以查阅用它调用汇编 以上  发表于 2017-10-10 23:48
CallWindowProc第一个参数是指向函数句柄,然后试你这生成的PNG挂掉了,因为你压进去的IDAT数据最后没有加adler32校验码……从gzip文件里取出来的值是裸的  发表于 2017-10-10 22:39

评分

参与人数 1星屑 +20 收起 理由
RyanBern + 20 赞认真的讨论交流

查看全部评分

回复 支持 反对

使用道具 举报

Lv5.捕梦者

梦石
0
星屑
34825
在线时间
4145 小时
注册时间
2007-12-15
帖子
9970
发表于 2017-10-30 09:12:03 | 显示全部楼层
楼主你好~请问存档后,地图上的雾图形可能消失的问题,有解决方法了吗?

点评

刚才试了一下,果然是更改地图设置闹的,听你提到刷新我才想起来有这回事,谢谢啦。  发表于 2017-10-30 11:16
那个,会不会是我动了地图设置造成的,在存档后发现地图某处需要修改,再读取时候雾图形就消失了。我先试一下吧。  发表于 2017-10-30 10:56
好的 我整出一个,你试一下。  发表于 2017-10-30 10:48
能给个存档和游戏的测试链接吗?查了下game_map的默认刷新机制,不太可能出现这种问题啊。  发表于 2017-10-30 09:59
回复 支持 反对

使用道具 举报

Lv5.捕梦者 (版主)

遠航の猫咪

梦石
3
星屑
22385
在线时间
2335 小时
注册时间
2005-10-15
帖子
1160

开拓者

 楼主| 发表于 2017-11-6 01:26:39 | 显示全部楼层
update 1.3,完全测试无误的事件遮挡规则,四个模式的导出。

评分

参与人数 1+1 收起 理由
89444640 + 1 塞糖

查看全部评分

SailCat (小猫子·要开心一点) 共上站 24 次,发表过 11 篇文章 上 次 在: [2006年01月28日11:41:18 星期六] 从 [162.105.120.91] 到本站一游。
回复 支持 反对

使用道具 举报

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

本版积分规则

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

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

GMT+8, 2024-3-29 00:39

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

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