Adventures in Phaser with TypeScript– Physics using Arcade Physics

 

 

I’ve already cheated a few times using physics with the caveat that “I will cover this later”.  Well, welcome to later!  One thing you should be aware of right away is that there are a number of different physics engines available in Phaser.  In this particular tutorial we are going to cover Arcade physics, but there is also P2, Ninja and coming soon as a premium plugin, Box2D.

 

You may be thinking to yourself… huh?  Why the hell are there 3+ physics engines?   Let me try to break it down as best as I understand it.  First thing to know is, there is zero overhead from all of these systems, you need to enable them before you can use them, so you only pay the costs of what you need.

 

The Physics systems in Phaser are:

  • Arcade is a light weight engine, not the most accurate or full featured, but fast.  It’s also historically what was in Phaser all along.
  • P2 is an existing Javascript physics library.  It’s far more featured than Arcade, but also a lot slower as there is a ton more calculations going on behind the scenes.
  • Ninja… well Ninja I don’t entirely get the purpose behind.  As best I understand it, the author of Phaser, Richard Davey, wrote it a while ago and ported it from Flash to Phaser.  I think it falls somewhere in between Arcade and P2 in the feature/performance scale.  It’s stripped down in functionality from P2.
  • Box2D, well first off, it’s not available yet.  It is about as close to industry standard as 2D physics systems get though.

 

You can also use more than one physics system at a time, but they wont interact.  So if you have something either with tons of them in the scene ( say bullets or particles ) or where you need only basic physics, you can use Arcade.  Then if you require more accuracy, use P2.

 

This particle tutorial is going to look at Arcade Physics.  Let’s start with a very simple example.  We are going to enable physics, turn on gravity and enabled a sprite to be a physics body.

/// <reference path="phaser.d.ts"/>  class SimpleGame {      game: Phaser.Game;      player: Phaser.Sprite;        constructor() {          this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {              create: this.create, preload:this.preload, render: this.render          });      }      preload() {          this.game.load.image("decepticon", "decepticon.png");      }      render() {          // This renders debug information about physics bodies          this.game.debug.body(this.player);      }      create() {          this.player = this.game.add.sprite(this.game.width / 2, 0, "decepticon");            // Start the ARCADE Physics system          this.game.physics.startSystem(Phaser.Physics.ARCADE);                    // Enable physics on the player sprite          this.game.physics.enable(this.player, Phaser.Physics.ARCADE);            // Set the sprite to collide with the worlds edge          this.player.body.collideWorldBounds = true;          // And set bounce in the Y axis ( called restitution in most physics system ) to 1,           // which will make it bounce equal to 100 %          this.player.body.bounce.y = 1;            // Set the physics engines overall gravity.  98 == 98 pixels per second in this demo          this.game.physics.arcade.gravity.y = 98;      }  }    window.onload = () => {      var game = new SimpleGame();  };

 

Run it:

 

 

This example is pretty heavily commented, so I wont go into much detail.  It illustrates a few key concepts.  First, you need to start the physics subsystem before you can use it.  Second, you need to initialize a sprite to be a physics object.  Finally, you can render debug information using game.debug.body(), which will draw the bounding box as seen by the physics engine.  This can be incredibly useful for debugging physics simulation problems.

 

Now one of the very first things people are going to want to use a physics engine for is collision detection, so lets do that next!

/// <reference path="phaser.d.ts"/>  class SimpleGame {      game: Phaser.Game;      player1: Phaser.Sprite;      player2: Phaser.Sprite;        constructor() {          this.game = new Phaser.Game(640, 480, Phaser.AUTO, 'content', {              create: this.create, preload: this.preload,              render: this.render, update: this.update          });      }      preload() {          this.game.load.image("decepticon", "decepticon.png");      }      update() {          // Now check for a collision between objects          this.game.physics.arcade.collide(this.player1, this.player2);      }      render() {            }      create() {          this.player1 = this.game.add.sprite(this.game.width, this.game.height / 2 - 50, "decepticon");          this.player2 = this.game.add.sprite(0, this.game.height / 2 - 50, "decepticon");            this.game.physics.startSystem(Phaser.Physics.ARCADE);            // You can enable multiple bodies at once by passing in an array like so:          this.game.physics.arcade.enable([this.player1, this.player2]);          this.player1.body.collideWorldBounds = true;          this.player2.body.collideWorldBounds = true;            // Set the players to bounce along x axis instead          this.player1.body.bounce.x = 1;          this.player2.body.bounce.x = 1;            // Now set them moving.  Move to the center of the screen accelerating to 100pixels per second          this.game.physics.arcade.accelerateToXY(this.player1, this.game.width / 2,                                                  this.game.height / 2 - 50, 100);          this.game.physics.arcade.accelerateToXY(this.player2, this.game.width / 2,                                                  this.game.height / 2 - 50, 100);                    this.game.physics.arcade.gravity.y = 0;      }  }    window.onload = () => {      var game = new SimpleGame();  };

And the results:

 

This example shows us a couple things.  Most important is for collisions to occur you need to test for them using collide().  You can also see from this example that it’s possible to initialize multiple physics bodies at once by passing all the sprites in to game.physics.arcade.enable as an array.  This demo also illustrates that you can easily turn gravity off in a simulation.

 

In this particular demo we used AccelerateToXY to get our bodies moving.  There are a number of other functions for moving physics bodies, checking for intersections, measuring distances and angles available in the Physics.Arcade class, they work fairly straight forward so I leave exploring them as an exercise for the reader.  There are a few final concepts I want to touch on before moving on.

 

The first is collision handling.  The above example simply tested if a collision happened.  Phaser Arcade physics however give you a great deal more control than that, as will now see.  When you call collide() you have the option of passing in a pair of callbacks, like so:

    update() {          // Now check for a collision between objects          this.game.physics.arcade.collide(this.player1, this.player2,              // This callback is called when a collision occurs              // In this example we scale object1 on collision until its 2x bigger in size              (object1: any, object2: any) => {                  // object1 and object2 will be either of type Sprite, Group, Tilemap or Particle Emitter                  if (object1 instanceof (Phaser.Sprite)) {                      (<Phaser.Sprite>object1).scale.multiply(1.1, 1.1);                      if ((<Phaser.Sprite>object1).scale.x > 2) {                          (<Phaser.Sprite>object1).scale.set(1, 1);                      }                  }              },              // This callback is called for additional testing, AKA user driven logic on if a collision occurs              // If you return true, a collision occured, if you returned false, a collision doesn't occur              // In this example, object checks the direction in the X axis it's moving, if moving right to left              // then a collision wont occur.  As a result, the first collision test               //both objects will pass through each other.              (object1: any, object2: any) => {                  if (object1 instanceof (Phaser.Sprite)) {                      if ((<Phaser.Sprite>object1).deltaX < 0)                          return false;                      else                          return true;                      }              },              this);      }

Which when run results in:

The code comments explain most of what happens.  Basically you have a pair of callbacks available.  The first one is the action to perform IF a collision occurs, in addition to any action the physics engine is going to take that is.  In this simple example, we scale one of the two colliding objects up until it’s size has doubled.  As you can see, the colliding and collided objects are passed in to this function.  The type of this object can be a Sprite, Group, Particle Emitter or Tilemap.  We actually used Arcade Physics in earlier tilemap and particle tutorials if you want more details on collisions between these types. 

 

The second callback determines if a collision occurs at all.  Returning false out of that function means that no collision occurred, while true indicates one did.  If you run this example ( click here to open it in a new browser as it’s probably already run too far ), you will notice that on the first pass, no collision occurs.

 

The final thing I want to touch on is grouping.  As mentioned above, in the collision callbacks, one of the types of collisions that can occur is between groups.  I am not going to go into detail as I already covered grouping earlier, but I wanted you to be aware that you can group sprites together then perform collisions that way.  This is handy when you have a large number of similar sprites, such as bullets, because frankly doing a collide() call for each possible combination get’s old quickly!

 

In the future I will look at some of the other physics systems, but for quick and dirty collisions and simple physics, Arcade Physics should be enough.

 

Programming


Scroll to Top