【游戏开发小技】TexturePacker生成的图集逆向切分成精灵小图(json | python | PIL | TextureUnPacker | 逆向 | 切图)

2023-05-16

文章目录

      • 一、前言
      • 二、TexturePacker
        • 1、特别说明
        • 2、TexturePacker下载
        • 3、准备精灵散图(小图)
        • 4、使用TexturePacker打图集
        • 5、json结构分析
      • 三、图集逆向切分精灵图
        • 1、环境准备
        • 2、python代码:TextureUnpacker.py
        • 3、代码讲解
          • 3.1、PIL.Image.open方法
          • 3.2、PIL.Image.new方法
          • 3.3、PIL.Image.crop方法
          • 3.4、PIL.Image.paste方法
          • 3.5、PIL.Image.save方法
        • 4、执行python脚本

一、前言

嗨,大家好,我是林新发。
之前有同事找我问我能不能通过json逆向切分图集,我之前写过一个python脚本,但是没有存起来,
在这里插入图片描述
直到最近又有同事找我,给我发了jsonpng图集,
在这里插入图片描述
我决定写篇文章讲一下,也方便下次又有同事问我的时候直接丢出这篇文章~

二、TexturePacker

1、特别说明

首先,本文的python代码是针对TexturePacker工具Phaser 3框架生成出来的图集进行逆向切分,不过只要你懂了原理,其他格式的配置都可以一通百通。
如何知道图集是使用TexturePacker工具制作的呢?我们可以打开图集同名的配置文件,如果能看到texturepacker的字样,就说明是使用TexturePacker工具生成的,例:
在这里插入图片描述

2、TexturePacker下载

TexturePacker官网地址:https://www.codeandweb.com/texturepacker
在这里插入图片描述
下载下来后,我们进入bin目录,可以看到TexturePackerGUI.exe,双击即可打开,
在这里插入图片描述
如下
在这里插入图片描述

为了演示,我先使用搞一些散图,然后使用TexturePacker打个图集出来。

3、准备精灵散图(小图)

以下是我随便搜的一些精灵小图,
在这里插入图片描述

4、使用TexturePacker打图集

首先点击界面右侧的 框架,根据需求选择一个框架,
在这里插入图片描述

它支持非常多的框架,如下
请添加图片描述
我选择的是通用的Phaser 3,如下,(因为我那两个同事发给我的图集格式都是用的这个-_-,那我就用这个来做演示吧)
在这里插入图片描述

接着,我们把小图的整个文件夹拖到界面左侧中,它自动帮我们执行了图集排版,如下,

请添加图片描述
接着,我们点击发布精灵表
在这里插入图片描述
填写保存路径,即可生成图集png和一个json配置文件,如下
在这里插入图片描述

5、json结构分析

其中test.atlas.json配置表内容如下:

{
	"textures": [
		{
			"image": "test_atlas.png",
			"format": "RGBA8888",
			"size": {
				"w": 309,
				"h": 118
			},
			"scale": 1,
			"frames": [
				{
					"filename": "car.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 128,
						"h": 128
					},
					"spriteSourceSize": {
						"x": 0,
						"y": 6,
						"w": 128,
						"h": 116
					},
					"frame": {
						"x": 1,
						"y": 1,
						"w": 128,
						"h": 116
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "bookcase.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 64,
						"h": 64
					},
					"spriteSourceSize": {
						"x": 8,
						"y": 0,
						"w": 55,
						"h": 64
					},
					"frame": {
						"x": 131,
						"y": 1,
						"w": 55,
						"h": 64
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "table_tennis.png",
					"rotated": false,
					"trimmed": false,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 0,
						"y": 0,
						"w": 48,
						"h": 48
					},
					"frame": {
						"x": 131,
						"y": 67,
						"w": 48,
						"h": 48
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "pencil.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 3,
						"y": 3,
						"w": 42,
						"h": 42
					},
					"frame": {
						"x": 188,
						"y": 1,
						"w": 42,
						"h": 42
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "cate.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 5,
						"y": 3,
						"w": 39,
						"h": 41
					},
					"frame": {
						"x": 232,
						"y": 1,
						"w": 39,
						"h": 41
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "stone.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 6,
						"y": 4,
						"w": 35,
						"h": 40
					},
					"frame": {
						"x": 273,
						"y": 1,
						"w": 35,
						"h": 40
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "brinjaul.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 2,
						"y": 6,
						"w": 42,
						"h": 36
					},
					"frame": {
						"x": 188,
						"y": 45,
						"w": 42,
						"h": 36
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "carota.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 5,
						"y": 6,
						"w": 38,
						"h": 36
					},
					"frame": {
						"x": 232,
						"y": 44,
						"w": 38,
						"h": 36
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "bulb.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 6,
						"y": 4,
						"w": 36,
						"h": 40
					},
					"frame": {
						"x": 272,
						"y": 44,
						"w": 36,
						"h": 40
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "pizza.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 4,
						"y": 6,
						"w": 41,
						"h": 34
					},
					"frame": {
						"x": 181,
						"y": 83,
						"w": 41,
						"h": 34
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				},
				{
					"filename": "coconut.png",
					"rotated": false,
					"trimmed": true,
					"sourceSize": {
						"w": 48,
						"h": 48
					},
					"spriteSourceSize": {
						"x": 3,
						"y": 10,
						"w": 42,
						"h": 27
					},
					"frame": {
						"x": 224,
						"y": 83,
						"w": 42,
						"h": 27
					},
					"anchor": {
						"x": 0.5,
						"y": 0.5
					}
				}
			]
		}
	],
	"meta": {
		"app": "https://www.codeandweb.com/texturepacker",
		"version": "3.0",
		"smartupdate": "$TexturePacker:SmartUpdate:79dd9a57d14f6938f1ea38d2225ac0ce:6ec25dea8a4c4491ba119c28bbdefaf3:2a084c904c80f72428569743fdf0f51d$"
	}
}

我们分析一下json配置的结构,画个图方便一目了然,
(注意:这个json结构仅针对Phaser 3这个框架,其他框架的格式需要自行分析)
在这里插入图片描述
知道了json的结构,以及每个字段的含义,我们就可以开始逆向了~

三、图集逆向切分精灵图

1、环境准备

人生苦短,我用python,不要跟我争辩~
如果你没有python环境,安装一下,我用的是python 3
另外因为要进行图形处理,需要安装Pillow库(PIL)。
通过pip命令进行安装

pip install Pillow

如果在pythonimport PIL没有报错,则说明安装PIL库成功。

在这里插入图片描述

2、python代码:TextureUnpacker.py

这里我先直接上代码,注释我写得比较详细,大家应该能看懂,下文我会讲解核心的步骤,

# TexturePacker图集逆向工具,本代码只针对Phaser 3框架的json配置
# 只要你知道原理,其他配置都可自行分析处理,希望你能有自行修改和优化的能力
# Author: 林新发 https://blog.csdn.net/linxinfa
# Create: 2022-03-24

import os
from PIL import Image
import json

# 封装一个TextureUnpacker类
class TextureUnpacker(object):
    @classmethod
    def split_with_json(cls, f_json, save_dir=None):
        f_json = os.path.abspath(f_json)
        if save_dir is None:
            save_dir = f_json + '_split'
        else:
            save_dir = os.path.abspath(save_dir)
        # 读取json配置表
        f = open(f_json, 'r')
        txt = f.read()
        dt = json.loads(txt)
        f.close()
        # 大图集文件名
        big_texture_file_name = dt['textures'][0]['image']
        # 小图序列
        frames =  dt['textures'][0]['frames']
        # 打开大图
        big_img = Image.open(big_texture_file_name)
        # 遍历生成小图
        for index in range(0, len(frames)):
            info = frames[index]
            # 解析配置
            info = cls.parse_as_json(info)
            print(info)
            # 小图的保存路径
            little_image_save_path = os.path.join(save_dir, info['filename'])
            # 生成小图
            cls.generate_little_image(big_img, info, little_image_save_path)

    @classmethod
    def generate_little_image(cls, big_img, info, path):
        # 创建小图
        little_img = Image.new('RGBA', info['sz'])
        # PIL.Image.crop()方法用于裁剪任何图像的矩形部分
        # box –定义左,上,右和下像素坐标的4元组
        region = big_img.crop(info['box'])
        if info['rotated']:
            region = region.transpose(Image.ROTATE_90)
        # 把裁剪出来的图片粘贴到小图上
        little_img.paste(region, info['xy'])
        save_dir = os.path.dirname(path)
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        # 保存
        little_img.save(path)



    @classmethod
    def parse_as_json(cls, info):
        """
        "filename": "player_1.png",
        "rotated": false,
        "trimmed": false,
        "sourceSize": { "w": 1, "h": 1 },
        "spriteSourceSize": { "x": 0, "y": 0, "w": 1, "h": 1 },
        "frame": { "x": 1, "y": 1, "w": 1, "h": 1 }
        """
        # 小图宽高
        width = info['sourceSize']['w']
        height = info['sourceSize']['h']
        # 小图矩形信息
        frame = info['frame'] 
        # 是否旋转 (顺时针方向90度)
        rotated = info['rotated']
        if rotated:
            # box 定义左、上、右和下像素坐标的4元组
            box = (frame['x'], frame['y'], 
                    frame['x'] + frame['h'],
                    frame['y'] + frame['w'])
        else:
            box = (frame['x'], frame['y'],
                    frame['x'] + frame['w'],
                    frame['y'] + frame['h'])
        # 图形在小图中的偏移
        x = int((width - frame['w']) / 2)
        y = int((height - frame['h']) / 2)

        return {
            'box': box,
            'rotated': rotated,
            'sz': [width, height],
            'xy': (x, y),
            'filename' : info['filename']
        }

if __name__ == '__main__':
    unpacker = TextureUnpacker()
    unpacker.split_with_json('test_atlas.json')
    print('done')

3、代码讲解

核心就是封装的TextureUnpacker类,它做了以下几件事情,其中用到的PIL库的API我标注了黄色出来,如下
在这里插入图片描述
其中json配置的小图解析,我封装在parse_as_json方法中,如下,需要注意rotated旋转,
在这里插入图片描述

生成小图的逻辑封装在generate_little_image方法中,这个方法中用到了PIL库的一些API
在这里插入图片描述

关于PILAPI可以参见官方文档:https://pillow.readthedocs.io/en/latest/

这里我帮大家贴以下我用到的API的文档吧。

3.1、PIL.Image.open方法

在这里插入图片描述
打开图像,返回图像对象,
示例:

from PIL import Image
big_img = Image.open('test_atlas.png')
3.2、PIL.Image.new方法

在这里插入图片描述
创建image对象,示例:

from PIL import Image
little_img = Image.new('RGBA', (48, 48))
3.3、PIL.Image.crop方法

在这里插入图片描述
裁剪图像的某个矩形区域,示例

from PIL import Image
with Image.open("test_atlas.png") as im:
    (left, upper, right, lower) = (20, 20, 100, 100)
    im_crop = im.crop((left, upper, right, lower))
3.4、PIL.Image.paste方法

在这里插入图片描述
粘贴另一个image到自身image上,示例:

this_img.paste(other_image)
3.5、PIL.Image.save方法

在这里插入图片描述
image保存为文件,示例:

img_obj.save('test.png')

4、执行python脚本

python代码保存为TextureUnpacker.py,放在图集文件同级目录中,
在这里插入图片描述
执行python脚本,它会解析test_atlas.json并生成小图精灵,

if __name__ == '__main__':
    unpacker = TextureUnpacker()
    unpacker.split_with_json('test_atlas.json')
    print('done')

执行输出log如下

{'box': (1, 1, 129, 117), 'rotated': False, 'sz': [128, 128], 'xy': (0, 6), 'filename': 'car.png'}
{'box': (131, 1, 186, 65), 'rotated': False, 'sz': [64, 64], 'xy': (4, 0), 'filename': 'bookcase.png'}      
{'box': (131, 67, 179, 115), 'rotated': False, 'sz': [48, 48], 'xy': (0, 0), 'filename': 'table_tennis.png'}
{'box': (188, 1, 230, 43), 'rotated': False, 'sz': [48, 48], 'xy': (3, 3), 'filename': 'pencil.png'}        
{'box': (232, 1, 271, 42), 'rotated': False, 'sz': [48, 48], 'xy': (4, 3), 'filename': 'cate.png'}
{'box': (273, 1, 308, 41), 'rotated': False, 'sz': [48, 48], 'xy': (6, 4), 'filename': 'stone.png'}
{'box': (188, 45, 230, 81), 'rotated': False, 'sz': [48, 48], 'xy': (3, 6), 'filename': 'brinjaul.png'}
{'box': (232, 44, 270, 80), 'rotated': False, 'sz': [48, 48], 'xy': (5, 6), 'filename': 'carota.png'}
{'box': (272, 44, 308, 84), 'rotated': False, 'sz': [48, 48], 'xy': (6, 4), 'filename': 'bulb.png'}
{'box': (181, 83, 222, 117), 'rotated': False, 'sz': [48, 48], 'xy': (3, 7), 'filename': 'pizza.png'}
{'box': (224, 83, 266, 110), 'rotated': False, 'sz': [48, 48], 'xy': (3, 10), 'filename': 'coconut.png'}
done

可以看到生成了一个文件夹,
在这里插入图片描述
文件夹里就可以看到生成出来的精灵小图啦
在这里插入图片描述
完美,收工~
我是林新发,https://blog.csdn.net/linxinfa
一个在小公司默默奋斗的Unity开发者,希望可以帮助更多想学Unity的人,共勉~

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【游戏开发小技】TexturePacker生成的图集逆向切分成精灵小图(json | python | PIL | TextureUnPacker | 逆向 | 切图) 的相关文章

  • 深度学习蒟蒻入门——从0安装pytorch(CPU版)

    从0安装pytorch 1 检查自己的电脑有没有GPU2 安装CPU版的pytorch3 测试pytorch 1 检查自己的电脑有没有GPU 首先打开任务管理器 xff0c 选择性能栏 然后滑到最下 xff0c 看是否有GPU一项 xff0
  • 系统学习iOS动画 —— Stroke和路径动画

    这是要完成的动画 xff1a 先添加需要的代码 xff0c 这里需要将storyboard的ViewController换成TableViewController xff0c 将Under Top Bars 和 Under Bottom B
  • 不知道这些网站还做什么程序员呀!

    今天我就来总结一些程序员必备的网站 xff0c 囊括开源项目 解决bug 技术分享 一线资源和自我提升的网站 xff0c 希望能对广大程序猿有所帮助 xff0c 赶紧给我收藏起来 xff0c 下次刷不到了可别说我没提醒你 我们首先来看一下国
  • (音视频开发)WebRTC进阶流媒体服务器开发-多人互动架构

    一 xff1a 多人互动架构方案 xff08 一 xff09 WebRTC回顾 xff0c 两层含义 xff1a 1 WebRTC是google开源的流媒体客户端 xff0c 可以进行实时通讯 xff0c 主要应用于浏览器之间进行实时通讯
  • 10种linux下磁盘快照方式恢复系统

    导读大家都知道windows系统有一个磁盘快照的功能 xff0c 在windows2003中系统恢复开始依赖于一个叫做硬盘快照服务 Volume Snapshot Service 的服务 xff0c 他能够自动创建系统快照 包括正在使用的文
  • ubuntu安装go开发环境

    一 为ubuntu20 04更新源 给root用户设置密码 xff1a 命令 xff1a sudo passwd root 备份原来的源 xff0c 命令 xff1a sudo cp etc apt sources list etc apt
  • 如何修复Ubuntu中包缓存文件被毁问题

    导读今天 xff0c 我尝试更新我的 Ubuntu 18 04 LTS 的仓库列表 xff0c 但收到了一条错误消息 xff1a E The package cache file is corrupted it has the wrong
  • 1002 A+B for Polynomials (25分) 详解+易错点

    注意点 xff1a 系数为0 xff0c 则不输出 xff0c 例 xff1a 其中 1和1相加为0 xff0c 则在输出时避免这一项 xff0c 而且要注意结果的K值 xff0c 不要包括这一项 xff0c 思路 xff0c 利用结构体存
  • Linux远程桌面的选择

    Linux的远程桌面主要分两个部分 xff1a Linux客户机连Linux服务器和Windows客户机连Linux服务器 xff0c 还有现在用平板电脑连远程桌面 Linux客户机连Windows服务器比较简单没啥可说的 xff0c rd
  • Kali Linux mdk3WiFi洪水攻击 攻击路由器 生成虚假WiFi WiFi身份验证攻击可使连接WiFi的手机掉线重连抓包

    将无线网卡转换为监听模式 airmon ng start wlan0 查找附近无线网络 airodump ng wlan0mon Authentication DoS xff1a xff08 洪水攻击 xff0c 又叫做身份验证攻击 xff
  • 大一java程序设计的某次作业题解

    题目描述 xff1a 设计程序实现输入日期及机票张数 xff0c 计算出应付金额 假设北京至上海的机票全价为 1200 元 张 xff0c 以 2017 年为例进行程序编写 xff0c 所有的法定假日 xff0c 机票无折扣 xff1b 除
  • D. Make It Round(1759D)

    要求n k后缀0数量最多 xff08 k lt 61 m xff09 xff0c 且n k尽可能大 比赛时思路 xff08 错误 xff09 xff1a 10是由2和5组成 xff0c 故先统计n的因子包含2的个数num2 包含5的个数nu
  • Codeforces Round #841 (Div. 2)

    B Kill Demodogs 分析 显然要选择和两斜线的元素相加 所以答案可以表示为 xff1a 即 xff1a 根据公式 得答案为 但答案不能直接得这个 因为n的范围为1e9 xff0c 而ull的范围为1e20 xff0c 答案的第一
  • Educational Codeforces Round 141 Editorial C~D

    1783C Yet Another Tournament 分析 正解思路是贪心 开始自己也想的贪心 xff1a 首先显然打败的人数越多越好 xff0c 然后选择权值最小的人打败 这个思路前半部分没问题 xff0c 后半部分过不了样例的第二个
  • Codeforces Round #844 (Div. 1 + Div. 2, based on VK Cup 2022 - Elimination Round) D

    1781D Many Perfect Squares 分析 对于每组 xff0c 若和均为完全平方数 xff0c 则存在 xff1a 所以枚举所有 xff0c 对于每个 xff0c 枚举其所有 双因子对 xff0c 若两个因子之差为偶数 x
  • 匹配已有字符串

    生活小妙招 通过set和substr函数 xff0c 方便快捷地写出匹配已有字符串的代码 前置芝士 xff1a set使用详解 题目 xff1a G Perfect Word 代码实现 通过set 43 string的substr的使用快速
  • 在vue中使用rules的定义和校验规则

    表单内容里面定义属性 lt Form ref 61 34 rulesForm 34 model 61 34 rulesForm 34 label width 61 34 100 34 rules 61 34 rules 34 gt lt F
  • Codeforces Raif Round 1 (Div. 1 + Div. 2) D

    D Bouncing Boomerangs 分析 一个stack用于存只有一个的物品的行的物品位置 xff0c 一个queue用于存有两个物品的行的左边物品位置 xff08 其实这两个容器用stack还是queue无所谓 xff0c 只是这
  • 使用django服务的前置操作

    注意 xff1a 这是一个用于小组作业说明的内部文章 xff0c 如果你在寻找部署django服务的完整流程 xff0c 请退出此文章 python版本 xff1a 3 7 输入命令 xff0c 安装django工具 xff1a pip i
  • Codeforces Round #853 (Div. 2) C题

    CF1789C Serval and Toxel 39 s Arrays 学弟的思路 思路清晰 xff0c 代码简洁明了 xff0c 吊打目前80 以上题解 分析 记录每个数字在多少个数组中出现过 xff0c 即记录每个数字出现的次数 然后

随机推荐