2

Tiled collision editor

Tiled map editor with layer and sprite selection

I have added tree collision this way in Tiled. How can I use this collision to collide with the player in Phaser?

winner_joiner
  • 12,173
  • 4
  • 36
  • 61

2 Answers2

1

Well there are many way's to do this, this is one way:

  1. get object layer, with the name from Tiled (link to the documentation):

    // let map = this.make.tilemap( {...} );
    let objectLayer = map.getObjectLayer( 'Trees' );
    
  2. iterate over all objects from that layer (link to the documentation):

    for( let obj in objectLayer.objects ){
        // ...
    }
    
  3. For each object from the layer:
    Depending on your object type (point, rectangle, ellipse, ...) you create the physics-body for the collision (for this example I will use a ellipse):

    let ellipse = this.add.ellipse( obj.x, obj.y, obj.width, obj.height );
    // you might need to set the "origin" 
    this.physics.add.existing( ellipse, true );
    ellipse.body.setCircle(obj.width / 2);
    

    Info/Tipp: if you are using arcade physics the "hitbox" will be a rectangle, doesn't matter which gameObject you use. If you want round physics body with arcade you could use the setCircle method on the body (link to documentation). For complex shapes I would recommend using the matter.js engine.

  4. Setup collision with: player, ai, ...

    this.physics.add.collider( player, ellipse );
    

Updated running demo:

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

let json_map = {"compressionlevel":-1,"height":5,"infinite":false,"layers":[{"compression":"","data":"AQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQAAAA==","encoding":"base64","height":5,"id":1,"name":"TileLayer1","opacity":1,"type":"tilelayer","visible":true,"width":8,"x":0,"y":0},{"draworder":"topdown","id":2,"name":"ObjectLayer1","objects":[{"class":"","ellipse":true,"height":10,"id":1,"name":"","rotation":0,"visible":true,"width":10,"x":8,"y":8},{"class":"","height":5,"id":2,"name":"","rotation":0,"visible":true,"width":15,"x":19.2,"y":25.6},{"class":"","height":0,"id":3,"name":"","point":true,"rotation":0,"visible":true,"width":0,"x":48,"y":4.8}],"opacity":1,"type":"objectgroup","visible":true,"x":0,"y":0}],"nextlayerid":3,"nextobjectid":4,"orientation":"orthogonal","renderorder":"right-down","tiledversion":"1.9.2","tileheight":8,"tilesets":[{"columns":1,"firstgid":1,"image":"tiles.png","imageheight":8,"imagewidth":8,"margin":0,"name":"tiles","spacing":0,"tilecount":1,"tileheight":8,"tilewidth":8}],"tilewidth":8,"type":"map","version":"1.9","width":8};

let config = {
    type: Phaser.AUTO,
    width: 8 * 8,
    height: 5 * 8,
    zoom: 4,
    physics: {
        default: 'arcade',
        arcade: { debug: true }
    },
    scene: { preload, create },
}; 

function preload () {
    this.load.tilemapTiledJSON('map', json_map);
}

function create () {
    let graphics = this.make.graphics();
    graphics.fillStyle(0x933AFF);
    graphics.fillRect(0, 0, 10, 10);
    graphics.generateTexture('tiles', 10, 10);

    let player = this.add.rectangle(50, 10, 5, 5, 0xffffff);

    this.physics.add.existing(player);

    player.setDepth(100);
    player.body.setVelocityX(-10);

    let map = this.make.tilemap({ key: 'map', tileWidth: 8, tileHeight: 8 });
    let tiles = map.addTilesetImage('tiles', 'tiles');
    let layer = map.createLayer(0, tiles, 0, 0);
    let objectLayer = map.getObjectLayer( 'ObjectLayer1' );

    for( let obj of objectLayer.objects ){
        
        // since you are not displaying the object the shape doesn't matter, only the collision body
        let gameObject = this.add.rectangle( obj.x, obj.y, obj.width, obj.height )
                .setOrigin(0);
        
        this.physics.add.existing( gameObject, true );
        
        if(obj.ellipse){
           // For the ellipse version you would need to change the body
            gameObject.body.setCircle( obj.width / 2 );
        } else if(obj.point){
            // For the point we need no set an width and height
            gameObject.body.setSize( 4, 4 );
        }
        
        this.physics.add.collider( player, gameObject );
    }
}

new Phaser.Game(config);
<script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>

Btw.: In my Tiled version even, when I create the circle-object the width and height, I had to set does properties manualy. So check them if they are set.

screenshot of Tiled

winner_joiner
  • 12,173
  • 4
  • 36
  • 61
  • It makes a rectangle over the whole area and doesn't affect where I have added the collision and what its size. I am using arcade physics. – Mohsin Uddin Abir Feb 24 '23 at 13:59
  • @MohsinUddinAbir yes I know, and that i wrote in the answer. arcade physics just knows boxes and with the `setCircle` function just circle bodies. if you can work around that limitations you havr to use the matterjs engine. – winner_joiner Feb 24 '23 at 14:23
  • Can you tell me the easiest way to use it in arcade physics, which contains the exact position and size, with an example? I don't like to change the physics engine to matter js. – Mohsin Uddin Abir Feb 24 '23 at 15:02
  • The best way to get a circle for an object would be `ellipse.setCircle(obj.width/2)` sadly no real ellipse is possible. _(I also updated the answer)_ – winner_joiner Feb 24 '23 at 15:05
  • And armed with this knowledge, I would only create objects Tiled, that are _rectangles_ or _circles_. (like this you will know how the circular/rectangle body will be in the game) – winner_joiner Feb 24 '23 at 15:09
  • Error: ellipse.setCircle is not a function. – Mohsin Uddin Abir Feb 24 '23 at 22:26
  • I don't know why Phaser didn't add an easy way to do it, I got the setCollisionFromCollisionGroup function for TileLayer, but for the ObjectLayer, there don't have like a function. – Mohsin Uddin Abir Feb 24 '23 at 22:35
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/252115/discussion-between-mohsin-uddin-abir-and-winner-joiner). – Mohsin Uddin Abir Feb 24 '23 at 23:00
0

Finally, I got a solution, and this is working for me.


 
  
  addCollisionFromTiled(layerName: string, group: number) {
    const graphics = this.scene.add.graphics().lineStyle(2, 0x00ff00, 1)
    const objectLayer = this.map.getObjectLayer(layerName)

    objectLayer.objects.forEach((object: Phaser.Types.Tilemaps.TiledObject) => {
      if (object.rectangle) {
        const rect2 = this.scene.add.rectangle(0, 0, object.width, object.height)
        const polygon = new Phaser.Geom.Polygon(rect2.pathData)
        const body2 = this.scene.matter.add.fromVertices(
          object.x! + object.width! / 2,
          object.y! + object.height! / 2,
          polygon.points.slice(0, -1)
        )
        const collision = this.scene.matter.add.gameObject(
          rect2,
          body2
        ) as Phaser.Physics.Matter.Sprite
        collision.setStatic(true)
        collision.setCollisionGroup(group)

        graphics.strokeRect(object.x!, object.y!, object.width!, object.height!)
      } else if (object.ellipse) {
        const elps2 = this.scene.add.ellipse(0, 0, object.width, object.height)
        const polygon = new Phaser.Geom.Polygon(elps2.pathData)
        const body2 = this.scene.matter.add.fromVertices(
          object.x! + object.width! / 2,
          object.y! + object.height! / 2,
          polygon.points.slice(0, -1)
        )
        const collision = this.scene.matter.add.gameObject(
          elps2,
          body2
        ) as Phaser.Physics.Matter.Sprite
        collision.setStatic(true)
        collision.setCollisionGroup(group)

        graphics.strokeEllipse(
          object.x! + object.width! / 2,
          object.y! + object.height! / 2,
          object.width!,
          object.height!
        )
      } else if (object.polygon || object.polyline) {
        const objPol = object.polygon ? object.polygon : object.polyline
        const polygon = new Phaser.Geom.Polygon(objPol)
        const points: { x: number; y: number }[] = []
        for (let point of polygon.points) {
          points.push({
            x: object.x! + point.x,
            y: object.y! + point.y,
          })
        }
        const sliceCentre = this.scene.matter.vertices.centre(points)
        const body2 = this.scene.matter.add.fromVertices(sliceCentre.x, sliceCentre.y, points)
        const poly2 = this.scene.add.polygon(sliceCentre.x, sliceCentre.y, points)
        const collision = this.scene.matter.add.gameObject(
          poly2,
          body2
        ) as Phaser.Physics.Matter.Sprite
        collision.setStatic(true)
        collision.setCollisionGroup(group)

        graphics.strokePoints(points)
      }
    })
  }
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Mar 18 '23 at 12:11