body { margin: 0; background: black; color: silver; }
const fragShader = `
#define SHADER_NAME BLUR_FS

precision mediump float;

//"in" attributes from our vertex shader
varying vec2 outTexCoord;

//declare uniforms
uniform sampler2D uMainSampler;
uniform float uResolution;
uniform float radius;
uniform vec2 dir;

void main()
{
    //this will be our RGBA sum
    vec4 sum = vec4(0.0);

    //our original texcoord for this fragment
    vec2 tc = outTexCoord;

    //the amount to blur, i.e. how far off center to sample from
    //1.0 -> blur by one pixel
    //2.0 -> blur by two pixels, etc.
    float blur = radius/uResolution;

    //the direction of our blur
    //(1.0, 0.0) -> x-axis blur
    //(0.0, 1.0) -> y-axis blur
    float hstep = dir.x;
    float vstep = dir.y;

    //apply blurring, using a 9-tap filter with predefined gaussian weights",

    sum += texture2D(uMainSampler, vec2(tc.x - 4.0*blur*hstep, tc.y - 4.0*blur*vstep)) * 0.0162162162;
    sum += texture2D(uMainSampler, vec2(tc.x - 3.0*blur*hstep, tc.y - 3.0*blur*vstep)) * 0.0540540541;
    sum += texture2D(uMainSampler, vec2(tc.x - 2.0*blur*hstep, tc.y - 2.0*blur*vstep)) * 0.1216216216;
    sum += texture2D(uMainSampler, vec2(tc.x - 1.0*blur*hstep, tc.y - 1.0*blur*vstep)) * 0.1945945946;
    
    vec4 tex = texture2D(uMainSampler, vec2(tc.x, tc.y));

    sum += tex * 0.2270270270;

    sum += texture2D(uMainSampler, vec2(tc.x + 1.0*blur*hstep, tc.y + 1.0*blur*vstep)) * 0.1945945946;
    sum += texture2D(uMainSampler, vec2(tc.x + 2.0*blur*hstep, tc.y + 2.0*blur*vstep)) * 0.1216216216;
    sum += texture2D(uMainSampler, vec2(tc.x + 3.0*blur*hstep, tc.y + 3.0*blur*vstep)) * 0.0540540541;
    sum += texture2D(uMainSampler, vec2(tc.x + 4.0*blur*hstep, tc.y + 4.0*blur*vstep)) * 0.0162162162;

    gl_FragColor = sum;
}
`;

class BlurPostFX extends Phaser.Renderer.WebGL.Pipelines.PostFXPipeline {
  constructor(game) {
    super({
      game,
      renderTarget: true,
      fragShader,
      uniforms: [
        "uProjectionMatrix",
        "uMainSampler",
        "uResolution",
        "radius",
        "dir"
      ]
    });
  }

  onPreRender() {
    this.set1f("uResolution", this.renderer.width);
    this.set1f("radius", 2);
    this.set2f("dir", 1.0, 1.0);
  }
}

const config = {
  type: Phaser.WEBGL,
  width: 800,
  height: 600,
  pipeline: { BlurPostFX },
  physics: {
    default: "arcade",
    arcade: {
      debug: false
    }
  },
  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 rt;

var RANGE = 1000;

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: false });

  ray = raycaster.createRay({
    autoSlice: true,
    origin: { x: 400, y: 300 },
    rayRange: RANGE
  });

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

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

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

  createTargets(this);

  intersections = ray.castCircle();

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

  rt = this.add.renderTexture(0, 0, 800, 600)
    .setPostPipeline(BlurPostFX);

  draw();

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

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();

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

  //rt.clear();
  //rt.draw(graphics);
  rt.fill(0);
  rt.erase(graphics);

  
}
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