Skip to content

wiserim/phaser-raycaster

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Phaser Raycaster

Raycasting plugin for Phaser 3.

GitHub release npm GitHub Github file size

Phaser Raycaster is a Phaser 3 plugin which provide raycasting for geometric game objects, sprites, Arcade Physics and Matter.js bodies.

Documentation: https://wiserim.github.io/phaser-raycaster/

Code examples are available on CodePen: LINK

Features:

  • compatible with arcade and matter physics,
  • raycasting in a single direction, 360 degrees circle or in a cone,
  • visibility detection (collision detection with game objects),
  • test rays on mapped game objects (containers, lines, rectangles, polygons, circles, sprites, tilemaps and matter bodies),
  • provides closest intersection points between rays and tested objects,
  • tests can be made on all mapped objects, selected ones or only ones within detection range,
  • static and dynamic mapping for individual objects,
  • mapped objects intersections detection,
  • debug mode.

NPM

npm install phaser-raycaster

CDN

https://www.jsdelivr.com/package/npm/phaser-raycaster

Getting started

1. Include plugin in your project:

<!--CDN-->
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser-raycaster.min.js"></script>
# NPM
npm install phaser-raycaster

2. Enable plugin in your Game config:

import PhaserRaycaster from 'phaser-raycaster'

let config = {
    type: Phaser.Auto,
    parent: 'game',
    width: 800,
    height: 600,
    backgroundColor: "black",
    scene: [
        Scene1
    ],
    plugins: {
        scene: [
            {
                key: 'PhaserRaycaster',
                plugin: PhaserRaycaster,
                mapping: 'raycasterPlugin'
            }
        ]
    }
}

new Phaser.Game(config);

3. Create new raycaster in your scene:

create() {
  this.raycaster = this.raycasterPlugin.createRaycaster(options);
  
  // additional code
}

If you're using TypeScript, you need to add to scene plugin class:

import PhaserRaycaster from 'phaser-raycaster'

export default class MyScene extends Phaser.Scene {
  raycasterPlugin: PhaserRaycaster

  // aditional code
}

4. Create new ray

create() {
  // additional code
  
  this.ray = this.raycaster.createRay();
  
  // additional code
}

5. Map game objects which will be tested by rays

//create game object
this.rectangle = this.add.rectangle(100, 100, 50, 50)
  .setStrokeStyle(1, 0xff0000);

//map game object
this.raycaster.mapGameObjects(this.rectangle);

//create group
this.group = this.add.group();

//map game objects actually in group
this.raycaster.mapGameObjects(this.group.getChildren());

//map tilemap layer
this.map = this.make.tilemap();
this.tilemap = this.map.createStaticLayer();

this.raycaster.mapGameObjects(this.tilemap, false, {
  collisionTiles: [1,2,3] //array of tiles types which can collide with ray
});

6. Cast ray

//set ray position
this.ray.setOrigin(400, 300);
//set ray direction (in radians)
this.ray.setAngle(2);
//set ray direction (in degrees)
this.ray.setAngleDeg(90);
//cast single ray and get closets intersection, hit mapped object and hit segment
let intersection = this.ray.cast();
let hitObject = intersection.object;
let hitSegment = intersection.segment;

//cast rays in all directions (toward all mapped objects vertices / points)
let intersections = this.ray.castCircle();

//set ray's cone angle (in radians)
this.ray.setCone(1);
//set ray's cone angle (in degrees)
this.ray.setConeDeg(90);

//cast rays in a cone
let intersections = this.ray.castCone();

7. Raycaster bounding box

By default Raycaster is setting it's bounding box based on Arcade Physics / Matter physics world bounds. If world size will change after creation of Raycaster, bounding box needs to be updated.

//define bounds
var bounds = new Phaser.Geom.Rectangle(x, y, width, height);

//get world bounds (arcade physics)
bounds = this.worldLayer.getBounds();

//get bounds (matter physics)
let walls = this.matter.world;
bounds = new Phaser.Geom.Rectangle(
  walls.top.vertices[3].x, //x
  walls.top.vertices[3].y, //y
  walls.bottom.vertices[1].x - walls.top.vertices[3].x, //width
  walls.bottom.vertices[1].y - walls.top.vertices[3].y //height
)

//set bounding box on raycaster creation
var raycaster = this.raycasterPlugin.createRaycaster({
  boundingBox: bounds
});

//set bounding box after creation
raycaster.setBoundingBox(x, y, width, height);

8. Collisions (arcade physics)

//enable auto slicing field of view
this.ray.autoSlice = true;
//enable arcade physics body
this.ray.enablePhysics();
//set collision (field of view) range
this.ray.setCollisionRange(200);
//cast ray
this.ray.castCircle();

//get all game objects in field of view (which bodies overlap ray's field of view)
let visibleObjects = this.ray.overlap();

//get objects in field of view
visibleObjects = this.ray.overlap(group.getChildren());

//check if object is in field of view
visibleObjects = this.ray.overlap(gameObject);

//add overlap collider (require passing ray.processOverlap as process callback)
this.physics.add.overlap(this.ray, targets, function(rayFoVCircle, target){
  /*
  * What to do with game objects in line of sight.
  */
}, this.ray.processOverlap.bind(this.ray));

9. Collisions (matter physics)

//enable auto slicing field of view
this.ray.autoSlice = true;
//enable matter physics body
this.ray.enablePhysics('matter');
//cast ray
this.ray.castCircle();

//get all game objects and bodies in field of view (which bodies overlap ray's field of view)
let visibleObjects = this.ray.overlap();

//get objects and bodies in field of view
visibleObjects = this.ray.overlap([gameObject1, gameObject2, body1, body2]);

//check if object or body is in field of view
visibleObjects = this.ray.overlap(gameObject);

//add onCollide event
this.ray.setOnCollide(function(collisionInfo){
  //get body
  let body = collisionInfo.bodyA.label === 'phaser-raycaster-ray-body' ? collisionInfo.bodyB : collisionInfo.bodyA;
    /*
    * What to do with game object which enters line of sight .
    */
  }
});

//add onCollideWith event
this.ray.setOnCollideWith(body, function(body, collisionInfo){
    /*
    * What to do with game object which enters line of sight.
    */
  }
});

//add onCollideEnd event
this.ray.setOnCollideEnd(function(collisionInfo){
  //get body
  let body = collisionInfo.bodyA.label === 'phaser-raycaster-ray-body' ? collisionInfo.bodyB : collisionInfo.bodyA;
    /*
    * What to do with game object which leaves line of sight.
    */
  }
});

//add onCollideActive event
this.ray.setOnCollide(function(collisionInfo){
  //get body
  let body = collisionInfo.bodyA.label === 'phaser-raycaster-ray-body' ? collisionInfo.bodyB : collisionInfo.bodyA;
    /*
    * What to do with game object while it's in line of sight.
    */
  }
});

10. Destroy objects

//remove mapped objects
this.raycaster.removeMappedObjects(object);
this.raycaster.removeMappedObjects(arrayOfObjects);

//destroy ray
this.ray.destroy();

//destroy raycaster
this.raycaster.destroy();

11. Statistics

//get raycaster statistics
let statistics = this.raycaster.getStats();
/*
  statistics = {
    mappedObjects: {
      total - mapped objects total
      static - static maps
      dynamic - dynamic maps
      rectangleMaps - rectangle maps
      polygonMaps - polygon maps
      circleMaps - circle maps
      lineMaps - line maps
      containerMaps - container maps
      tilemapMaps - tilemap maps
      matterMaps - matter body maps
    }
  }
*/

//get ray statistics
let rayStatistics = this.ray.getStats();
/*
  rayStatistics = {
    method - used casting method (cast, castCircle, castCone)
    rays - casted rays
    testedMappedObjects - tested mapped objects
    hitMappedObjects - hit mapped objects
    segments - tested segments
    time - casting time
  }
*/

12. Debug mode

  //enable debug mode
  this.raycaster = this.raycasterPlugin.createRaycaster({
    debug: true
  });

  //advanced debug mode options
  this.raycaster = this.raycasterPlugin.createRaycaster({
    debug: {
      enabled: false, //enable debug mode
      maps: true, //enable maps debug
      rays: true, //enable rays debug
      graphics: {
          ray: 0x00ff00, //debug ray color; set false to disable
          rayPoint: 0xff00ff, //debug ray point color; set false to disable
          mapPoint: 0x00ffff, //debug map point color; set false to disable
          mapSegment: 0x0000ff, //debug map segment color; set false to disable
          mapBoundingBox: 0xff0000 //debug map bounding box color; set false to disable
      }
    }
  });

  //change debug options after initialization
  this.raycaster.debugOptions.enabled = true;

  this.raycaster.setOptions({
    debug: true
  });