const config = {
  type: Phaser.WEBGL,
  width: 800,
  height: 600,
  physics: {
    default: "arcade",
    arcade: {
      debug: true
    }
  },
  scene: {
    preload: preload,
    create: create,
    update: update
  },
  loader: {
    baseURL: "https://labs.phaser.io",
    crossOrigin: "anonymous"
  },
  plugins: {
    scene: [
      {
        key: "PhaserRaycaster",
        plugin: PhaserRaycaster,
        mapping: "raycasterPlugin"
      }
    ]
  }
};

new Phaser.Game(config);

var raycaster;
var ray;
var graphics;
var obstacles;
var intersections = [];
var targets;
var slices;
var boundary;

var RANGE = 150;

function preload() {
  this.load.image("grid", "assets/pics/debug-grid-1920x1920.png");
  this.load.image("crate", "assets/sprites/crate.png");
}

function create() {
  raycaster = this.raycasterPlugin.createRaycaster({ debug: true });

  ray = raycaster.createRay({
    origin: {
      x: 400,
      y: 300
    },
    collisionRange: RANGE,
    detectionRange: RANGE,
    rayRange: 2 * RANGE
  });
  ray.enablePhysics();

  console.log("raycaster", raycaster);
  console.log("ray", ray);

  var bg = this.add.image(0, 0, "grid").setOrigin(0, 0);

  boundary = this.add
    .polygon(
      ray.origin.x,
      ray.origin.y,
      new Phaser.Geom.Circle(RANGE, RANGE, RANGE).getPoints(12),
      0xffff00
    )
    .setVisible(false);
  raycaster.mapGameObjects(boundary);

  obstacles = this.add.group();
  createObstacles(this);
  raycaster.mapGameObjects(obstacles.getChildren());

  createTargets(this);

  intersections = ray.castCircle();

  graphics = this.add
    .graphics({
      fillStyle: { color: 0xffffff, alpha: 0.5 }
    })
    .setVisible(false);
  draw();

  this.cameras.main.setMask(graphics.createGeometryMask());

  this.input.on("pointermove", function (pointer) {
    ray.setOrigin(pointer.x, pointer.y);

    boundary.setPosition(pointer.x, pointer.y);
    boundary.data.get("raycasterMap").updateMap();

    intersections = ray.castCircle();
  });

  this.physics.add.overlap(ray, targets, function (rayCircle, target) {
    target.setTint(0xff0000);
  });

  this.events.on("preupdate", function () {
    for (let target of targets.getChildren()) {
      target.clearTint();
    }
  });
}

function update() {
  draw();
}

function createTargets(scene) {
  targets = scene.physics.add.group({
    defaultKey: "crate",
    bounceX: 1,
    bounceY: 1,
    collideWorldBounds: true,
    velocityX: 60,
    velocityY: 60
  });

  targets.create(400, 300);
  targets.create(750, 500);
  targets.create(450, 75);
}

function createObstacles(scene) {
  let obstacle = scene.add.rectangle(100, 100, 75, 75, 0xaaaaaa, 0.5);
  obstacles.add(obstacle, true);

  obstacle = scene.add.line(400, 100, 0, 0, 200, 50);
  obstacles.add(obstacle);

  obstacle = scene.add.circle(650, 100, 50, 0xaaaaaa, 0.5);
  obstacles.add(obstacle);

  obstacle = scene.add.polygon(
    650,
    500,
    [0, 0, 50, 50, 100, 0, 100, 75, 50, 100, 0, 50],
    0xaaaaaa,
    0.5
  );
  obstacles.add(obstacle);

  for (let i = 0; i < 5; i++) {
    obstacle = scene.add.rectangle(
      350 + 30 * i,
      550 - 30 * i,
      50,
      50,
      0xaaaaaa,
      0.5
    );
    obstacles.add(obstacle, true);
  }

  obstacle = scene.add.image(100, 500, "crate");
  obstacles.add(obstacle, true);
}

function draw() {
  graphics.clear();
  graphics.fillStyle(0xffffff);

  if (intersections.length > 0) {
    graphics.fillPoints(intersections);
  }
}
Run Pen

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://cdn.jsdelivr.net/npm/phaser@3.55.2/dist/phaser.js
  2. https://cdn.jsdelivr.net/npm/phaser-raycaster@0.10.1/dist/phaser-raycaster.js