Crafty Beyond the Basics: Sprites

In all the Crafty tutorials up to this point, you have been using square or rectangular boxes to understand different concepts like animation and keyboard or touch events. While this is a great way to quickly learn more about the library, you will most probably like to use images in your actual games.

Crafty Beyond the Basics: Sprites

In this tutorial, you will learn how to load sprite sheets to use different images in your game. After that, you will learn how to animate different characters using sprite animation.

Loading a Sprite Sheet

For those who are unfamiliar with sprite sheets, they are image files which contain several smaller graphics arranged together in a grid. Each of these smaller graphics or sprites can be used alone or along with other images to animate different objects.

You cannot just directly load a sprite sheet in Crafty and start using it. You will also have to tell it the width and height of different tiles or sprites in your sprite sheet. You can also give specific names to individual images. For example, a sprite with a small stone in it can be named  “small_stone” for easy reference. Remembering a tile name is a lot easier than remembering the actual coordinates of different images.

We are going to use the following sprite sheet by tokka in different demos of this tutorial.

Crafty Beyond the Basics: Sprites

It has a lot of images which can be used when the player is walking, jumping, standing idle, or getting shot. In this case, the width and height of most of the tiles are 80 px and 94 px respectively. Some sprite sheets might also have extra padding around individual tiles or the sprite sheet as a whole. These can be specified while loading the sprite sheet using the paddingXpaddingY, and paddingAroundBorder properties.

Here is the code we need to load the above sprite sheet in our game:

var game_assets = {
  "sprites": {
    "hero_spritesheet.png": {
      tile: 80,
      tileh: 94,
      map: {
        hero_idle: [0, 0],
        hero_walking: [1, 1],
        hero_jumping: [2, 3],
        hero_sitting: [0, 4]
      }
    }
  }
};

Crafty.load(game_assets);

The tiles are referenced by using a pair of numbers. The first number determines the column of our sprite, and the second number determines its row. The numbers are zero-based. For example, the sprite in the first column and first row can be accessed by using [0, 0]. Similarly, the sprite in the third column and fourth row can be accessed using [2, 3]. As mentioned before, you can give different names to individual sprites for ease of use.

Rendering Static Sprites

Once you have loaded a sprite sheet, you can render the images on screen by using the following code:

var idle_hero = Crafty.e("2D, Canvas, hero_idle")
  .attr({ x: 10, y: 10 });

The x and y attributes determine the location at which the sprites will be rendered. You can also set the width and height of individual sprites by using the w and h attributes.

Since the sprite has a 2D component, you can apply all the properties and attributes of the 2D component to these images. For example, you can flip our hero horizontally so that it faces the other direction by using this.flip("X"). Similarly, you can rotate a sprite by using the rotation attribute or make it transparent by using the alpha attribute.

var sitting_hero = Crafty.e("2D, Canvas, hero_sitting")
  .attr({ x: 440, y: 250 })
  .flip("X");

Let’s say you need to render only part of a given sprite on the canvas. You can do so with the help of the .crop(Number x, Number y, Number w, Number h) method. The first two parameters determine the x and y offset values of the sprite. The last two parameters determine the width and height of the cropped sprite. All the values should be specified in pixels.

Animating Sprites

Up to this point, you have been moving different entities around by changing their x and y attributes. It was not an issue as you were moving just rectangular boxes most of the time. However, moving our main character around by sliding it like this will look very unnatural. Adding animation to objects like coins that a player can collect will also make the game more enjoyable.

All the sprite-related animations require you to add the SpriteAnimation component to the entity. This component treats the different images in a sprite map as animation frames.

The information about an animation is stored in the reel object. The reel object has five different properties:

  • an id which is the name of the reel
  • a frames array that has a list of frames for the animation in the format [xpos, ypos]
  • currentFrame property which returns the index of the current frame
  • an easing property which determines how the animation should move forward
  • duration property which sets the animation duration in milliseconds

There are four different events that can be triggered by a sprite animation. These are:

  • StartAnimation: This event is triggered when the animation starts playing or resumes from paused state.
  • AnimationEnd: This event is triggered when an animation ends.
  • FrameChange: This event is triggered every time the frame in the current reel changes.
  • ReelChange: This event is triggered when the reel itself is changed.

All these events receive the reel object as a parameter.

Besides the reel object, there is also a .reel() method which is used to define sprite animations. You can use the .animate() method to play any of the defined animations.

The process of sprite animation begins by creating a static sprite on the canvas.

var walking_hero = Crafty.e('2D, Canvas, hero_walking, SpriteAnimation')
  .attr({
    x: 40,
    y: 20
  });

The hero_walking image in the above case is the first image the user sees before the animation. The attributes are used to specify the location of the current entity. Once the entity has been created, you can use the .reel() method to define the actual animation.

walking_hero.reel("walking", 1000, [
  [0, 1],
  [1, 1],
  [2, 1],
  [3, 1],
  [4, 1],
  [5, 1]
]);

This method accepts three parameters. The first parameter is the Id of the animation being created. The second parameter is used to set the length of animation in milliseconds, and the third parameter is an array of arrays containing the column (x) and row (y) position of successive frames. In this case, the array contains the position of all the sprites in the second row.

Another version of this method requires five parameters. The first two parameters are the reel Id and animation length. The third and fourth parameters are used to set the starting x and y position for the animation on the sprite map. The last parameter sets the number of sequential frames in the animation. When it’s set to a negative value, the animation will play backwards.

The above reel animation could also be created using the following code:

walking_hero.reel("walking", 1000, 0, 1, 6);

While this version is succinct, it is a bit limited. This method is only useful if all the sprites that you want to include in the animation are in a single row. Moreover, the animation will show these images in the order in which they appear in the sprite sheet.

After you have defined the animation, you can play it using the .animate( reel_Id [, loopCount]) method. The first parameter is the animation you want to play, and the second parameter determines the number of times you want to play this animation. The animations are played once by default. Setting loopCount to -1 will play the animation indefinitely.

In certain situations, you might want to play an animation just once based on an event. For example, a jump animation should be played when the user presses a button to make the player jump. You can try it out in the above demo. Pressing the Up Arrow key will make the second sprite jump. Here is the code to detect the key press:

jumping_hero.bind('KeyDown', function(evt) {
  if (evt.key == Crafty.keys.UP_ARROW) {
    jumping_hero.animate("jumping", 1);
  }
});

You can also pause and resume animations midway by using the .pauseAnimation() and .resumeAnimation() methods.

If a single sprite has multiple animations attached to it, you can determine if a specific animation is currently playing by using the .isPlaying([String reelId]) method. If no Id is provided, it will check if any animation is playing at all.

Conclusion

After completing this tutorial, you should be able to load your own sprite sheets in Crafty and use them to represent different game entities instead of various rectangles. You can now also apply different animations to an entity based on different events. I should remind you that I have used Crafty version 0.7.1 in this tutorial, and the demos might not work with other versions of the library.

JavaScript has become one of the de-facto languages of working on the web. It’s not without it’s learning curves, and there are plenty of frameworks and libraries to keep you busy, as well. If you’re looking for additional resources to study or to use in your work, check out what we have available in the СodeHolder marketplace.

In the next tutorial, you will learn how to add sounds in your game.