Phaser Pixel Perfect collision for a maze

315 Views Asked by At

I am making a maze game and need to ensure the maze walls can be collided with. I have two ideas for how to do this:

  1. Make create individual pieces of maze wall, arrange them in the shape of the maze I want, and then program collision detection for each one.

  2. Make one sprite for the walls of the entire maze and use pixel perfect collision to allow the play to go between the walls and still be hit by them.

As I find the process of setting the x and y coordinates of static sprites in games tedious, I really hope to not have to use the first solution. But I've never actually programmed pixel perfect collision in any language before and I'm struggling to find a website that covers it for Phaser 3 or understand in general. Can someone provide an example?

1

There are 1 best solutions below

6
winner_joiner On

The best solution would be to use a tilemap, with a tileset(s) and using the (opensource) tool Tiled. (you could even use collision shape, instead of the usual boxes here an example)

By the way, this method, is the one, that I would recommend.

But to keep things easy, and without extra tools, an option is to use phaser's tilemap with an array ( here are some other examples ), here you just have to define a grid/map (and atleast one layer) and than you can use it for collision. The only thing is if you want to use a single big image with the whole maze, you would have to match the colliding tiles with the maze on the image.

This solution is not pixel perfect, since the collisions are in tiles, but you could make the tile smaller, if you need more details. (Although you could improve the collision accuracy, in the collide or overlap function link to documentation )

Here some demo code:

document.body.style = 'margin:0;';

var config = {
    type: Phaser.AUTO,
    width: 400,
    height: 150,
    zoom: 1.25,
    scene: { create },
    banner: false
}; 

var level1 = [[1,1,1,1,1,1],
    [1,0,0,0,0,1],
    [1,1,1,0,0,1],
    [1,0,0,1,0,1],
    [1,0,0,0,0,1],
    [1,1,1,1,1,1]];
    
var level2 = [[ 1,1,1,1,1,1,1,1,1,1,1,1 ],
    [ 1,1,1,1,1,1,1,1,1,1,1,1 ],
    [ 1,1,0,0,0,0,0,0,0,0,1,1 ],
    [ 1,1,0,0,0,0,0,0,0,0,1,1 ],
    [ 1,1,1,1,1,1,0,0,0,0,1,1 ],
    [ 1,1,1,1,1,1,0,0,0,0,1,1 ],
    [ 1,1,0,0,0,0,0,0,0,0,1,1 ],
    [ 1,1,0,0,0,0,0,0,0,0,1,1 ],
    [ 1,1,0,0,0,0,0,0,1,0,1,1 ],
    [ 1,1,0,0,0,0,0,0,0,0,1,1 ],
    [ 1,1,1,1,1,1,1,1,1,1,1,1 ],
    [ 1,1,1,1,1,1,1,1,1,1,1,1 ]];
    
var level3 = [[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1],
    [1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]];

function create () {
    
    this.add.text(10, 10, 'Tile size\n16 x 16').setFontSize('12px');
    this.add.text(150, 10, 'Tile size\n8 x 8').setFontSize('12px');
    this.add.text(290, 10, 'Tile size\n4 x 4').setFontSize('12px');
    
    // Just empty tileset so that it can be overlayed over a background image
    let g = this.make.graphics({x: 0, y: 0, add: false});
    g.generateTexture('empty-block', 16, 16);

    // When loading from an array, make sure to specify the tileWidth and tileHeight
    var map = this.make.tilemap({ data: level1, tileWidth: 16, tileHeight: 16 });
    var tiles = map.addTilesetImage('empty-block');
    var layer = map.createLayer(0, tiles, 10, 40);

    map.setCollision(1);

    // Just to visualize the collision Tiles
    var debugGraphics = this.add.graphics();
    map.renderDebug(debugGraphics);
    
    //Map with smaller tiles
    // When loading from an array, make sure to specify the tileWidth and tileHeight
    var map2 = this.make.tilemap({ data: level2, tileWidth: 8, tileHeight: 8 });
    var tiles2 = map2.addTilesetImage('empty-block');
    var layer2 = map2.createLayer(0, tiles2, 140, 0);

    map2.setCollision(1);

    // Just to visualize the collision Tiles
    map2.renderDebug(debugGraphics);
    
    //Map with much smaller tiles        
    // When loading from an array, make sure to specify the tileWidth and tileHeight
    var map3 = this.make.tilemap({ data: level3, tileWidth: 4, tileHeight: 4 });
    var tiles3 = map3.addTilesetImage('empty-block');
    var layer3 = map3.createLayer(0, tiles3, 140, 0);

    map3.setCollision(1);

    // Just to visualize the collision Tiles
    map3.renderDebug(debugGraphics);

}

new Phaser.Game(config);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.min.js"></script>

You could make the tiles even smaller, and with the tiles you can define the collision with an array or csv or json or ..., BUT too many tiles might cause performance issues so, don't make them too small.