---
title: "Panda2 sprite sheet animations tutorial"
description: "Creating a sprite sheet animation, optimizing the sheet for fast downloads, building a basic game scene with a character."
canonical: "https://www.codeandweb.com/texturepacker/tutorials/panda2-sprite-sheet-tutorial"
date: "2023-09-13T00:00:00.000Z"
modified: "2023-09-13T00:00:00.000Z"
source: "html"
---

# Panda2 sprite sheet animations tutorial

Andreas Löw

[texturepacker](https://www.codeandweb.com/texturepacker/tutorials.md),  [tutorial](https://www.codeandweb.com/tags/tutorial),  [panda](https://www.codeandweb.com/tutorials/panda.md),  [sprite-sheets](https://www.codeandweb.com/tags/sprite-sheets),  [animation](https://www.codeandweb.com/tags/animation)

[GitHub](https://github.com/CodeAndWeb/panda2-sprite-sheet-example)

![Panda2 sprite sheet animations tutorial](https://www.codeandweb.com/static/d921e925ff44a306613fc81fbb6e38ec/a6312/_opengraph-image.png)

In this tutorial you are going to learn how to use sprite sheets for animations in your Panda 2 project.

You'll create a simple scene with a walking character that you can control using the keyboard: Click inside the scene, use `←︎` and `→︎` to move betty:

You can follow the tutorial step by step to build this game scene, starting from an almost blank Panda 2 project.

The complete tutorial code is available on [GitHub](https://github.com/CodeAndWeb/panda2-sprite-sheet-example). All important stages of the project are available in the sub-folders.

## Creating the basic game scene

Please open the folder _01-start_ in Finder / Explorer. You should see the basic project structure like this:

*   media
    *   betty.atlas - the atlas description file
    *   betty.png - the sprite atlas / sprite sheet
*   spritesheets
    *   betty - Folder containing the sprites
    *   betty.tps - TexturePacker project file
*   src
    *   engine - Panda 2 engine
    *   game
        *   config.js - configuration
        *   main.js - the source code

The sprite atlas is already packed for you — I'll explain how to add new sprites, edit pivot points and optimize the sprite sheet for faster rendering and downloads later.

Open the project in Panda 2 — you should see a simple game scene with some floor tiles and a background:

  ![Image](https://www.codeandweb.com/static/2f9ef27ef362c76edcbc3873ad3802f7/283d3/01-start.png)

Let's have a quick look at the source code. The project is set up for landscape mode in _config.js_:

```js
game.config = {
    name: 'Sprite Sheet Demo',

    system: {
        width: 1024,
        height: 768,
        scale: true,
        center: true,
        resize: false
    },

    mobile: {
    }
};
```

Let's have a look at _main.js_:

First you have to load the sprite sheet:

```js
// load sprite sheet
game.addAsset('betty.atlas');
```

This loads both — the sprite sheet data and the image. The data file contains the information about the sprites, their sizes and positions in the sheet.

Next is initializing the scene in the `init` method:

```js
    init: function() {
        // set background color for the scene
        this.backgroundColor = '#cceeff';

        // create floor tiles
        var groundY = this.makeFloor();
    },
```

The `makeFloor` method places some ground floor tiles at the bottom of the scene. It returns the y position of the floor tiles which we need to place the character later.

```js
    makeFloor: function() {
        for(var i=0; i<5; i++) {
            var floor = new game.Sprite("floor.png");
            floor.addTo(this.stage);
            floor.y = game.height-floor.height;
            floor.x = floor.width*i;
        }
        return game.height-floor.height;
    }
```

Note that it references a sprite `floor.png` — this is the name of a packed in the sheet. It's not a separate file that is loaded from file system or a server. This speeds up the start time of your game since only 1 image has to be loaded instead of loading each animation phase separately.

Here's the complete code of the _main.js_:

```js
game.module(
    'game.main'
)
.body(function() {

// load sprite sheet
game.addAsset('betty.atlas');

game.createScene('Main', {

    init: function() {
        // set background color for the scene
        this.backgroundColor = '#cceeff';

        // create floor tiles
        var groundY = this.makeFloor();
    },

    makeFloor: function() {
        for(var i=0; i<5; i++) {
            var floor = new game.Sprite("floor.png");
            floor.addTo(this.stage);
            floor.y = game.height-floor.height;
            floor.x = floor.width*i;
        }
        return game.height-floor.height;
    }
});

}); // module
```

So far you've used the sprite sheet that is part of the demo source. You are not going to learn how you can create your own sprite sheet, add or remove sprites and more:

## Creating your own sprite sheet

First, download TexturePacker — it's the tool you are going to use for this:

[Download TexturePacker 8.3.0](https://www.codeandweb.com/texturepacker/download/8.3.0/win)

for Windows

[Also available for macOS and Linux](https://www.codeandweb.com/texturepacker/download.md)

Install and start TexturePacker, activate the trial version for this tutorial.

Here's how you create the sprite sheet in 4 simple steps:

  ![How to create a sprite sheet in TexturePacker](https://www.codeandweb.com/static/f8e701bbf543dcc148f4aa4bb90e1d93/283d3/texturepacker-first-use.png)

1.  Drag and drop the folder containing your sprites onto TexturePacker
2.  Select **Panda 2** from the **Framework** dropdown
3.  Set the file name for the _.atlas_ file. The name for the image is automatically updated
4.  Press publish

That's it. Wasn't too complicated, was it?

TexturePacker has some nice features for you: You've added the folder containing the sprites, we call this a _smart folder_. It checks for all sprites inside the folder and adds them to the sheet.

It also preserves the folder structure as part of the sprite names. E.g. you can access the frame `stand/left.png`.

TexturePacker detects if you make changes — update, add or remove a sprite and loads the new sprites simply press **Publish** to write the new sprite sheet.

## Optimize your sprites: More FPS and faster loading

### Trimming

TexturePacker already does some nice things for you out of the box: It removes additional transparency around your sprites. This is called **trimming**. Why that?

1.  It reduces the file size
2.  It reduced memory usage
3.  It speeds up the rendering of your game

  ![Bad sprite sheet](https://www.codeandweb.com/static/cdcf572838bbbdffa92752705cceb31c/283d3/betty-untrimmed.png)

Bad sprite sheet: Untrimmed sprites contain transparency which wastes memory and performance. **4680kb RAM**

  ![Optimized sprite sheet](https://www.codeandweb.com/static/e17e35e7ef348748f4ede2596b5af95c/283d3/betty-trimmed.png)

Sprite sheet optimized by TexturePacker: Obsolete transparency is removed. **2993kb RAM**

This is because transparent pixels are not simply nothing: They are data — costing you 4 bytes of RAM each. The graphics hardware has to read each transparent pixel event if it decides not to paint it.

In terms of RAM usage: The original sprite sheet (with untrimmed sprites) consumes 4680kb. **Trimming the sprites reduces the memory usage by 36%** to 2993kb.

You might now be concerned about how this will affect your game code in Panda. The good answer is: Not at all.

The .atlas file TexturePacker also creates for you contains the exact positions and the original sprite sizes. You game code behaves as if the sprites still contain the transparency.

### Using 8-bit pngs (pngquant)

TexturePacker can even do more in terms of optimization: The current file size for the betty sheet is 407kb trimmed. Reducing the amount of colors to 256 also reduced the file size dramatically: 145 Kb remain — with almost no visual impact! **This is a reduction in download size by 64%**.

You can easily achieve this by setting **Texture Format** to **PNG-8 (indexed)**.

Let's not return to the game code.

## Playing an animation in Panda 2

Ok - back to our code. Let's add the Betty animation to the scene:

Add the following lines to the `init` method in _main.js_:

```js
this.sprite = game.Animation.fromTextures('right/');
this.sprite.play();
this.sprite.addTo(this.stage)
```

Ok — Betty is now running in the top left corner of the screen.

Panda 2 automatically takes all sprites starting with `right/` and builds an animation from them. Just make sure to prefix the sprite names with zeros. This is important because otherwise the order will be 1,10,11,... instead of 1,2,3...

Let's put her on the ground.

As you might remember there's the `groundY` variable that you returned from `makeFloor`. Let's use that to place the sprite. Append these lines to the previous block:

```js
this.sprite.position.set(game.width/2,groundY);
```

6 feet under... not what you expected? Right — Panda's default anchor point is top left.

A better anchor point would be the bottom center. The sprite is 256x256 — so the center is 128/256. Let's change that, append these lines to the previous block:

```js
this.sprite.anchor.set(128,256);
```

Ok. Let's change the animation to stand. Replace the first line with this:

```js
this.sprite = game.Animation.fromTextures('stand/right.png');
```

Why is Betty hovering above the floor? The problem is in the sprite:

  ![Sprite with anchor point at the bottom](https://www.codeandweb.com/static/4b4ff4b2e713c58b597865d983c1d3d0/9dd4e/right-02.png)

The anchor point of this sprite is in the bottom center.

  ![Fixed anchor point](https://www.codeandweb.com/static/3fd5cbc615a4a434092f92291d44ed9b/9dd4e/stand-right.png)

There's some space at the bottom of the sprite. The anchor point should be higher.

No problem you might say: Let's nudge the anchor point position up a bit. Maybe 5 px? No? 10? No? 9? That's it!

```js
this.sprite.anchor.set(128,256-9);
```

Ok. Things to remember: If you play the run animation you have to set the anchor to 128/256. If set the stand frame it's now 128/247. That's not good! There really must be a better solution...

## TexturePacker's anchor point editor

Go back to TexturePacker:

  ![Visual anchor point editor](https://www.codeandweb.com/static/c89fb6a70dc4f75b46b7acc24409f085/283d3/anchor-point-editor.png)

1.  Click **Sprite settings** in the toolbar to open the editor
2.  Select all sprites except for the _floor.png_
3.  Click in the center screen and select all anchor points by pressing `CTRL-A` or `CMD-A`. You can also use rubber band selection (press mouse button in the center screen and drag the mouse to get a selection rectangle).
4.  Use the right sidebar's **Predefined** combo box and set the anchor to **Bottom center**

So all sprites now have the bottom center as anchor. But we wanted to fix the stand left/right frames... let's do that:

  ![Visual anchor point editor](https://www.codeandweb.com/static/b93b10eed17ef6bdadf3c4ba0d1364ce/283d3/anchor-point-editor-2.png)

1.  Select the **stand** folder
2.  Select the anchor points in the middle
3.  Adjust the anchor point - your choice:
    *   by dragging the anchors with the mouse
    *   by pressing cursor `↑` 9 times
    *   by entering the coordinates

Finally, press publish to write the sprite sheet with the new pivot points.

TexturePacker also contains a realtime animation preview. Click **Anim preview** in the toolbar. The selected sprites are played as animation. The animation adapts to changes in the anchor points in real time.

![Visual anchor point editor with animation preview](https://www.codeandweb.com/static/pivotpoint_ani-0b233778266d7f1e1bfe1c57171301c6.gif)

Switch back to panda and reload the scene. `CMD-R` / `CTRL-R`. What's that? Betty is hovering? This is because you are now setting the anchor point twice.

**Remove** the following line from the code:

```js
this.sprite.anchor.set(128,256-9);
```

Ok — looks good.

Betty's animation is symmetrical, the sprite is lit from the top, not from the side. This allows you to remove the animation phases for left from the sprite sheet because you can simply flip the sprite in the code. This is not always an option — especially in games with strong lighting. E.g. if the character is drawn in a way that he's lit from the right side you can't flip him — this would look very strange — the shadow part would point into the direction of the light source...

But it's fine in our case: Delete the _left_ animation folder, _stand/left.png_ and _jump/left.png_. Go back into TexturePacker and press publish.

## Improving the code

### Creating a player class

In this final stage of the tutorial you'll stay in Panda. You've initialised the player animation in the main scene setup. This was ok for the first demo but would not be a good idea for a real game.

Let's create a separate player class to encapsulate all the player data. It'll also contain the sprite itself.

So you'll need the following methods:

*   `init()` — to initialize the class
*   `addTo(container)` — to add the sprite to the game scene
*   `setPosition(x,y)` — to set the initial position of the sprite
*   `run(direction)` — to move the sprite and start the running animation. `direction=1` is right, `direction=-1` is left
*   `stand()` — to make betty stand still, facing in the direction of the last movement

There'll also be one method to update the animations:

*   `changeAnimation(action)` — update animation, private

So this is the skeleton of the player class. Add this to the _main.js_ before the closing `});`

```js
game.createClass('Player', {
    init: function() {
    },

    addTo: function(container) {
    },

    setPosition: function(x,y) {
    },

    run: function(direction) {
    },

    stand: function() {
    },

    changeAnimation: function(action) {
    }
});
```

Let's fill in the methods, start with init:

```js
    init: function() {
        this.sprite = game.Animation.fromTextures('right/');
        this.sprite.anims.run = this.sprite;
        this.sprite.anims.stand = game.Animation.fromTextures('stand/right');

        this.direction = 1;
    },
```

This creates the animation as variable `sprite`. The next 2 lines `this.sprite.anims.run` and `this.sprite.anims.stand` add child animations. You can later use `this.sprite.play('run')` to play the run animation. You also set the starting direction to right (1).

You can't add the `Player` class directly to the scene because it's not derived from `Container`. The sprite is a member variable. The following method adds the sprite to a container:

```js
    addTo: function(container) {
        this.sprite.addTo(container);
    },
```

You also have to set the initial position of the player:

```js
    setPosition: function(x,y) {
        this.sprite.position.set(x,y);
    },
```

The `stand()` method is also simple. It calls changeAnimation with the stand animation:

```js
    stand: function() {
        this.changeAnimation('stand');
    },
```

`run(direction)` sets the direction and moves the player and updates the animation.

The new position is calculated using the following formula: `direction * speed * game.delta`.

*   `game.delta` is the time between the last game update and the current. This lets the player move with the same speed on all devices.
*   `speed` is the speed value, a constant
*   `direction` is 1 for right, -1 for left

```js
    run: function(direction) {
        this.direction = direction;
        var speed = 400;
        this.sprite.x += direction * speed * game.delta;
        this.changeAnimation('run');
    },
```

The final method is `changeAnimation`.

The first line `this.sprite.scale.x = this.direction;` flips the animation if `direction=-1`. The second line avoids restarting the animation if it's already playing.

```js
    changeAnimation: function(action) {
        this.sprite.scale.x = this.direction;
        if (this.sprite.currentAnim === this.sprite.anims[action]) return;
        this.sprite.play(action);
    }
```

### Add the player to the scene and add controls

Remove the test code from the previous chapter and update the `init()` method of the game scene:

```js
    init: function() {
        this.backgroundColor = '#cceeff';

        var groundY = this.makeFloor();

        this.player = new game.Player();
        this.player.addTo(this.stage);
        this.player.setPosition(game.width/2, groundY);
    },
```

The last thing missing are the controls. Add the following method to the scene:

```js
    update: function() {
        if (game.keyboard.down('LEFT')) {
            this.player.run(-1);
        }
        else if (game.keyboard.down('RIGHT')) {
            this.player.run(1);
        }
        else {
            this.player.stand();
        }
    },
```

Ok that's it:

## Structured data

```json
{
  "@context": "https://schema.org",
  "@type": "TechArticle",
  "headline": "Panda2 sprite sheet animations tutorial",
  "author": [
    {
      "@type": "Person",
      "name": "Andreas Löw"
    }
  ],
  "dateModified": "2023-09-13T00:00:00.000Z",
  "image": "/static/d921e925ff44a306613fc81fbb6e38ec/a6312/_opengraph-image.png"
}
```
