Cocos Creator 图集 plist 格式完全解析:TexturePacker 合图原理与动画序列实战

引言

在 2D 游戏开发中,纹理图集(Texture Atlas)是优化渲染性能的核心技术。通过将多张小图合并到一张大图中,可以显著减少 Draw Call 次数,提升游戏帧率。Cocos Creator 支持 TexturePacker 生成的 plist 格式图集文件,不仅可以静态引用图片,还能通过解析 plist 数据实现帧动画播放。本文将深入解析 plist 文件的数据结构、各字段含义,以及在实际开发中的应用技巧。

为什么需要纹理图集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
┌─────────────────────────────────────────────────────────────────────┐
│ 纹理图集优化原理 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 不使用图集(3 个 Draw Call) │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 图片1 │ │ 图片2 │ │ 图片3 │ │
│ │ .png │ │ .png │ │ .png │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
│ └────────────┴────────────┘ │
│ │ │
│ 3 次 GPU 绘制 │
│ │
│ ──────────────────────────────────────────────────────────────── │
│ │
│ 使用图集(1 个 Draw Call) │
│ ┌─────────────────────────┐ │
│ │ ┌───┬───┬───┐ │ │
│ │ │ 1 │ 2 │ 3 │ │ 一张大图 │
│ │ └───┴───┴───┘ │ │
│ │ 图集.png │ │
│ └──────────┬──────────────┘ │
│ │ │
│ 1 次 GPU 绘制 │
│ │
│ 优势: │
│ • 减少 Draw Call(从 N 次降到 1 次) │
│ • 减少内存碎片 │
│ • 方便实现帧动画 │
│ │
└─────────────────────────────────────────────────────────────────────┘
方案 Draw Call 内存占用 加载速度
独立图片 N 次 碎片化
纹理图集 1 次 连续

plist 文件结构解析

整体结构

plist 文件本质上是一个 XML 或 JSON 格式的数据文件,描述了大图中每个小图的位置信息:

1
2
3
4
5
6
7
8
{
"frames": {
// 每张小图的裁剪信息
},
"metadata": {
// 图集元数据
}
}

frames 字段详解

frames 字段包含了每张原始小图在大图中的位置信息:

1
2
3
4
5
6
7
8
9
10
11
{
"frames": {
"sprite_01.png": {
"frame": "{{2, 2}, {100, 80}}",
"offset": "{0, 0}",
"rotated": false,
"sourceColorRect": "{{0, 0}, {100, 80}}",
"sourceSize": "{100, 80}"
}
}
}
字段 类型 说明
frame 字符串 小图在大图中的位置和尺寸 {x, y}, {width, height}
offset 字符串 小图中心点相对于原始图中心点的偏移
rotated 布尔 是否顺时针旋转 90 度
sourceColorRect 字符串 去除透明部分后的实际内容位置和尺寸
sourceSize 字符串 原始图片的完整尺寸

metadata 字段详解

1
2
3
4
5
6
7
8
{
"metadata": {
"format": 2,
"size": "{512, 256}",
"textureFileName": "atlas.png",
"premultiplyAlpha": false
}
}
字段 说明
format plist 格式版本(1-3)
size 大图的总尺寸
textureFileName 对应的大图文件名
premultiplyAlpha 是否预乘 Alpha

关键字段深度解析

frame 字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────────┐
│ frame 字段解析 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 大图 (atlas.png) │
│ ┌────────────────────────────────────────┐ │
│ │ (0,0) │ │
│ │ ┌──────────┐ │ │
│ │ │ sprite_1 │ (x=2, y=2) │ │
│ │ │ 100x80 │ 宽度=100, 高度=80 │ │
│ │ └──────────┘ │ │
│ │ ↑ │ │
│ │ frame: "{{2, 2}, {100, 80}}" │ │
│ │ │ │
│ └────────────────────────────────────────┘ │
│ │
│ 含义: │
│ • {2, 2}:小图左上角在大图中的坐标 │
│ • {100, 80}:小图的宽度和高度 │
│ │
└─────────────────────────────────────────────────────────────────────┘

rotated 字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
┌─────────────────────────────────────────────────────────────────────┐
│ rotated 字段解析 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 原始图片 (100x80) │
│ ┌──────────────────┐ │
│ │ │ │
│ │ │ │
│ └──────────────────┘ │
│ │
│ rotated = true 时(顺时针旋转 90°) │
│ ┌────────┐ │
│ │ │ │
│ │ │ │
│ │ │ 在大图中以 80x100 存储 │
│ │ │ 渲染时引擎会自动逆时针旋转 90° 还原 │
│ └────────┘ │
│ │
│ 目的:更紧密地排列图片,减少大图空白区域 │
│ │
└─────────────────────────────────────────────────────────────────────┘

offset 字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────────────┐
│ offset 字段解析 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ sourceSize: {112, 63} 原始图片大小 │
│ ┌────────────────────────┐ │
│ │ (透明区域) │ │
│ │ ┌──────────────────┐ │ │
│ │ │ 实际内容 │ │ sourceColorRect: {{4, 4}, {106, 59}} │
│ │ │ 106 x 59 │ │ │
│ │ └──────────────────┘ │ │
│ │ (透明区域) │ │
│ └────────────────────────┘ │
│ │
│ 原始中心点:{112/2, 63/2} = {56, 31.5} │
│ 实际内容中心点:{4 + 106/2, 4 + 59/2} = {57, 33.5} │
│ │
│ offset = {57 - 56, 33.5 - 31.5} = {1, 2} │
│ │
│ 注意:Cocos 坐标系 y 轴向上为正 │
│ 在 plist 中 offset = {1, -2} │
│ │
│ 含义:实际内容相对于原始内容中心点的偏移 │
│ │
└─────────────────────────────────────────────────────────────────────┘

MovieClip 动画格式

数据格式示例

Egret 引擎的 MovieClip 动画数据格式(Cocos Creator 可兼容解析):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
{
"mc": {
"character_walk": {
"frameRate": 24,
"labels": [
{
"name": "fall",
"frame": 1,
"end": 3
},
{
"name": "move",
"frame": 4,
"end": 6
}
],
"events": [
{
"name": "@fallhalf",
"frame": 2
},
{
"name": "@movehalf",
"frame": 5
}
],
"frames": [
{
"res": "7ADF0F11",
"x": 224,
"y": 123
},
{
"res": "69D013A4",
"x": 228,
"y": 122
}
]
}
},
"res": {
"7ADF0F11": {
"x": 1,
"y": 87,
"w": 105,
"h": 80
}
}
}

mc 字段说明

字段 说明
mcName MovieClip 名称,可包含多个动画
frameRate 帧率,默认 24fps
labels 帧标签列表,用于分段播放
events 帧事件列表,以 @ 开头
frames 关键帧数据列表

labels 标签

1
2
3
4
5
{
"name": "move",
"frame": 4,
"end": 6
}
字段 说明
name 标签名称
frame 标签起始帧
end 标签结束帧(可在此范围内循环)

res 资源字段

1
2
3
4
5
6
7
8
{
"7ADF0F11": {
"x": 1,
"y": 87,
"w": 105,
"h": 80
}
}
字段 说明
x 资源在纹理集中的 x 坐标
y 资源在纹理集中的 y 坐标
w 资源宽度
h 资源高度

Cocos Creator 中使用图集

静态图片引用

1
2
3
4
5
6
7
8
9
10
11
12
13
// 加载图集
cc.loader.loadRes("atlas/plist", cc.SpriteAtlas, (err, atlas) => {
if (err) {
cc.error('加载图集失败:', err);
return;
}

// 获取指定名称的 SpriteFrame
const frame = atlas.getSpriteFrame('sprite_01');

// 设置到 Sprite 组件
this.node.getComponent(cc.Sprite).spriteFrame = frame;
});

帧动画实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
const { ccclass, property } = cc._decorator;

@ccclass
export default class FrameAnimation extends cc.Component {

@property(cc.SpriteAtlas)
atlas: cc.SpriteAtlas = null;

@property
frameRate: number = 12;

private _frames: cc.SpriteFrame[] = [];
private _currentFrame: number = 0;
private _isPlaying: boolean = false;

onLoad() {
// 收集图集中的所有帧
const spriteFrames = this.atlas.getSpriteFrames();

// 按名称排序(假设名称为 frame_01, frame_02...)
this._frames = spriteFrames.sort((a, b) => {
return a.name.localeCompare(b.name);
});
}

/**
* 播放动画
*/
play(loop: boolean = false) {
this._isPlaying = true;
this._currentFrame = 0;

this.schedule(() => {
this._updateFrame();

if (!loop && this._currentFrame >= this._frames.length - 1) {
this.stop();
}
}, 1 / this.frameRate);
}

/**
* 停止动画
*/
stop() {
this._isPlaying = false;
this.unscheduleAllCallbacks();
}

private _updateFrame() {
if (this._frames.length === 0) return;

const sprite = this.getComponent(cc.Sprite);
sprite.spriteFrame = this._frames[this._currentFrame];

this._currentFrame = (this._currentFrame + 1) % this._frames.length;
}
}

动态创建图集(运行时)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/**
* 从纹理动态创建图集
*/
function createAtlasFromTexture(texture: cc.Texture2D, frames: any[]): cc.SpriteAtlas {
const atlas = new cc.SpriteAtlas();

for (const frameData of frames) {
const rect = new cc.Rect(
frameData.x,
frameData.y,
frameData.width,
frameData.height
);

const spriteFrame = new cc.SpriteFrame(
texture,
rect,
frameData.rotated || false,
cc.v2(0, 0), // offset
new cc.Size(frameData.sourceWidth, frameData.sourceHeight)
);

spriteFrame.name = frameData.name;
atlas.addSpriteFrame(spriteFrame);
}

return atlas;
}

TexturePacker 设置建议

导出配置

设置项 推荐值 说明
Data Format Cocos2d 生成 plist + png
Texture Format PNG 支持透明度
Size Constraints POT (Power of 2) 兼容旧设备
Allow Rotation 勾选 提高空间利用率
Trim Mode Trim 去除透明边缘
Algorithm MaxRects 最佳排列算法

图集尺寸选择

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌─────────────────────────────────────────────────────────────────────┐
│ 图集尺寸选择指南 │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ 小图集 (512 x 512) │
│ ─────────────────── │
│ • UI 按钮、小图标 │
│ • 加载速度快 │
│ │
│ 中图集 (1024 x 1024) │
│ ───────────────────── │
│ • 角色动画帧 │
│ • 场景元素 │
│ │
│ 大图集 (2048 x 2048) │
│ ───────────────────── │
│ • 大型角色、特效 │
│ • 注意老设备可能不支持 │
│ │
│ 最大限制 │
│ ─────────── │
│ • OpenGL ES 2.0: 2048 x 2048 │
│ • OpenGL ES 3.0: 4096 x 4096 │
│ │
└─────────────────────────────────────────────────────────────────────┘

总结

Cocos Creator 纹理图集的核心要点:

  1. 优化原理:将多张小图合并为一张大图,减少 Draw Call 和内存碎片
  2. plist 结构frames 描述每张图的裁剪信息,metadata 描述图集元数据
  3. 关键字段frame 定位大图中的位置,offset 处理透明边缘偏移,rotated 标识旋转优化
  4. 动画应用:通过 cc.SpriteAtlas 获取 SpriteFrame 序列,实现帧动画播放
  5. 工具配置:TexturePacker 选择 Cocos2d 格式,启用 Trim 和 Allow Rotation 优化空间利用率
  6. 尺寸限制:注意目标平台的纹理最大尺寸限制,老设备通常只支持 2048x2048

通过深入理解 plist 格式和图集原理,可以更高效地管理游戏资源,优化渲染性能。