Deprecated - Drawings

WARNING The Legacy Drawing API is being deprecated in v0.25.0

A flag must be used after v0.24.5 to use the legacy drawing api, you cannot mix it with new Graphics API

ex.Flags.enable('use-legacy-drawing');
const game = new ex.Engine(...)

Sprites

To create a LegacyDrawing.Sprite you need to have a loaded LegacyDrawing.Texture resource. You can then use LegacyDrawing.Texture.asSprite to quickly create a LegacyDrawing.Sprite or you can create a new instance of Sprite using the constructor. This is useful if you want to "slice" out a portion of an image or if you want to change the dimensions.

const game = new ex.Engine()
const txPlayer = new ex.LegacyDrawing.Texture('/assets/tx/player.png')
// load assets
const loader = new ex.Loader([txPlayer])

// start game
game.start(loader).then(function () {
  // create a sprite (quick)
  const playerSprite = txPlayer.asSprite()
  // create a sprite (custom)
  const playerSprite = new ex.LegacyDrawing.Sprite(txPlayer, 0, 0, 80, 80)
})

You can then assign an Actor a sprite through Actor.addDrawing and Actor.setDrawing.

Sprite Sheets

You can also use a LegacyDrawing.SpriteFont which is special kind of LegacyDrawing.SpriteSheet for use with Labels.

Creating a SpriteSheet

To create a LegacyDrawing.SpriteSheet you need a loaded LegacyDrawing.Texture resource.

const game = new ex.Engine()
const txAnimPlayerIdle = new ex.LegacyDrawing.Texture(
  '/assets/tx/anim-player-idle.png'
)
// load assets
const loader = new ex.Loader([txAnimPlayerIdle])

// start game
game.start(loader).then(function () {
  const player = new ex.Actor()

  // create sprite sheet with 5 columns, 1 row, 80x80 frames
  const playerIdleSheet = new ex.LegacyDrawing.SpriteSheet(
    txAnimPlayerIdle,
    5,
    1,
    80,
    80
  )

  // create animation (125ms frame speed)
  const playerIdleAnimation = playerIdleSheet.getAnimationForAll(game, 125)

  // add drawing to player as "idle"
  player.addDrawing('idle', playerIdleAnimation)
  // add player to game
  game.add(player)
})

Creating animations

SpriteSheets provide a quick way to generate a new LegacyDrawing.Animation instance.

You can use all the frames of a texture using LegacyDrawing.SpriteSheet.getAnimationForAll or you can use a range of frames (LegacyDrawing.SpriteSheet.getAnimationBetween) or you can use specific frames (LegacyDrawing.SpriteSheet.getAnimationByIndices).

To create an animation these methods must be passed an instance of Engine. It's recommended to generate animations for an actor during initialization because the engine is passed to the initialization function. However, if your engine instance is in the global scope, you can create an animation at any time provided the texture has been loaded.

// create sprite sheet with 5 columns, 1 row, 80x80 frames
const playerIdleSheet = new ex.LegacyDrawing.SpriteSheet(
  txAnimPlayerIdle,
  5,
  1,
  80,
  80
)

// create animation for all frames (125ms frame speed)
const playerIdleAnimation = playerIdleSheet.getAnimationForAll(game, 125)
// create animation for a range of frames (2-4) (125ms frame speed)
const playerIdleAnimation = playerIdleSheet.getAnimationBetween(game, 1, 3, 125)
// create animation for specific frames 2, 4, 5 (125ms frame speed)
const playerIdleAnimation = playerIdleSheet.getAnimationByIndices(
  game,
  [1, 3, 4],
  125
)
// create a repeating animation (ping-pong)
const playerIdleAnimation = playerIdleSheet.getAnimationByIndices(
  game,
  [1, 3, 4, 3, 1],
  125
)

Multiple rows

Sheets are organized in "row major order" which means left-to-right, top-to-bottom. Indexes are zero-based, so while you might think to yourself the first column is column "1", to the engine it is column "0". You can easily calculate an index of a frame using this formula:

Given: col = 5, row = 3, columns = 10
index = col + row * columns
index = 4 + 2 * 10 // zero-based, subtract 1 from col & row
index = 24

You can also simply count the frames of the image visually starting from the top left and beginning with zero.

// get a sprite for column 3, row 6
const sprite = animation.getSprite(2 + 5 * 10)

Animations

Creating an animation

Create a LegacyDrawing.Texture that contains the frames of your animation. Once the texture is loaded, you can then generate an LegacyDrawing.Animation by creating a LegacyDrawing.SpriteSheet and using LegacyDrawing.SpriteSheet.getAnimationForAll.

const game = new ex.Engine()
const txAnimPlayerIdle = new ex.LegacyDrawing.Texture(
  '/assets/tx/anim-player-idle.png'
)
// load assets
const loader = new ex.Loader([txAnimPlayerIdle])
// start game
game.start(loader).then(function () {
  const player = new ex.Actor()

  // create sprite sheet with 5 columns, 1 row, 80x80 frames
  const playerIdleSheet = new ex.LegacyDrawing.SpriteSheet(
    txAnimPlayerIdle,
    5,
    1,
    80,
    80
  )

  // create animation (125ms frame speed)
  const playerIdleAnimation = playerIdleSheet.getAnimationForAll(game, 125)

  // add drawing to player as "idle"
  player.addDrawing('idle', playerIdleAnimation)
  // add player to game
  game.add(player)
})

Sprite Fonts

Generating the font sheet

You can use tools such as Bitmap Font Builder to generate a sprite sheet for you to load into Excalibur.

Creating a sprite font

Start with an image with a grid containing all the letters you want to support.

Once you load it into Excalibur using a LegacyDrawing.Texture resource, you can create a LegacyDrawing.SpriteFont using the constructor.

For example, here is a representation of a font sprite sheet for an uppercase alphabet with 4 columns and 7 rows:

ABCD
EFGH
IJKL
MNOP
QRST
UVWX
YZ

Each letter is 30x30 and after Z is a blank one to represent a space.

Then to create the LegacyDrawing.SpriteFont:

const game = new ex.Engine()
const txFont = new ex.LegacyDrawing.Texture('/assets/tx/font.png')
// load assets
const loader = new ex.Loader([txFont])

// start game
game.start(loader).then(function () {
  // create a font
  const font = new ex.LegacyDrawing.SpriteFont(
    txFont,
    'ABCDEFGHIJKLMNOPQRSTUVWXYZ ',
    true,
    4,
    7,
    30,
    30
  )
  // create a label using this font
  const label = new ex.Label('Hello World', 0, 0, null, font)
  // display in-game
  game.add(label)
})

If you want to use a lowercase representation in the font, you can pass false for caseInsensitive and the matching will be case-sensitive. In our example, you would need another 7 rows of lowercase characters.

Font colors

When using sprite fonts with a Label, you can set the Label.color property to use different colors.

Known Issues

One font per Label
Issue #172

If you intend on changing colors or applying opacity effects, you have to use a new SpriteFont instance per Label.

Using opacity removes other effects
Issue #148

If you apply any custom effects to the sprites in a SpriteFont, including trying to use Label.color, they will be removed when modifying Label.opacity.