Grouping Sprites

Groups let you create game scenes, and manage similar sprites together as single units. Pixi has an object called a Container that lets you do this. Let’s find out how it works.

Imagine that you want to display three sprites: a cat, hedgehog and tiger. Create them, and set their positions - but don’t add them to the stage.

  1. //The cat
  2. let cat = new Sprite(id["cat.png"]);
  3. cat.position.set(16, 16);
  4. //The hedgehog
  5. let hedgehog = new Sprite(id["hedgehog.png"]);
  6. hedgehog.position.set(32, 32);
  7. //The tiger
  8. let tiger = new Sprite(id["tiger.png"]);
  9. tiger.position.set(64, 64);

Next, create an animals container to group them all together like this:

  1. let animals = new PIXI.Container();

Then use addChild to add the sprites to the group.

  1. animals.addChild(cat);
  2. animals.addChild(hedgehog);
  3. animals.addChild(tiger);

Finally add the group to the stage.

  1. app.stage.addChild(animals);

(As you know, the stage object is also a Container. It’s the root container for all Pixi sprites.)

Here’s what this code produces:

Grouping sprites

What you can’t see in that image is the invisible animals group that’s containing the sprites.

Grouping sprites

You can now treat the animals group as a single unit. You can think of a Container as a special kind of sprite that doesn’t have a texture.

If you need a list of all the child sprites that animals contains, use its children array to find out.

  1. console.log(animals.children)
  2. //Displays: Array [Object, Object, Object]

This tells you that animals has three sprites as children.

Because the animals group is just like any other sprite, you can change its x and y values, alpha, scale and all the other sprite properties. Any property value you change on the parent container will affect the child sprites in a relative way. So if you set the group’s x and y position, all the child sprites will be repositioned relative to the group’s top left corner. What would happen if you set the animals‘s x and y position to 64?

  1. animals.position.set(64, 64);

The whole group of sprites will move 64 pixels right and 64 pixels to the down.

Grouping sprites

The animals group also has its own dimensions, which is based on the area occupied by the containing sprites. You can find its width and height values like this:

  1. console.log(animals.width);
  2. //Displays: 112
  3. console.log(animals.height);
  4. //Displays: 112

Group width and height

What happens if you change a group’s width or height?

  1. animals.width = 200;
  2. animals.height = 200;

All the child sprites will scale to match that change.

Group width and height

You can nest as many Containers inside other Containers as you like, to create deep hierarchies if you need to. However, a DisplayObject (like a Sprite or another Container) can only belong to one parent at a time. If you use addChild to make a sprite the child of another object, Pixi will automatically remove it from its current parent. That’s a useful bit of management that you don’t have to worry about.

Local and global positions

When you add a sprite to a Container, its x and y position is relative to the group’s top left corner. That’s the sprite’s local position For example, what do you think the cat’s position is in this image?

Grouping sprites

Let’s find out:

  1. console.log(cat.x);
  2. //Displays: 16

16? Yes! That’s because the cat is offset by only 16 pixel’s from the group’s top left corner. 16 is the cat’s local position.

Sprites also have a global position. The global position is the distance from the top left corner of the stage, to the sprite’s anchor point (usually the sprite’s top left corner.) You can find a sprite’s global position with the help of the toGlobal method. Here’s how:

  1. parentSprite.toGlobal(childSprite.position)

That means you can find the cat’s global position inside the animals group like this:

  1. console.log(animals.toGlobal(cat.position));
  2. //Displays: Object {x: 80, y: 80...};

That gives you an x and y position of 80. That’s exactly the cat’s global position relative to the top left corner of the stage.

What if you want to find the global position of a sprite, but don’t know what the sprite’s parent container is? Every sprite has a property called parent that will tell you what the sprite’s parent is. If you add a sprite directly to the stage, then stage will be the sprite’s parent. In the example above, the cat‘s parent is animals. That means you can alternatively get the cat’s global position by writing code like this:

  1. cat.parent.toGlobal(cat.position);

And it will work even if you don’t know what the cat’s parent container currently is.

There’s one more way to calculate the global position! And, it’s actually the best way, so listen up! If you want to know the distance from the top left corner of the canvas to the sprite, and don’t know or care what the sprite’s parent containers are, use the getGlobalPosition method. Here’s how to use it to find the tiger’s global position:

  1. tiger.getGlobalPosition().x
  2. tiger.getGlobalPosition().y

This will give you x and y values of 128 in the example that we’ve been using. The special thing about getGlobalPosition is that it’s highly precise: it will give you the sprite’s accurate global position as soon as its local position changes. I asked the Pixi development team to add this feature specifically for accurate collision detection for games. (Thanks, Matt and the rest of the team for adding it!)

What if you want to convert a global position to a local position? you can use the toLocal method. It works in a similar way, but uses this general format:

  1. sprite.toLocal(sprite.position, anyOtherSprite)

Use toLocal to find the distance between a sprite and any other sprite. Here’s how you could find out the tiger’s local position, relative to the hedgehog.

  1. tiger.toLocal(tiger.position, hedgehog).x
  2. tiger.toLocal(tiger.position, hedgehog).y

This gives you an x value of 32 and a y value of 32. You can see in the example images that the tiger’s top left corner is 32 pixels down and to the left of the hedgehog’s top left corner.

Using a ParticleContainer to group sprites

Pixi has an alternative, high-performance way to group sprites called a ParticleContainer (PIXI.particles.ParticleContainer). Any sprites inside a ParticleContainer will render 2 to 5 times faster than they would if they were in a regular Container. It’s a great performance boost for games.

Create a ParticleContainer like this:

  1. let superFastSprites = new PIXI.particles.ParticleContainer();

Then use addChild to add sprites to it, just like you would with any ordinary Container.

You have to make some compromises if you decide to use a ParticleContainer. Sprites inside a ParticleContainer only have a few basic properties: x, y, width, height, scale, pivot, alpha, visible – and that’s about it. Also, the sprites that it contains can’t have nested children of their own. A ParticleContainer also can’t use Pixi’s advanced visual effects like filters and blend modes. Each ParticleContainer can use only one texture (so you’ll have to use a spritesheet if you want Sprites with different appearances). But for the huge performance boost that you get, those compromises are usually worth it. And you can use Containers and ParticleContainers simultaneously in the same project, so you can fine-tune your optimization.

Why are sprites in a Particle Container so fast? Because the positions of the sprites are being calculated directly on the GPU. The Pixi development team is working to offload as much sprite processing as possible on the GPU, so it’s likely that the latest version of Pixi that you’re using will have much more feature-rich ParticleContainer than what I’ve described here. Check the current ParticleContainer documentation for details.

Where you create a ParticleContainer, there are four optional arguments you can provide: size, properties, batchSize and autoResize.

  1. let superFastSprites = new ParticleContainer(maxSize, properties, batchSize, autoResize);

The default value for maxSize is 1500. So, if you need to contain more sprites, set it to a higher number. The properties argument is an object with 5 Boolean values you can set: scale, position, rotation, uvs and alphaAndTint. The default value of position is true, but all the others are set to false. That means that if you want to change the rotation, scale, tint, or uvs of sprite in the ParticleContainer, you have to set those properties to true, like this:

  1. let superFastSprites = new ParticleContainer(
  2. size,
  3. {
  4. rotation: true,
  5. alphaAndtint: true,
  6. scale: true,
  7. uvs: true
  8. }
  9. );

But, if you don’t think you’ll need to use these properties, keep them set to false to squeeze out the maximum amount of performance.

What’s the uvs property? Only set it to true if you have particles which change their textures while they’re being animated. (All the sprite’s textures will also need to be on the same tileset image for this to work.)

(Note: UV mapping is a 3D graphics display term that refers to the x and y coordinates of the texture (the image) that is being mapped onto a 3D surface. U is the x axis and V is the y axis. WebGL already uses x, y and z for 3D spatial positioning, so U and V were chosen to represent x and y for 2D image textures.)

(I’m not sure what exactly what those last two optional arguments, batchSize and autoResize, so if anyone knows, please us know in the Issues!)