So I wrote my own Virtual Joystick Plugin for the HTML5 game engine Phaser. That may sound stupid now, but at that time @photostorm didn’t release his official plugin yet. Still, it’s a $20 value there.

So I needed a Virtual Joystick plugin (kinda) a few weeks ago. I found phaser-touch-control-plugin, but I want the joystick to stay in one place on the screen. So I wrote my own Virtual Joystick Plugin. That may sound stupid now, but at that time @photostorm didn’t release his official plugin yet.

Let’s start with a quick demonstration.


Design

  1. A background for the joystick (the grey box) to show the players where to put their finger.
    class Joystick extends Phaser.Sprite {
     constructor(x, y) {
         super(game, 0, 0, 'background');
         this.anchor.setTo(0.5, 0.5);
     }
    }
    
  2. A draggable sprite for the pin (the red box) which players can, duh, drag. Here, pin.input.enableDrag is called with first param true so that the pin is always centered at your finger tip.
    constructor(x, y) {
     ...
     let pin = game.add.sprite(0, 0, 'pin');
         pin.anchor.setTo(0.5, 0.5);
         pin.inputEnabled = true;
         pin.input.enableDrag(true);
     this.addChild(pin);
     this.pin = pin;
    }
    
  3. Event onDragStop: The pin returns to the center of the background.
    constructor(x, y) {
     ...
     pin.events.onDragStop.add(this.onDragStop, this);
     ...
    }
    onDragStop(){
     this.pin.position.setTo(0, 0);
    }
    
  4. A restriction maxDistance to prevent the pin from going outside of the background (like in the picture below). If the background is a circle, this is normally the radius, meaning half of the background image width. The check happens at event onDragUpdate.
    constructor(x, y) {
     ...
     this.maxDistance = this.width/2;
     pin.events.onDragUpdate.add(this.onDragUpdate, this);
     ...
    }
    onDragUpdate(){
     let {pin, maxDistance} = this;
     var distance = pin.getMagnitude();
     if (distance > maxDistance) {
         pin.setMagnitude(maxDistance);
     }
    }
    

There are two problems:

  1. Event onDragUpdate: if distance is larger than maxDistance, the pin position is changed and not at player finger anymore.
  2. In other game, players can start dragging from anywhere on the background of the joystick and the pin jumps at it immediately. Here, players have to actually start dragging at the pin position.

To solve this, I added a transparent layer to act as the draggable, and use the pin only for indication of position.

  1. Add a transparent dragger and move dragging events to dragger
    constructor(x, y) {
     ...
     let dragger = this.dragger = game.add.sprite(0, 0, null);
     dragger.anchor.setTo(0.5, 0.5);
     dragger.width = dragger.height = this.width;
     dragger.inputEnabled = true;
     dragger.input.enableDrag(true);
     this.addChild(dragger);
     dragger.events.onDragStop.add(this.onDragStop, this);
     dragger.events.onDragUpdate.add(this.onDragUpdate, this);
    }
    onDragStop(){
     this.dragger.position.setTo(0, 0);
     this.pin.position.setTo(0, 0);
    }
    
  2. The pin’s position then would be set based on the dragger’s position and maxDistance.
    onDragUpdate(){
     let {pin, dragger, maxDistance} = this;
     var distance = dragger.getMagnitude();
     pin.position.copyFrom(dragger);
     if (distance > maxDistance) {
         pin.setMagnitude(maxDistance);
     }
    }
    

Check out the full source code on this gist, and see this Virtual Joystick in action.

netcell

Mobile game Production lead for 2+ years and Software engineer for 8+ years. Producing cross-platform games, apps and wedsites. Dog lover.