Fork me on GitHub

Bitmap Animation demo with EaselJS and Box2dWeb

Building on the EaselBox I've been cobbling together to integrate EaselJS Box2dWeb, here is an example using some sprite sheets to build animations. It's a universe with an Earth and some stars, with the gravity of the objects pulling them together.

The code below demonstrates how to add the Bitmap animations, as well as calculate and apply the gravities between the objects. Also check out the blog post.

(Rocket ship image courtesy of: the guy at bittyjava.wordpress.com)

To add more stars, click on the canvas!

Drawing with EaselJS

The debug drawing directly from Box2dWeb

The sprite sheet for the earth is courtesy of this guy. sprite sheet is a 4 x 4 grid of the steps of the animation series of the earth spinning. The first step starts in the upper left-hand corner and goes from left to right. It then jumps down to row two, and goes from left to right.

In this case, there are only 13 frames to the animation--we won't use the full 4x4 grid--but that's OK.

To add the earth image to our project, all we need to do is:

@world.addEntity( 
  radiusPixels: 20,  # model the Earth as a circle with radius 20 pixels
  # note: we'll have to scale down the image to 25% of its original size
  scaleX: 0.25,  
  scaleY: 0.25,
  imgSrc: 'img/Earth.png',
  # each frame of the animation is 213 x 160.  There are 13 frames.
  # regX and regY indicate which point in the frame (usually the center)
  # to rotate around.
  frames: {width:213, height:160, count: 13, regX:106, regY:82}, 
  # starting position and rotational speed
  xPixels: canvas.width * 2 / 3,
  yPixels: canvas.height * 2 / 3,
  angleRadians: 45,
  angularVelRadians: 2)

We'll do something similar for the sprite sheet for the stars, seen here on the right. The sprite sheet comes from the EaselJS sparkles demo. consists of 13 steps (13 again--coincidence!) in the series of the sparkle animation. You can find the code for adding this sheet below.

Here is the full source for this demo, using my EaselBox framework. It is written in Coffeescript.

class window.GravityDemo
  # to set up Easel-Box2d world
  pixelsPerMeter = 30
  gravityX = 0
  gravityY = 0
  # game-specific
  frameRate = 20
  gravitationalConstant = 1.5
  
  constructor: (canvas, debugCanvas, statsCanvas) ->    
    # set up the EaselJS-Box2dWeb world
    @world = new EaselBoxWorld(this, frameRate, canvas, debugCanvas, gravityX, gravityY, pixelsPerMeter)
    
    @world.addImage("/img/space.jpg")  # background image
    
    # Add the earth entity--model as a circle of radius 20 pixels
    @world.addEntity( 
      radiusPixels: 20,
      scaleX: 0.25,
      scaleY: 0.25,
      imgSrc: 'img/Earth.png',
      # each frame of the animation is 213 x 160.  There are 13 frames.
      # regX and regY indicate which point in the frame (usually the center)
      # to rotate around.
      frames: {width:213, height:160, count: 13, regX:106, regY:82},
      xPixels: canvas.width * 2 / 3,
      yPixels: canvas.height * 2 / 3,
      angleRadians: 45,
      angularVelRadians: 2)    
      
    # let's go through the canvas and randomly sprinkle some stars in a 4x3 grid
    for i in [0..4]
      for j in [0..3]
        # add some random x- and y-offsets, and x- and y-velocities
        x =  -40 + Math.floor(Math.random()*80) + canvas.width * i / 4
        y = -40 + Math.floor(Math.random()*80) + canvas.height * j / 3
        xVel = -10 + Math.floor(Math.random()*20)
        yVel = -10 + Math.floor(Math.random()*20)
        this.addSparkle x, y, xVel, yVel
        
    # set up frame rate display
    # uses mr.doob's stats.js:  https://github.com/mrdoob/stats.js#readme
    # (we love mr.doob!)
    @stats = new Stats()
    statsCanvas.appendChild @stats.domElement
    
    # when you click on the canvas, we'll add more stars
    canvas.onclick = (event) =>
      this.addSparkle(event.offsetX, event.offsetY, 0, 0)
      
  # adding the stars--animated bitmap
  addSparkle: (spX, spY, xVel, yVel) ->
    @world.addEntity(                     
      radiusPixels: 4,
      imgSrc: '/img/sparkle_21x23.png', 
      frames: {width:21, height:23, regX:10, regY:11},
      startFrame: Math.random() * 13, 
      xPixels: spX,
      yPixels: spY,
      xVelPixels: xVel,
      yVelPixels: yVel)     
      
  # this is the callback that gets called each step in the Box2d physics
  # simuation.  Do whatever general game logic stuff you want to do here.
  # we'll use this opportunity re-calculte the gravities between objects,
  # and then apply them to all the bodies in the simulation
  tick: () ->
    @stats.update()
    # loop through all objects
    for object1 in @world.objects
      # calculate the gravity of the other object on this object
      for object2 in @world.objects
        applyGravities object1, object2 unless object1 == object2
        
  # some low-level Box2d action here
  applyGravities = (obj1, obj2) ->
    # From high school physics: Gravity =
    #   GRAVITATIONAL_CONTSTANT * mass of object1 * mass of object 2 /  (distance between objects) ^ 2
    pos1 = obj1.body.GetWorldCenter()
    pos2 = obj2.body.GetWorldCenter()
    diffVec = pos2.Copy()
    diffVec.Subtract(pos1) # distance between the two bodies
    distSq = diffVec.LengthSquared()  #distance squared
    
    # calculate force of gravity
    forceMagnitude = gravitationalConstant * obj1.body.GetMass() * obj2.body.GetMass() / distSq
    
    diffVec.Normalize() # normalized (unit=1) vector showing diretion betwen objects
    diffVec.Multiply(forceMagnitude) # create the vector of gravity of correct magnitude
    
    # apply this gravity as the force on the objectd
    obj1.body.ApplyForce(diffVec, pos1)