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

Posted 林新发

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了游戏开发小技TexturePacker生成的图集逆向切分成精灵小图(json | python | PIL | TextureUnPacker | 逆向 | 切图)相关的知识,希望对你有一定的参考价值。

文章目录

一、前言

嗨,大家好,我是林新发。
之前有同事找我问我能不能通过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(以上是关于游戏开发小技TexturePacker生成的图集逆向切分成精灵小图(json | python | PIL | TextureUnPacker | 逆向 | 切图)的主要内容,如果未能解决你的问题,请参考以下文章

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

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

TexturePacker 图集生成工具

LibGDX - TexturePacker2不生成/打包图像

Unity3D-UGUI图集打包与动态使用(TexturePacker)

移相器的 TexturePacker 设置