Advanced Usage
Advanced Usage
Section titled “Advanced Usage”Learn advanced patterns and techniques for using Phaser Virtual Joystick in your games.
Multiple Joysticks
Section titled “Multiple Joysticks”You can create multiple joysticks for different purposes:
export class GameScene extends Phaser.Scene {  private movementJoystick!: VirtualJoystick;  private aimJoystick!: VirtualJoystick;
  create() {    // Movement joystick (left side)    this.movementJoystick = new VirtualJoystick({      scene: this,      bounds: {        topLeft: { x: 0, y: 0 },        bottomRight: { x: this.cameras.main.width / 2, y: this.cameras.main.height }      },      stick: {        fillColor: 0x4ECDC4,  // Teal        radius: 35      }    });
    // Aim joystick (right side)    this.aimJoystick = new VirtualJoystick({      scene: this,      bounds: {        topLeft: { x: this.cameras.main.width / 2, y: 0 },        bottomRight: { x: this.cameras.main.width, y: this.cameras.main.height }      },      stick: {        fillColor: 0xFF6B6B,  // Red for aiming        radius: 30      }    });
    this.add.existing(this.movementJoystick);    this.add.existing(this.aimJoystick);
    // Handle movement    this.movementJoystick.on('move', (data) => {      this.player.setVelocity(data.x * 200, data.y * 200);    });
    // Handle aiming    this.aimJoystick.on('move', (data) => {      this.aimWeapon(data.x, data.y);    });  }
  private aimWeapon(x: number, y: number) {    const angle = Math.atan2(y, x);    this.weapon.setRotation(angle);  }}Custom Styling Themes
Section titled “Custom Styling Themes”Create different visual themes for your joysticks:
Neon Theme
Section titled “Neon Theme”const neonJoystick = new VirtualJoystick({  scene: this,  deadZone: {    fillColor: 0x00FF00,    strokeColor: 0x00CC00,    alpha: 0.3  },  baseArea: {    fillColor: 0x00FF00,    alpha: 0.1,    strokeColor: 0x00CC00  },  stick: {    fillColor: 0x00FF00,    strokeColor: 0x00CC00,    alpha: 0.9  }});Dark Theme
Section titled “Dark Theme”const darkJoystick = new VirtualJoystick({  scene: this,  deadZone: {    fillColor: 0x333333,    strokeColor: 0x666666,    alpha: 0.2  },  baseArea: {    fillColor: 0x333333,    alpha: 0.2,    strokeColor: 0x666666  },  stick: {    fillColor: 0x666666,    strokeColor: 0x999999,    alpha: 0.8  }});Minimal Theme
Section titled “Minimal Theme”const minimalJoystick = new VirtualJoystick({  scene: this,  deadZone: { alpha: 0 },  baseArea: {    alpha: 0.05,    strokeWidth: 1,    strokeColor: 0xFFFFFF  },  stick: {    fillColor: 0xFFFFFF,    strokeWidth: 1,    alpha: 0.8  }});Joystick with Custom Icon
Section titled “Joystick with Custom Icon”Add custom icons to your joystick:
export class GameScene extends Phaser.Scene {  create() {    // Create custom icon    const stickIcon = this.add.text(0, 0, '🎮', {      fontSize: '24px',      color: '#ffffff'    });
    const joystick = new VirtualJoystick({      scene: this,      stickIcon: stickIcon,      stick: {        fillColor: 0x4ECDC4,        radius: 35      }    });
    this.add.existing(joystick);  }}Dynamic Joystick Positioning
Section titled “Dynamic Joystick Positioning”Position the joystick where the user first touches:
export class GameScene extends Phaser.Scene {  private joystick!: VirtualJoystick;  private isJoystickActive = false;
  create() {    // Create joystick with custom bounds    this.joystick = new VirtualJoystick({      scene: this,      bounds: {        topLeft: { x: 0, y: 0 },        bottomRight: { x: this.cameras.main.width, y: this.cameras.main.height }      }    });
    this.add.existing(this.joystick);
    // Handle touch events    this.input.on('pointerdown', (pointer) => {      if (!this.isJoystickActive) {        this.isJoystickActive = true;        // Joystick will appear at touch point      }    });
    this.joystick.on('release', () => {      this.isJoystickActive = false;    });  }}Smooth Movement with Acceleration
Section titled “Smooth Movement with Acceleration”Add acceleration and deceleration for smoother movement:
export class GameScene extends Phaser.Scene {  private joystick!: VirtualJoystick;  private player!: Phaser.Physics.Arcade.Sprite;  private targetVelocity = { x: 0, y: 0 };  private acceleration = 0.1;  private maxSpeed = 200;
  create() {    this.player = this.physics.add.sprite(400, 300, 'player');
    this.joystick = new VirtualJoystick({      scene: this    });
    this.add.existing(this.joystick);
    this.joystick.on('move', (data) => {      this.targetVelocity.x = data.x * this.maxSpeed;      this.targetVelocity.y = data.y * this.maxSpeed;    });
    this.joystick.on('release', () => {      this.targetVelocity.x = 0;      this.targetVelocity.y = 0;    });  }
  update() {    // Update joystick (required for smooth following behavior)    this.joystick?.update();
    // Smooth acceleration towards target velocity    const currentVel = this.player.body.velocity;
    this.player.setVelocity(      currentVel.x + (this.targetVelocity.x - currentVel.x) * this.acceleration,      currentVel.y + (this.targetVelocity.y - currentVel.y) * this.acceleration    );  }}Joystick with Visual Feedback
Section titled “Joystick with Visual Feedback”Add visual feedback based on joystick input:
export class GameScene extends Phaser.Scene {  private joystick!: VirtualJoystick;  private player!: Phaser.Physics.Arcade.Sprite;  private trail!: Phaser.GameObjects.Graphics;
  create() {    this.player = this.physics.add.sprite(400, 300, 'player');
    // Create trail effect    this.trail = this.add.graphics();
    this.joystick = new VirtualJoystick({      scene: this    });
    this.add.existing(this.joystick);
    this.joystick.on('move', (data) => {      // Move player      this.player.setVelocity(data.x * 200, data.y * 200);
      // Update trail      this.updateTrail(data);    });
    this.joystick.on('release', () => {      this.player.setVelocity(0, 0);      this.trail.clear();    });  }
  private updateTrail(data: { x: number, y: number }) {    this.trail.clear();
    // Draw trail based on joystick intensity    const intensity = Math.sqrt(data.x * data.x + data.y * data.y);    const alpha = intensity * 0.5;
    this.trail.fillStyle(0x00ff00, alpha);    this.trail.fillCircle(      this.player.x + data.x * 20,      this.player.y + data.y * 20,      5    );  }}Joystick with Haptic Feedback
Section titled “Joystick with Haptic Feedback”Add haptic feedback for better mobile experience:
export class GameScene extends Phaser.Scene {  private joystick!: VirtualJoystick;  private lastHapticTime = 0;
  create() {    this.joystick = new VirtualJoystick({      scene: this    });
    this.add.existing(this.joystick);
    this.joystick.on('press', () => {      this.triggerHaptic('light');    });
    this.joystick.on('move', (data) => {      const magnitude = Math.sqrt(data.x * data.x + data.y * data.y);
      // Trigger haptic feedback based on movement intensity      if (magnitude > 0.8 && Date.now() - this.lastHapticTime > 100) {        this.triggerHaptic('medium');        this.lastHapticTime = Date.now();      }    });
    this.joystick.on('release', () => {      this.triggerHaptic('light');    });  }
  private triggerHaptic(type: 'light' | 'medium' | 'heavy') {    if ('vibrate' in navigator) {      const patterns = {        light: [10],        medium: [20],        heavy: [30]      };
      navigator.vibrate(patterns[type]);    }  }}Desktop Testing
Section titled “Desktop Testing”Enable joystick on desktop for testing:
const joystick = new VirtualJoystick({  scene: this,  enableWithoutTouch: true  // Allows mouse interaction on desktop});Performance Optimization
Section titled “Performance Optimization”Optimize joystick performance for better frame rates:
export class GameScene extends Phaser.Scene {  private joystick!: VirtualJoystick;  private updateThrottle = 0;  private throttleRate = 16; // ~60fps
  create() {    this.joystick = new VirtualJoystick({      scene: this    });
    this.add.existing(this.joystick);
    // Throttle updates for better performance    this.joystick.on('move', (data) => {      this.updateThrottle++;      if (this.updateThrottle % 2 === 0) { // Update every other frame        this.handleMovement(data);      }    });  }
  update() {    // Always call update for smooth following behavior    this.joystick?.update();  }
  private handleMovement(data: { x: number, y: number }) {    // Your movement logic here    this.player.setVelocity(data.x * 200, data.y * 200);  }}Best Practices
Section titled “Best Practices”- Always call update(): Call joystick.update()in your scene’s update method
- Add to scene: Always call this.add.existing(joystick)after creating
- Use bounds: Define custom activation areas for better control
- Test on devices: Always test on actual mobile devices
- Consider haptics: Add haptic feedback for better UX
- Visual feedback: Use custom styling to show joystick state
- Multiple joysticks: Use separate joysticks for different actions
- Clean up: Destroy joysticks when changing scenes
- Custom icons: Add visual elements to make joysticks more intuitive
- Performance: Throttle updates if needed for better performance