# 纹理贴图集
如果你正在从事一款大型复杂的游戏,你需要快速的利用雪碧图创建精灵。而纹理贴图集
就是用来帮助你完成这件事的。纹理贴图集
是一个JSON数据文件,其中包含子图像在雪碧图上的位置和大小。如果你使用纹理贴图集,你只需要知道子图像的名字。您可以按任意顺序排列雪碧图,JSON文件将为您跟踪它们的大小和位置。这真的很方便,因为这意味着雪碧图的大小和位置不会硬编码到你的游戏程序中。如果你改变了雪碧图,比如添加图片,调整大小,或者删除图片,只要更新JSON文件,你的游戏就会使用这些数据来显示正确的图片。你不需要对你的游戏代码做任何改变。
Pixi与标准的JSON纹理贴图集格式兼容,该格式由一个名为“纹理打包器Texture Packer”的工具输出。纹理打包器的基本许可证是免费的。让我们看看如何使用它来制作纹理贴图集,并将图集加载到Pixi中。(除了Texture Packer,还有其它工具供你选择,如Shoebox和spritesheet.js。她们标准格式输出的PNG和JSON文件,与Pixi兼容。)
# 制作纹理贴图集
首先,首先要收集在游戏中使用到的单个图像文件。
(本节中的所有图像均由Lanea Zimmerman创建。您可以在此处找到她的更多作品。感谢Lanea!)
接下来,打开Texture Packer,选择JSON Hash作为框架类型。拖动你的图像到Texture Packer的工作区。(你也可以将Texture Packer指向任何包含你的图片的文件夹。)它将自动排列这些图片,并给它们命名为原始图像的名字。
(如果你使用免费版的Texture Packer,将Algorithm设置为Basic
,将Trim mode
设置为None
,将Extrude设置为0
,将Size constraints设置为Any size
,将PNG Opt Level向左滑动到0
。这些基本设置,可让免费版本的Texture Packer创建您的文件且不产生任何警告或错误。
完成上面的设置后,单击Publish按钮。选择文件名和位置,然后保存。 您会得到2个文件:一个PNG文件和一个JSON文件。在这个例子中,我的两个文件名是treasureHunter.json
和treasureHunter.png
。为方便起见,最好把它们保存在您的项目的images
文件夹。(可以将JSON文件视为图像文件的额外元数据,因此将两个文件保存在同一个文件夹中是有意义的)。JSON文件描述了雪碧图中每个子图像的名称、大小和位置。下面是描述blob monster子图像的一段摘录。
WARNING
此处翻译需斟酌
"blob.png":
{
"frame": {"x":55,"y":2,"w":32,"h":24},
"rotated": false,
"trimmed": false,
"spriteSourceSize": {"x":0,"y":0,"w":32,"h":24},
"sourceSize": {"w":32,"h":24},
"pivot": {"x":0.5,"y":0.5}
},
2
3
4
5
6
7
8
9
treasureHunter.json
文件还包含“ dungeon.png”,“ door.png”,“ exit.png”和“ explorer.png”属性,每个属性都具有类似的数据。这些子图像中都称为帧frame
。有了这些数据,您就不需要知道每个子图像在雪碧图中的大小和位置。你只需要知道sprite精灵
的帧id(frame id)
,帧id只是原始图像文件的名称,比如“blob.png”或“explorer.png”。
使用纹理贴图集的其中一个优点是,您可以在每个图像周围轻松添加2个像素填充(Texture Packer默认这么做)。这对于保护图像的出血(译者:出血是排版和图片处理方面的专有名词,指在主要内容周围留空以便印刷或裁切)来说很重要。出血对于防止两个图片相邻而相互影响来说很重要。这种情况往往发生于你的GPU渲染某些图片的时候。把边上的一两个像素加上去还是不要?这对于每一个GPU来说都有不同的做法。所以对每一个图像空出一两个像素,可以确保所有的图像显示一致。 (此处译文源自官方文档,略有修改,点击查看)
(注意:如果图形周围有两个像素填充,并且在Pixi显示图形时仍然发现奇怪的“偏离一像素”问题,请尝试更改纹理的缩放模式算法。方法如下:texture.baseTexture.scaleMode = PIXI.SCALE_MODES.NEAREST;
。由于GPU浮点舍入错误,有可能会出现这个错误。)
现在你知道了如何创建一个纹理贴图集,让我们看看如在你的游戏代码中加载它们吧。
# 加载纹理贴图集
我们需要使用Pixi的loader
把纹理贴图集导入进来。如果JSON文件是由Texture Packer制作的,loader
将自动解析贴图集上的每一帧数据,并创建纹理。下面就是使用loader
加载treasureHunter.json
文件的示例。加载完成后,就会执行setup
方法。
loader
.add("images/treasureHunter.json")
.load(setup);
2
3
现在每一个图片都被加载到Pixi缓存里了,您可以使用与Texture Packer中相同的名称访问缓存中的每个纹理(“blob.png”,“dungeon.png”,“explorer.png”等)。
# 通过纹理贴图集创建精灵
Pixi提供了三种从纹理图集创建精灵的方法:
1、使用纹理缓存TextureCache
:
let texture = TextureCache["frameId.png"],
sprite = new Sprite(texture);
2
2、如果你已经使用Pixi的loader
加载了纹理贴图集,那你可以用loader的resource
来创建:
let sprite = new Sprite(
resources["images/treasureHunter.json"].textures["frameId.png"]
);
2
3
3、为了创建一个精灵,输入了太多的东西!所以我建议你创建一个别名id
,指向贴图集的textures
对象,就像这样:
let id = PIXI.loader.resources["images/treasureHunter.json"].textures;
然后你就这样创建精灵:
let sprite = new Sprite(id["frameId.png"]);
下面介绍如何在setup
函数中使用这三种方法来创建和显示dungeon
、explorer
和treasure
精灵。
//Define variables that might be used in more
//than one function
let dungeon, explorer, treasure, id;
function setup() {
//There are 3 ways to make sprites from textures atlas frames
//1. Access the `TextureCache` directly
let dungeonTexture = TextureCache["dungeon.png"];
dungeon = new Sprite(dungeonTexture);
app.stage.addChild(dungeon);
//2. Access the texture using through the loader's `resources`:
explorer = new Sprite(
resources["images/treasureHunter.json"].textures["explorer.png"]
);
explorer.x = 68;
//Center the explorer vertically
explorer.y = app.stage.height / 2 - explorer.height / 2;
app.stage.addChild(explorer);
//3. Create an optional alias called `id` for all the texture atlas
//frame id textures.
id = PIXI.loader.resources["images/treasureHunter.json"].textures;
//Make the treasure box using the alias
treasure = new Sprite(id["treasure.png"]);
app.stage.addChild(treasure);
//Position the treasure next to the right edge of the canvas
treasure.x = app.stage.width - treasure.width - 48;
treasure.y = app.stage.height / 2 - treasure.height / 2;
app.stage.addChild(treasure);
}
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
允许这段代码显示效果如下:
stage的大小是512×512像素,您可以在上面的代码中看到,app.stage.height
和app.stage.width
属性用于对齐精灵。下面是explorer
的y坐标垂直居中的方法:
explorer.y = app.stage.height / 2 - explorer.height / 2;
学习使用纹理贴图集创建和显示精灵是一个重要的要求。所以在我们继续之前,请你尝试把剩下的精灵(blob
和exit
门)添加进去。完成后,这样您就可以生成这样一个场景:
正南是完成这些操作的完整代码。 (您可以在此存储库中的examples / spriteFromTextureAtlas.html文件中找到)请注意,已创建多个位置随机的Blob
精灵并添加到舞台上。
<!doctype html>
<meta charset="utf-8">
<title>Make a sprite from a texture atlas</title>
<body>
<script src="../pixi/pixi.min.js"></script>
<script>
//Aliases
let Application = PIXI.Application,
Container = PIXI.Container,
loader = PIXI.loader,
resources = PIXI.loader.resources,
TextureCache = PIXI.utils.TextureCache,
Sprite = PIXI.Sprite,
Rectangle = PIXI.Rectangle;
//Create a Pixi Application
let app = new Application({
width: 512,
height: 512,
antialias: true,
transparent: false,
resolution: 1
}
);
//Add the canvas that Pixi automatically created for you to the HTML document
document.body.appendChild(app.view);
//load a JSON file and run the `setup` function when it's done
loader
.add("images/treasureHunter.json")
.load(setup);
//Define variables that might be used in more
//than one function
let dungeon, explorer, treasure, door, id;
function setup() {
//There are 3 ways to make sprites from textures atlas frames
//1. Access the `TextureCache` directly
let dungeonTexture = TextureCache["dungeon.png"];
dungeon = new Sprite(dungeonTexture);
app.stage.addChild(dungeon);
//2. Access the texture using throuhg the loader's `resources`:
explorer = new Sprite(
resources["images/treasureHunter.json"].textures["explorer.png"]
);
explorer.x = 68;
//Center the explorer vertically
explorer.y = app.stage.height / 2 - explorer.height / 2;
app.stage.addChild(explorer);
//3. Create an optional alias called `id` for all the texture atlas
//frame id textures.
id = PIXI.loader.resources["images/treasureHunter.json"].textures;
//Make the treasure box using the alias
treasure = new Sprite(id["treasure.png"]);
app.stage.addChild(treasure);
//Position the treasure next to the right edge of the canvas
treasure.x = app.stage.width - treasure.width - 48;
treasure.y = app.stage.height / 2 - treasure.height / 2;
app.stage.addChild(treasure);
//Make the exit door
door = new Sprite(id["door.png"]);
door.position.set(32, 0);
app.stage.addChild(door);
//Make the blobs
let numberOfBlobs = 6,
spacing = 48,
xOffset = 150;
//Make as many blobs as there are `numberOfBlobs`
for (let i = 0; i < numberOfBlobs; i++) {
//Make a blob
let blob = new Sprite(id["blob.png"]);
//Space each blob horizontally according to the `spacing` value.
//`xOffset` determines the point from the left of the screen
//at which the first blob should be added.
let x = spacing * i + xOffset;
//Give the blob a random y position
//(`randomInt` is a custom function - see below)
let y = randomInt(0, app.stage.height - blob.height);
//Set the blob's position
blob.x = x;
blob.y = y;
//Add the blob sprite to the stage
app.stage.addChild(blob);
}
}
//The `randomInt` helper function
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
</script>
</body>
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
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
您可以在上面的代码中看到所有blob精灵都是使用for
循环创建的。 每个blob
沿x
轴均匀分布,如下所示:
let x = spacing * i + xOffset;
blob.x = x;
2
spacing
变量的值是48,xOffset
变量的值是150。这意味着第一个blob
精灵x
坐标位置是150。所以它在舞台左侧偏移150像素的位置上。后面的每个精灵x
值,都在上一个精灵位置的基础上加48像素。这将创建一个从左到右沿地牢地板(dungeon)均匀间隔的blob怪物线。
每一个blob
精灵在y
坐标上随机的,代码如下:
let y = randomInt(0, stage.height - blob.height);
blob.y = y;
2
blob
的y
坐标是0-512之间的随机值。这些值是通过自定义方法randomInt
生成,它会在给定的两个数字范围内生成随机值。
randomInt(lowestNumber, highestNumber)
这意味着如果你想要一个1到10之间的随机数,你可以这样写:
let randomNumber = randomInt(1, 10);
下面是randomInt
的完整代码:
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
2
3