使用Vim修复Sprite Sheet Editor 0.5.6版生成的错误XML文件

  • 本站文章除注明转载外,均为本站原创或者翻译。
  • 本站文章欢迎各种形式的转载,但请18岁以上的转载者注明文章出处,尊重我的劳动,也尊重你的智商;
  • 本站部分原创和翻译文章提供markdown格式源码,欢迎使用文章源码进行转载;
  • 本博客采用 WPCMD 维护;
  • 本文标题:使用Vim修复Sprite Sheet Editor 0.5.6版生成的错误XML文件
  • 本文链接:http://zengrong.net/post/1435.htm

Sprite Sheet Editor 0.5.6有一个很重大的Bug,在保存metadata的时候,我将frame的ow/oh属性保存成了与w/h属性相同的值。

根据Sprite Sheet Editor修剪每帧中的空白区域的原理说明可以知道,对于剪切过空白的帧来说,ow和oh是还原原始帧大小的关键。如果这两个值出错,会导致无法取得动画的正确尺寸。

好在这个是可以回溯的。可以通过比较某个Label包含的所有帧的尺寸,通过ox/oy和w/h计算出每帧的实际尺寸,最大的那一个,就是该Label中的所有帧的统一ow/oh属性值。

使用这个方法,得到的实际值,甚至比原来通过Sprite Sheet Editor进行手工设定的值更小,也就是更加节省内存。

选择什么工具处理?AS3有强悍的E4X,JAVA和BASH也不错。但我正好想学习下Vim脚本,就用它了!

处理思路:

  • metadata中的 <labels>标签中保存所有Label的帧索引;
  • 取得每个Label的帧索引,获取每帧的行号,保存;
  • 针对每个Label进行处理,通过Label中每帧的ox/oy/w/h计算出ow/oh;
  • 记录每个Label中最大的ow/oh;
  • 用最大的ow/oh替换该Label中所有帧的ow/oh;
  • 循环处理所有Label。

错误的XML文件

" 转换Sprite Sheet Editor 0.5.6版生成的错误XML文件
" zrong (zrongzrong@gmail.com) 2011-09-02

" 获取当前目录下的所有xml文件
let xmls = split(glob("*.xml"), "\")
" 执行批量修复工作
for b:xml in xmls
    echom 'progress '.b:xml
    call RepareWH(b:xml)
endfor

" 修复file参数提供的xml文件
function! RepareWH(file)
    exec 'sp '.a:file
    normal gg
    let labelStart = search('')+1
    " 找不到Label,处理所有帧
    if labelStart <= 1
        echom 'label not found'
        let framesLine = GetFrames()
        call WriteFrames(range(len(framesLine)), framesLine)
        update
        close
        return
    endif
    let labelEnd = search('<\/labels>')-1
    let labels = []
    " 将所有的label名称与对应的帧索引写入一个List
    " List中的每一项是一个字典,name是label名称,index帧索引的数组(字符串形式保存)
    for i in range(labelStart, labelEnd)
        let labelMatch = matchlist(getline(i), '<\(\w\+\)>\([0-9,]\+\)')
        call add(labels, {'name':labelMatch[1], 'index':split(labelMatch[2], ',')})
    endfor
    normal gg
    let framesLine = GetFrames()
    echom 'find '.len(framesLine).' frames'
    " 修复所有的label
    for label in labels
        echo 'progress label '.label.name
        call WriteFrames(label.index, framesLine)
    endfor
    update
    close
endfunction

" 将所有的frame块转换成字典,并存入list
function! GetFrames()
    let framesLine = []
    while search('','',line('$'))
        let frameStart = line(".")+1
        let frameEnd = search('<\/frame>')-1
        call add(framesLine, ParseFrame(frameStart, frameEnd))
    endwhile
    return framesLine
endfunction

" 将一帧的信息转换成字典,并返回
function! ParseFrame(start, end)
    let aFrame = {}
    let reg = '<\(\w\+\)>\([-0-9]\+\)'
    for lineIndex in range(a:start, a:end)
        let matchFrame = matchlist(getline(lineIndex), reg)
        let aFrame[matchFrame[1]] = str2nr(matchFrame[2])
        " 写入ow和oh的行号方便后面替换
        if matchFrame[1] =~ 'ow'
            let aFrame.owl = lineIndex
        endif
        if matchFrame[1] =~ 'oh'
            let aFrame.ohl = lineIndex
        endif
    endfor
    return aFrame
endfunction

" 计算真实尺寸的最大值,将最大值写入这一组帧
function! WriteFrames(frameIndexs, framesLine)
    let owMax = 0
    let ohMax = 0
    for index in a:frameIndexs
        "echo '== progress frame '.index
        let l:frame = a:framesLine[str2nr(index)]
        " 如果修剪尺寸小于原始尺寸,则说明该帧没有问题
        if l:frame.w < l:frame.ow && l:frame.h < l:frame.oh
            "echom 'this frame '.index.' is not problem'
            continue
        endif
        " 真实的帧大小,是用修剪大小减去偏移值
        let trueW = l:frame.w - l:frame.ox
        let trueH = l:frame.h - l:frame.oy
        " 更新真实帧大小的最大值
        if trueW > owMax
            let owMax = trueW
        endif
        if trueH > ohMax
            let ohMax = trueH
        endif
    endfor
    if owMax > 0 && ohMax > 0
        " 写入帧的真实大小
        for index in a:frameIndexs
            let l:frame = a:framesLine[str2nr(index)]
            let ows = substitute(getline(l:frame.owl), '\d\+', owMax, '')
            let ohs = substitute(getline(l:frame.ohl), '\d\+', ohMax, '')
            echo '== write frame '.index.' line:'.l:frame.owl.','.l:frame.ohl.' w:'.owMax.' h:'.ohMax
            call setline(l:frame.owl, ows)
            call setline(l:frame.ohl, ohs)
        endfor
    endif
endfunction