# 纹理贴图集

如果你正在从事一款大型复杂的游戏,你需要快速的利用雪碧图创建精灵。而纹理贴图集就是用来帮助你完成这件事的。纹理贴图集是一个JSON数据文件,其中包含子图像在雪碧图上的位置和大小。如果你使用纹理贴图集,你只需要知道子图像的名字。您可以按任意顺序排列雪碧图,JSON文件将为您跟踪它们的大小和位置。这真的很方便,因为这意味着雪碧图的大小和位置不会硬编码到你的游戏程序中。如果你改变了雪碧图,比如添加图片,调整大小,或者删除图片,只要更新JSON文件,你的游戏就会使用这些数据来显示正确的图片。你不需要对你的游戏代码做任何改变。

Pixi与标准的JSON纹理贴图集格式兼容,该格式由一个名为“纹理打包器Texture Packer (opens new window)”的工具输出。纹理打包器的基本许可证是免费的。让我们看看如何使用它来制作纹理贴图集,并将图集加载到Pixi中。(除了Texture Packer,还有其它工具供你选择,如Shoebox (opens new window)spritesheet.js (opens new window)。她们标准格式输出的PNG和JSON文件,与Pixi兼容。)

# 制作纹理贴图集

首先,首先要收集在游戏中使用到的单个图像文件。

(本节中的所有图像均由Lanea Zimmerman创建。您可以在此处 (opens new window)找到她的更多作品。感谢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.jsontreasureHunter.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}
},
1
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来说都有不同的做法。所以对每一个图像空出一两个像素,可以确保所有的图像显示一致。 (此处译文源自官方文档,略有修改,点击查看 (opens new window)

(注意:如果图形周围有两个像素填充,并且在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);
1
2
3

现在每一个图片都被加载到Pixi缓存里了,您可以使用与Texture Packer中相同的名称访问缓存中的每个纹理(“blob.png”,“dungeon.png”,“explorer.png”等)。

# 通过纹理贴图集创建精灵

Pixi提供了三种从纹理图集创建精灵的方法: 1、使用纹理缓存TextureCache

let texture = TextureCache["frameId.png"],
    sprite = new Sprite(texture);
1
2

2、如果你已经使用Pixi的loader加载了纹理贴图集,那你可以用loader的resource来创建:

let sprite = new Sprite(
  resources["images/treasureHunter.json"].textures["frameId.png"]
);
1
2
3

3、为了创建一个精灵,输入了太多的东西!所以我建议你创建一个别名id,指向贴图集的textures对象,就像这样:

let id = PIXI.loader.resources["images/treasureHunter.json"].textures; 
1

然后你就这样创建精灵:

let sprite = new Sprite(id["frameId.png"]);
1

下面介绍如何在setup函数中使用这三种方法来创建和显示dungeonexplorertreasure精灵。

//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);
}
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

允许这段代码显示效果如下:

stage的大小是512×512像素,您可以在上面的代码中看到,app.stage.heightapp.stage.width属性用于对齐精灵。下面是explorer的y坐标垂直居中的方法:

explorer.y = app.stage.height / 2 - explorer.height / 2;
1

学习使用纹理贴图集创建和显示精灵是一个重要的要求。所以在我们继续之前,请你尝试把剩下的精灵(blobexit门)添加进去。完成后,这样您就可以生成这样一个场景:

正南是完成这些操作的完整代码。 (您可以在此存储库中的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>
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
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;
1
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;
1
2

bloby坐标是0-512之间的随机值。这些值是通过自定义方法randomInt生成,它会在给定的两个数字范围内生成随机值。

randomInt(lowestNumber, highestNumber)
1

这意味着如果你想要一个1到10之间的随机数,你可以这样写:

let randomNumber = randomInt(1, 10);
1

下面是randomInt的完整代码:

function randomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
1
2
3
lastUpdate: 11/16/2021, 6:11:55 AM