免费用TexturePack和UnPack的功能

本文介绍如何通过Python脚本免费使用TexturePacker的功能,包括从plist文件重新生成无水印图片,以及将大图和plist文件拆分为小图片。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

##正常免费的使用TexturePack和UnPack的功能
前言:去官网下载个TexturePack下来,可发现要使用需要付费才可,如果不想付费点击使用免费版本,但是呢免费版本打出来的图片带有水印,plist倒是正常的,所以我们要做的就是在plist的基础上使用Python脚本读取plist的信息重新生成图片,这样就可以免费使用TexturePack的功能了!
使用免费版就可以使用pack功能了
可惜免费版打出来的是有带水印的,左边免费版
当然有人可能使用的是破解版,可是破解版基本是拷贝一份TexturePackerGUI去覆盖原exe,涉及到用命令打包的你会发现网上大部分破解版并没有破解TexturePacker.exe也就不能使用了,所以这个时候最简单的方式就是去官网下载正版使用free模式,这样即便是终端命名TexturePacker.exe也能使用了,只是打出来的有红色的水印,不过plist信息确实和付费的一样,我们的操作就在这里

介绍

以下是 python的pyqt5的textureUnpack 平台说明
多年前我曾经用python2.7开源过一个图集解压和重新打包的方法 https://blog.youkuaiyun.com/qq_15682489/article/details/78937045

软件架构

考虑到很多用户 没有安装python 不用说2.7或者3.7 可能环境也会有所缺失
所以我就用python3.7.7的pyqt5打包一个版本 一个直接可以用的exe软件
不知道怎么管理软件 我就直接放在这个开源项目里了
放在 https://gitee.com/woaiyouru/pyqtTxUpk/blob/master/pack/txUpk.exe

###TextureRePk 即基于plist的RePack

# -*- coding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import os
from xml.etree import ElementTree
from PIL import Image

def tree_to_dict(tree):
    d = {}
    for index, item in enumerate(tree):
        if item.tag == 'key':
            if tree[index+1].tag == 'string':
                d[item.text] = tree[index + 1].text
            elif tree[index + 1].tag == 'true':
                d[item.text] = True
            elif tree[index + 1].tag == 'false':
                d[item.text] = False
            elif tree[index + 1].tag == 'dict':
                d[item.text] = tree_to_dict(tree[index+1]) 

    return d

def gen_png_from_plist(plist_filename, png_dir):
    # 第一步 读取plist的信息 解析
    to_list = lambda x: x.replace('{','').replace('}','').split(',')
    # 解析plist为字典
    root = ElementTree.fromstring(open(plist_filename, 'r').read())
    plist_dict = tree_to_dict(root[0])
    # 根据plist画一张大图作为底图
    get_size = plist_dict["metadata"]["size"]
    sizelist = [ int(x) for x in to_list(get_size) ]
    result_image = Image.new('RGBA', sizelist, (0,0,0,0))
    # 判断plist类型 有的plist的用 frame 有的用 aliases 关键有的frame还不一样  这里我先做我面对的几种不一样的情况
    # food14.png {'frame': '{{107,214},{28,28}}', 'rotated': False, 'sourceColorRect': '{{0,0},{28,28}}', 'sourceSize': '{28,28}', 'offset': '{0,0}'}
    # res/shuihuzhuan/loading/logo.png {'spriteSize': '{280,296}', 'textureRect': '{{2,346},{280,296}}', 'textureRotated': True, 'spriteOffset': '{0,0}', 'spriteSourceSize': '{280,296}'}
    for k, v in plist_dict['frames'].items():
        full_path = os.path.join(png_dir, k)
        if not os.path.exists(full_path):
            print u"图片不存在:" + full_path
            continue
        # 打开子图片
        part_png = Image.open(full_path) 
        
        spriteSourceSize = v["spriteSourceSize"] if v.has_key("spriteSourceSize") else v["sourceSize"]
        spriteSourceSize = [ int(x) for x in to_list(spriteSourceSize) ]
        # pack后剩下的有效区域
        textureRect = v["textureRect"] if v.has_key("textureRect") else v["frame"]
        textureRect = [ int(x) for x in to_list(textureRect) ]
        # 是否旋转
        isRotate = v["textureRotated"] if v.has_key("textureRotated") else v["rotated"]
        # 小图在大图上的区域
        spriteOffset = v["spriteOffset"] if v.has_key("spriteOffset") else v["offset"]
        spriteOffset = [ int(x) for x in to_list(spriteOffset) ]
        # 获得长宽
        width = int( textureRect[3] if isRotate else textureRect[2] )  
        height = int( textureRect[2] if isRotate else textureRect[3] ) 

        if (part_png.size[0] != spriteSourceSize[0]) or (part_png.size[1] != spriteSourceSize[1]):
            print "图片和所描述尺寸不一致:目标替换尺寸->" + str(spriteSourceSize) + " 图片尺寸->" + str(part_png.size)
            continue

        if isRotate:
            rect_box=(  
                ( spriteSourceSize[0] - height)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] - width)/2 - spriteOffset[1],  
                ( spriteSourceSize[0] + height)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] + width)/2 - spriteOffset[1]   
                )  
        else:
            rect_box=(  
                ( spriteSourceSize[0] - width)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] - height)/2 - spriteOffset[1],  
                ( spriteSourceSize[0] + width)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] + height)/2 - spriteOffset[1] 
                )  
        rect_png = part_png.crop(rect_box)
        # 这里有个 spriteOffset 是干嘛的?先不管了
        result_box = ( textureRect[0], textureRect[1], textureRect[0] + width, textureRect[1] + height )
        if isRotate:
            result_image.paste(part_png.rotate(-90), result_box, mask = 0)
        else:
            result_image.paste(rect_png, result_box, mask = 0)
        print "paste", k
    # 保存图片
    save_name = os.path.join( os.path.dirname(plist_filename), plist_dict["metadata"]["realTextureFileName"] )
    result_image.save(save_name)

def main():
    # plist 所在路径
    plist_filename = r'D:\xampp\htdocs\Aproject\mGit\mSnack\res\game\snakePack2.plist'
    # 图片所在文件夹
    png_dir = r'D:\xampp\htdocs\Aproject\mGit\mSnack\res\game\snakePack2'
    # 根据plist生成图片
    if (os.path.exists(plist_filename) and os.path.exists(png_dir)):
        gen_png_from_plist( plist_filename, png_dir )
    else:
        print "make sure you have both plist and pngDir in directory"


if __name__ == '__main__':
    main()

这个脚本目前是以Python2.7版本所写,只需要拖动到SublimeText,修改好plist所在位置和素材图片文件夹就可以了,按Ctrl+B键即可快速运行

###TextureUnPk即解压plist和大图为小图片
有时候我们有了大图和plist想要把它拆分成小图片,但是手上有没有趁手的工具,不妨用Python再写一个脚本用于功能TextureUnPacker

# -*- coding:utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

import os
from xml.etree import ElementTree
from PIL import Image

def tree_to_dict(tree):
    d = {}
    for index, item in enumerate(tree):
        if item.tag == 'key':
            if tree[index+1].tag == 'string':
                d[item.text] = tree[index + 1].text
            elif tree[index + 1].tag == 'true':
                d[item.text] = True
            elif tree[index + 1].tag == 'false':
                d[item.text] = False
            elif tree[index+1].tag == 'dict':
                d[item.text] = tree_to_dict(tree[index+1]) 

    return d

def gen_png_from_plist(filename):

    plist_filename = filename + '.plist'
    png_filename = filename + '.png'
    # 判断两个文件是否存在
    if (not os.path.exists(plist_filename)) or (not os.path.exists(png_filename)):
        print "make sure you have both plist and png files in the directory"
        return True
    to_list = lambda x: x.replace('{','').replace('}','').split(',')
    # 打开大图
    big_image = Image.open(png_filename)
    # 解析plist
    root = ElementTree.fromstring(open(plist_filename, 'r').read())
    plist_dict = tree_to_dict(root[0])
    # 通常遇到的两种plist类型 格式
    # food14.png {'frame': '{{107,214},{28,28}}', 'rotated': False, 'sourceColorRect': '{{0,0},{28,28}}', 'sourceSize': '{28,28}', 'offset': '{0,0}'}
    # res/shuihuzhuan/loading/logo.png {'spriteSize': '{280,296}', 'textureRect': '{{2,346},{280,296}}', 'textureRotated': True, 'spriteOffset': '{0,0}', 'spriteSourceSize': '{280,296}'}
    for k, v in plist_dict['frames'].items():
        spriteSourceSize = v["spriteSourceSize"] if v.has_key("spriteSourceSize") else v["sourceSize"]
        spriteSourceSize = [ int(x) for x in to_list(spriteSourceSize) ]
        # pack后剩下的有效区域
        textureRect = v["textureRect"] if v.has_key("textureRect") else v["frame"]
        textureRect = [ int(x) for x in to_list(textureRect) ]
        # 是否旋转
        isRotate = v["textureRotated"] if v.has_key("textureRotated") else v["rotated"]
        # 小图在大图上的区域
        spriteOffset = v["spriteOffset"] if v.has_key("spriteOffset") else v["offset"]
        spriteOffset = [ int(x) for x in to_list(spriteOffset) ]
        # 获得长宽
        width = int( textureRect[3] if isRotate else textureRect[2] )  
        height = int( textureRect[2] if isRotate else textureRect[3] ) 
        box = [ textureRect[0], textureRect[1], textureRect[0] + width, textureRect[1] + height ]

        rect_on_big = big_image.crop(box)
        if isRotate:
            rect_on_big = rect_on_big.rotate(90)

        result_image = Image.new('RGBA', spriteSourceSize, (0,0,0,0))
        if isRotate:
            result_box=(  
                ( spriteSourceSize[0] - height)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] - width)/2 - spriteOffset[1],  
                ( spriteSourceSize[0] + height)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] + width)/2 - spriteOffset[1]   
                )  
        else:
            result_box=(  
                ( spriteSourceSize[0] - width)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] - height)/2 - spriteOffset[1],  
                ( spriteSourceSize[0] + width)/2 + spriteOffset[0],  
                ( spriteSourceSize[1] + height)/2 - spriteOffset[1] 
                )  

        result_image.paste(rect_on_big, result_box, mask = 0)

        outfile = filename + '/' + k
        print outfile, "generated"
        if not os.path.exists(os.path.dirname(outfile)):
            os.makedirs(os.path.dirname(outfile))
            
        result_image.save(outfile)

        if isRotate:
            print("isRotate", isRotate, k)


def main():
    # 这里输入要解压的plist文件路径 不需要带.plist 同样的图片一样的名字
    # filename = r'C:\Users\Administrator\Desktop\mibao\xxx'
    filename = r"D:\xampp\htdocs\Aproject\mGit\mSnack\res\game\snakePack2"
    gen_png_from_plist(filename)

if __name__ == '__main__':
    main()

这里写图片描述
比如我要解压exit的图片,这里只需要填入filename = r’G:\quick\reName\exit’即可,前面带r的意思是正则表达式,也就是你输入什么字符串就是什么路径,不会像\n被解析为回车键。

好了到了这里我们就讲解完毕了

public initializer(sceneGl: WebGLRenderingContext, sceneProgram: WebGLProgram, sceneOption: FisheyeSceneOption) { this.applyOptions(sceneGl, sceneProgram, sceneOption); const gl = this.gl; gl.pixelStorei(gl.UNPACK_ALIGNMENT, 1); const shaderProgram = this.program; const vertexBuffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0]), gl.STATIC_DRAW); const xyLocation = gl.getAttribLocation(shaderProgram, 'xy'); gl.vertexAttribPointer(xyLocation, 2, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(xyLocation); const scaleLocation = gl.getUniformLocation(shaderProgram, 'scale'); const scaleFactor = [1, 1]; gl.uniform2fv(scaleLocation, scaleFactor); const translateLocation = gl.getUniformLocation(shaderProgram, 'translate'); const translateFactor = [0, 0]; gl.uniform2fv(translateLocation, translateFactor); const tempStrengthLocation = gl.getUniformLocation(shaderProgram, 'strength'); if (!tempStrengthLocation) return; this.strengthLocation = tempStrengthLocation; gl.uniform1f(this.strengthLocation, this.strengthValue); const texture = gl.createTexture(); const enableMipMap = this.options.isAntialias ? gl.LINEAR_MIPMAP_LINEAR : gl.LINEAR; gl.bindTexture(gl.TEXTURE_2D, texture); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, enableMipMap); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); }
06-26
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值