Placeholder image
Use a spritesheet with Pixi.js
  posted 07 Dec, 2016
written by evansbsr

introduction

In the previous tutorial we saw how to create a spritesheet using shoebox. In this tutorial we will see how to load and use this spritesheet within the Pixi.js rendering engine. We will then create the basis for a simple tic-tac-toe style game.

Pixi.js is a 2D rendering engine written in Javascript. You can download it from pixijs.com. For best results run this tutorial on your local web server, e.g: XAMPP

download assets for tutorial

source assets

view online demo

demo

step 1 : document setup

Create a new HTML file and save it. You can create a HTML file using any text editor e.g. Notepad. For coding, a code editor such as Sublime Text 3 is recommended. 

Begin by typing the code below in your preferred code editor. Make sure to change the src attribute of the script tag to the location of the pixi.js file you downloaded.

<html> 
  <body>  
  <script type="text/javascript" src="../demo_assets/js/pixi.js"></script>
  </body>
</html>

Next add the following code below the first script tag:

<script type="text/javascript"> 
  var rendererWidth = rendererHeight = 450;
  var renderer = PIXI.autoDetectRenderer(rendererWidth, rendererHeight);

  document.body.appendChild(renderer.view);
</script>

In the first line of the code above we create two variables, rendererWidth and rendererHeight. These will be used to set the width and height of the canvas element that is created. In the next line we create a renderer object and save a reference to it in the renderer variable.

Next we append the renderer object to the page. If you open the HTML file in a browser at this point you should see a black box that has the dimensions set in the rendererWidth and rendererHeight variables. In our case we set both to 450 which gives us a black box that is 450px wide by 450px tall.

 

step 2: use pixi loader to load spritesheet image

Next we'll use pixi's inbuilt loader to load in the spritesheet image. In this example I'm using the assets created in the previous tutorial. These assets are 'sprites.png' and 'sprites.json' which you can get by downloading the source assets linked to above. Place both files in the same folder as your HTML file and add the code below to your script tag

window.onload = function() {
  PIXI.loader
    .add("spritesheets","sprites.json")
    .load(function (loader, resources) {
       initialiseScene();
  });
}

The function in the code above is called when the browser has finished loading the page. In the function we create an instance of pixi's inbuilt resource loader. We then pass in the name of our spritesheet and the path to the sprites.json file using the add function.

Next we add a callback function that is called when the loader has finished loading the assets. We then call another function initialiseScene() which we will define next.

step 3: define initialiseScene function

Next we define the initialiseScene function which will be called when the assets have finished loading:

function initialiseScene() {
  createObjects();
  animate();
}

In this function we call two other functions to initialise our scene: createObjects() and animate().

 

step 4: create pixi display objects

Next we create the display objects that pixi will use to render graphics to the screen. Start by adding the following two variables to your script

var stage = new PIXI.Container(); 
var background;

The stage variable is a container that we will use to hold the display objects that we create. The background variable will be used in the createObjects() function. Next add this code:

function createObjects() {
  background = new PIXI.Sprite.fromFrame("bg.png");
  stage.addChild(background); 
}

In the createObjects() function we first create a pixi sprite instance and store a reference to it in the background variable. As we define the sprite we call its fromImage() function and pass in the string 'bg.png', which is one of the images in the spritesheet we loaded in earlier.This will create a sprite from that image. The next line adds the background sprite object to the stage. The stage object will be used in the animate() function to render objects to the screen.

 

step 5: define animate() function

Next we define the animate() function to render objects to the screen

function animate(timestamp) {   
  requestAnimationFrame(animate);  
  renderer.render(stage); 
}

requestAnimationFrame() is a special function that the browser calls every frame. We pass to it the animate function which we want to call every new update frame. Next we call the renderer's render function and tell it to draw all of the stage container's child objects to the screen. If you've followed the tutorial using the assets provided you should have something that looks like this:

class=img

step 6: add more sprites

Next we add some more sprites from the spritesheet and add them to the stage object. The objective of this tutorial is to create the basis for a tic-tac-toe game. Tic-tac-toe is a game that consists of a 3x3 grid of cells where players can insert either an X or an O. In our spritesheet we have three images corresponding to these three states:

class=img

In order ro create our 3x3 grid of cells we need to create nine separate sprite objects. Add the following code to create a container to hold the grid sprites.

var squareContainer = new PIXI.Container(); 
squareContainer.x = squareContainer.y = 76; 

Next add the following line of code to the createObjects() function below the line of code where you added the background sprite to the stage

stage.addChild(squareContainer);

The squareContainer is what we will use to hold the sprites of our 3x3 grid. We make it a child of the stage object so that it will be drawn to the screen. We then set its x and y positions to 76. This means that the squareContainer object is positioned 76 pixels from the top and 76 pixels from the left of the canvas.

Each cell of the grid is capable of being in one of three states: X, O or blank. Let's go ahead and create a Javascript object that we will use to hold all the three states.

function Square(_x, _y) { 
  this.sprite = new PIXI.Sprite.fromFrame("X.png");
  squareContainer.addChild(this.sprite); 
}

The Square function above creates an object that we will reuse to make multiple sprites. The two parameters _x and _y will be used to set the position of the sprite. In the first line we create a new pixi sprite and call its fromFrame function to create a new sprite from the image 'X.png'. This image is taken from the spritesheet we loaded in earlier. Next we add the newly created sprite to the squareContainer.

We then add the following code to set the Square function to set the position of the Square objects that we will create

this.sprite.position = { x: this.sprite.width  * _x, 
                         y: this.sprite.height * _y };  

The _x and _y  variables will be supplied when the Square object is created. Add the following code to the createObjects() function to create the Square objects.

for(var i = 0;i < 3;i++) {
  for(var j = 0;j < 3;j++) {
    square = new Square(i, j);
  }
}

What we have in the code above is a double for() loop.Both for() loops increment a variable (i and j) from 0 - 2. The i and j variables are then passed to the newly created Square variable where they will act as _x and _y.

If you load the page we are working on in your browser you should see something like this:

class=img

 

Our Square object sprites have been added in a 3x3 grid. Let's add some spacing between the sprites to make them more distingushable. Add the following code to the Square function.

var spacing = 14; //the distance between individual sprites
this.sprite.position = { x: this.sprite.width  * _x + (spacing * _x), 
                         y: this.sprite.height * _y + (spacing * _y)};     

In the code above we create a variable named spacing that we use to specify the amount in pixels that we want to have between each sprite. We then multiply that spacing variable by the _x and _y values which gives us the amount of space we need horizontally and vertically between sprites. Upon adding this code the result looks something like this

class=img

step 7: make sprites interactive

Add the following code to the Square function to make the sprite objects clickable. 

this.sprite.interactive = true;
this.sprite.buttonMode = true;

Setting the sprite's interactive property to true will allow you to make the sprite respond to input events such as mouse clicks. The next line sets the sprite's buttonMode property to true. This sets the mouse cursor to a pointer when you hover on the sprite letting the user know that the sprite is clickable.

 

step 8: change sprite image on click

Let's add some code so that we see something happen when the user clicks on a sprite. The sprite will occupy one of three states. Initially the sprite should be blank and upon clicking the sprite should turn to an X or an O. Change the following line in the Square function to set the sprite's initial frame to 'blank.png'.

this.sprite = new PIXI.Sprite.fromFrame("blank.png");

Add a boolean variable at the top of the script to determine whether the sprite should show an X or an O upon clicking it.

var showX = true;

We set the showX variable to true as it is customary in a game of tic-tac-toe to have the first player insert an X.

Next we add the following code to change the sprite image when the user clicks on one of the sprites. 

var self = this;          
this.clicked = false;

// add an event listener to the mouse click event. 
this.sprite.click = function() { 
  if(this.clicked) {
    return;
  } else {
    this.clicked = true;
  }
  showX = !showX; 
  var frame = showX ? "X.png" : "O.png";
  this.setFrame(frame);
}

The first line sets the _self variable to this. This is because of the concept of scope in Javascript. _self is what we use to hold a reference to the current Square object we are working with. This allows us to reference the Square object from within the click function that we are about to define. The next line creates a boolean variable clicked which we use to check whether the sprite has already been clicked or not. If it has already been clicked the function will quit at that point. If it has not already been clicked then the Square object's clicked variable will be set to true and the function will continue.

The next line alternates the showX variable between true and false. This means that if the showX variable is true it will become false and if it is false it will become true. 

We then create a frame variable that will hold the name of the image that we are changing the sprite to. This line below is what is called a ternary operator. It sets the value of frame to either 'X' or 'O'  depending on the value of showX. If showX is true then frame will be set to 'X' and if it is false then frame will be set to 'O'.

var frame = showX ? "X.png" : "O.png";

'X.png' and 'O.png' are the names of two of the images in our spritesheet.

Next we call the Square object's setFrame function and pass to it the name of the image that we would like the sprite to change to. Let us now define the setFrame function by adding the code below to the Square function.

this.sprite.setFrame = function(frame) { 
  this.texture = PIXI.utils.TextureCache{{ frame }};
}

Pixi stores the images that we loaded from our spritesheet into a texture cache. The code above sets the sprite's texture property to the frame image which it gets from the texture cache. 

step 9: add css to HTML file

Next let's add some CSS to the <head> tag of our file. This css helps to improve the appearance of our project.

<style>
  body {
    background-color: rgb(238, 231, 238);
  }
  html, body {
    margin: 0;
    padding: 0;
  }
  canvas {
    width: auto;
    max-width: 100%;
    max-width: 100vw;
    height: auto;
    max-height: 100%;
    max-height: 100vh;
    margin: auto;
    position: relative;
    top: 50%;
    left: 50%;
    transform: translateY(-50%) translateX(-50%);
    background-color: #ffc77d;  
  }
</style>

conclusion

Finally we have something that resembles a game of tic-tac-toe.

 

This tutorial will end here as it is only intended to help you learn how to work with spritesheets in pixi.js. You may choose to continue further and add various features such as:

  • determining whether X or O has won the game
  • resetting the game tiles to blank tiles upon completion of a round
  • adding a score table to show how many games have been won by X or O
  • adding a timer specifying how long a player has to make their next move. This adds some tension to the game which would make it more engaging for players

download completed project files

project files

The following is the code we have written for this tutorial:  

<html>
  <style>
    body {
      background-color: rgb(238, 231, 238);
    }
    html, body {
      margin: 0;
      padding: 0;
    }
    canvas {
      width: auto;
      max-width: 100%;
      max-width: 100vw;
      height: auto;
      max-height: 100%;
      max-height: 100vh;
      margin: auto;
      position: relative;
      top: 50%;
      left: 50%;
      transform: translateY(-50%) translateX(-50%);
      background-color: #ffc77d;  
    }
  </style>
  <body>  
  <script type="text/javascript" src="../demo_assets/js/pixi.js"></script>
  <script type="text/javascript"> 
    var rendererWidth = rendererHeight = 450;
    var renderer = PIXI.autoDetectRenderer(rendererWidth, rendererHeight);

    document.body.appendChild(renderer.view);

    var background;
    var stage = new PIXI.Container();
    var squareContainer = new PIXI.Container(); 

    var showX = false;
    
    squareContainer.x = squareContainer.y = 76; 

    // create a PIXI loader when the page has finished loading
    window.onload = function() {
    PIXI.loader 
      .add("spritesheet", "../demo_assets/img/tutorial-3/sprites.json")  
      .load(function (loader, resources) {
          initialiseScene();
      });
    }
    
    // initialiseScene is called after the spritesheet image has finished loading
    function initialiseScene() { 
      createObjects();
      animate();
    } 

    // create the display objects that we use to draw objects to the screen
    function createObjects() {
      background = new PIXI.Sprite.fromFrame("bg.png"); 
      stage.addChild(background); 
      stage.addChild(squareContainer);

      for(var i = 0;i < 3;i++) {
        for(var j = 0;j < 3;j++) {
          square = new Square(i, j);
        }
      }
    } 

    function Square(_x, _y) { 
      this.sprite = new PIXI.Sprite.fromFrame("blank.png");
      squareContainer.addChild(this.sprite); 
      this.sprite.interactive = true;
      this.sprite.buttonMode = true;

      var spacing = 14; //the distance between individual sprites
      this.sprite.position = { x: this.sprite.width  * _x + spacing * _x, 
                               y: this.sprite.height * _y + spacing * _y };     

      var self = this;          
      this.clicked = false;

      // add an event listener to the mouse click event. 
      this.sprite.click = function() { 
        if(this.clicked) {
          return;
        } else {
          this.clicked = true;
        }
        showX = !showX; 
        var frame = showX ? "X.png" : "O.png";
        this.setFrame(frame);
      } 

      this.sprite.setFrame = function(frame) { 
        this.texture = PIXI.utils.TextureCache{{ frame }};
      }
    }

    // render the scene 
    function animate(timestamp) { 
      requestAnimationFrame(animate);  
      renderer.render(stage); 
    } 
  </script>
  </body>
</html>