skip to Main Content

I’m making a top down game in Phaser 3.60.0 and I have everything coded to run on arcade physics. The only thing I need the matter physics engine for is sloped tiles used for diagonal walls.

Here are screenshots of what I have.
Arcade sprite along with matter collision on Tiled map
The player sprite goes through the collision

I haven’t figured out a way to have the two collide properly. I tried using this.physics.add.collider(this.player.sprite, groundLayer); which does work, but the slopes are turned into rectangles.

With matter, it seems like the collision is enabled by default, and the only instance of using a collision code is to trigger an event, which isn’t necessary.

I don’t want to convert my player sprite to matter since it’s a lot of work and quite complicated with my level of knowledge.

Here’s my code.

My level file:

import Player from './Player.js';
export default class Field01 extends Phaser.Scene {
  constructor () {
    super({
      key: 'field01',
      physics: {
        arcade: {
          debug: true,
          gravity: { y: 0 }
        },
        matter: {
          debug: true,
          gravity: { y: 0 },
          debugShowBody: true,
          debugBodyColor: 0x0000ff
        }
      }
    });
  }
preload() 
  {
    this.load.image('tiles', 'assets/tilemaps/tiles/overworld-ground.png');
    this.load.tilemapTiledJSON('map', 'assets/tilemaps/maps/Field01.json');
    this.load.image('ground', 'assets/platform.png');
    this.load.spritesheet('dude', 'assets/sprites/characters/link.png', { frameWidth: 16, frameHeight: 24 });
  }
create() 
  {
    var platforms;
    var player;
    this.map = this.make.tilemap({ key: 'map' });
    this.map.landscape = this.map.addTilesetImage('fieldfare', 'tiles');
    const groundLayer = this.map.createLayer("ground", [this.map.landscape], 0, 0);
    this.map.setCollisionBetween(2,9);
    this.cameras.main.setSize(256,224);
    this.cameras.main.setBounds(0, 0, this.map.widthInPixels, this.map.heightInPixels);
    this.player = new Player(this, 352, 48);
    this.add.existing(Player);
    this.cameras.main.startFollow(this.player.sprite);
    this.cameras.main.setDeadzone(4,4);
    platforms = this.physics.add.staticGroup();
    platforms.create(64, 64, 'ground');
    groundLayer.setCollisionByProperty({ collides: true });
    this.matter.world.convertTilemapLayer(groundLayer);
    this.physics.add.collider(this.player.sprite, groundLayer);
    this.physics.add.collider(this.player.sprite, platforms, function(player, platforms) { this.scene.switch('Level1'), player.y += 48}, null, this);
  }
update() {
  this.player.update();
  }
}

My player file:

export default class Player {
  constructor(scene, x, y) {
    this.scene = scene;
    const anims = scene.anims;
        anims.create({
            key: 'turn1',
            frames: [ { key: 'dude', frame: 32 } ],
            frameRate: 20
        });
        anims.create({
            key: 'rdown',
            frames: anims.generateFrameNumbers('dude', { start: 0, end: 7 }),
            frameRate: 16,
            repeat: -1
        });
        anims.create({
            key: 'rright',
            frames: anims.generateFrameNumbers('dude', { frames: [ 8, 9, 10, 11, 12, 13, 14, 15 ] }),
            frameRate: 16,
            repeat: -1
        });
        anims.create({
            key: 'rup',
            frames: anims.generateFrameNumbers('dude', { frames: [ 16, 17, 18, 19, 20, 21, 21, 23 ] }),
            frameRate: 16,
            repeat: -1
        });
        anims.create({
            key: 'rleft',
            frames: anims.generateFrameNumbers('dude', { frames: [ 24, 25, 26, 27, 28, 29, 30, 31 ] }),
            frameRate: 16,
            repeat: -1
        });
    this.sprite = scene.physics.add.sprite(x, y, "dude", 0).setSize(16, 16).setOffset(0, 8);
    this.sprite.anims.play("rdown");
    this.gamepad = scene.input.gamepad.once('down', function (pad, button, index) {
        this.gamepad = pad;
    }, this);
  }

  update() {
    const gamepad = this.gamepad;
    const sprite = this.sprite;
    const speed = 90;
    const prevVelocity = sprite.body.velocity.clone();
    sprite.body.setVelocity(0);
    if (gamepad.right && gamepad.up) {
      sprite.body.setVelocityX(speed);
      sprite.body.setVelocityY(-speed);
    } else if (gamepad.right && gamepad.down) {
      sprite.body.setVelocityX(speed);
      sprite.body.setVelocityY(speed);
    } else if (gamepad.left && gamepad.up) {
      sprite.body.setVelocityX(-speed);
      sprite.body.setVelocityY(-speed);
    } else if (gamepad.left && gamepad.down) {
      sprite.body.setVelocityX(-speed);
      sprite.body.setVelocityY(speed);
    } else if (gamepad.left) {
      sprite.body.setVelocityX(-speed);
    } else if (gamepad.right) {
      sprite.body.setVelocityX(speed);
    } else if (gamepad.up) {
      sprite.body.setVelocityY(-speed);
    } else if (gamepad.down) {
      sprite.body.setVelocityY(speed);
    }
    sprite.body.velocity.normalize().scale(speed);
    if (gamepad.left) {
      sprite.anims.play("rleft", true);
    } else if (gamepad.left && gamepad.down) {
      sprite.anims.play("rleft", true);
    } else if (gamepad.left && gamepad.up) {
      sprite.anims.play("rleft", true);
    } else if (gamepad.right && gamepad.down) {
      sprite.anims.play("rright", true);
    } else if (gamepad.right && gamepad.up) {
      sprite.anims.play("rright", true);
    } else if (gamepad.up) {
      sprite.anims.play("rup", true);
    } else if (gamepad.right) {
      sprite.anims.play("rright", true);
    } else if (gamepad.down) {
      sprite.anims.play("rdown", true);
    } else {
      sprite.stopOnFrame(sprite.anims.currentAnim.getFrameAt(0))
    }
  }
}

Here’s my game config file;

import Field01 from "./Field01.js";

var config = {
  type: Phaser.AUTO,
  width: 256,
  height: 224,
  pixelArt: true,
  input: {
    gamepad: true
  },
  physics: {
    default: 'arcade',
    arcade: {
      gravity: { y: 0 },
      debug: true
    }
  },
  scene: [ Field01 ]
  }

var game = new Phaser.Game(config);

And my index file:

<!DOCTYPE html>
<html>
  <head>
    <script src="phaser.js"></script>
    <script src="matter.js"></script>
  </head>
  <body>
    <script type="module" src="game.js"></script>
  </body>
</html>

Anything I can do?

If I have to change to matter, then how would I properly convert it? This is just a last resort if nothing else is possible.

2

Answers


  1. Chosen as BEST ANSWER

    I managed to figure out how to convert my arcade player to matter.

    Here's how I did it for anyone that has this issue and finds this in the future.

    First off I changed the add sprite line of code in my player file. I replaced "Physics" with "Matter"

        this.sprite = scene.matter.add.sprite(x, y, "dude", 0);
    

    Next I changed the update section and removed the "body" part out of the

    sprite.body.setVelocity(speed)
    sprite.body.setVelocityX(speed)
    sprite.body.setVelocityY(-speed)
    

    And all the others.

    Now it should looks like this.

       if (gamepad.right && gamepad.up) {
          sprite.setVelocityX(0.96);
          sprite.setVelocityY(-0.96);
        } else if (gamepad.right && gamepad.down) {
          sprite.setVelocityX(0.96);
          sprite.setVelocityY(0.96);
        } else if (gamepad.left && gamepad.up) {
          sprite.setVelocityX(-0.96);
          sprite.setVelocityY(-0.96);
        } else if (gamepad.left && gamepad.down) {
          sprite.setVelocityX(-0.96);
          sprite.setVelocityY(0.96);
        } else if (gamepad.left) {
          sprite.setVelocityX(-1.44);
        } else if (gamepad.right) {
          sprite.setVelocityX(1.44);
        } else if (gamepad.up) {
          sprite.setVelocityY(-1.44);
        } else if (gamepad.down) {
          sprite.setVelocityY(1.44);
        }
    

    I also replaced the "speed" with numbers so I can easily control the speed the player goes when diagonal or straight. I also removed the

    const speed = 90;
    

    and

    const prevVelocity = sprite.body.velocity.clone();
    

    line since they're not necessary anymore.

    Now it should work. But now there's one problem. The player sprite rotates when it moves along the walls. I fixed that problem by adding this line of code at the bottom of the Create() function in the player file.

    this.sprite
    .setFixedRotation();
    

    Now the player's angle is fixed and no longer spins.

    The player collides with the map as I intended now.

    Here's the full code for the player file.

    export default class Player {
      constructor(scene, x, y) {
        this.scene = scene;
        const anims = scene.anims;
            anims.create({
                key: 'turn1',
                frames: [ { key: 'dude', frame: 32 } ],
                frameRate: 20
            });
            anims.create({
                key: 'rdown',
                frames: anims.generateFrameNumbers('dude', { start: 0, end: 7 }),
                frameRate: 16,
                repeat: -1
            });
            anims.create({
                key: 'rright',
                frames: anims.generateFrameNumbers('dude', { frames: [ 8, 9, 10, 11, 12, 13, 14, 15 ] }),
                frameRate: 16,
                repeat: -1
            });
            anims.create({
                key: 'rup',
                frames: anims.generateFrameNumbers('dude', { frames: [ 16, 17, 18, 19, 20, 21, 21, 23 ] }),
                frameRate: 16,
                repeat: -1
            });
            anims.create({
                key: 'rleft',
                frames: anims.generateFrameNumbers('dude', { frames: [ 24, 25, 26, 27, 28, 29, 30, 31 ] }),
                frameRate: 16,
                repeat: -1
            });
        this.sprite = scene.matter.add.sprite(x, y, "dude", 0);
        
        this.sprite.anims.play("rdown");
        this.gamepad = scene.input.gamepad.once('down', function (pad, button, index) {
            this.gamepad = pad;
        }, this);
        this.sprite
        .setFixedRotation();
      }
    
      update() {
        const gamepad = this.gamepad;
        const sprite = this.sprite;
        sprite.setVelocity(0);
        if (gamepad.right && gamepad.up) {
          sprite.setVelocityX(0.96);
          sprite.setVelocityY(-0.96);
        } else if (gamepad.right && gamepad.down) {
          sprite.setVelocityX(0.96);
          sprite.setVelocityY(0.96);
        } else if (gamepad.left && gamepad.up) {
          sprite.setVelocityX(-0.96);
          sprite.setVelocityY(-0.96);
        } else if (gamepad.left && gamepad.down) {
          sprite.setVelocityX(-0.96);
          sprite.setVelocityY(0.96);
        } else if (gamepad.left) {
          sprite.setVelocityX(-1.44);
        } else if (gamepad.right) {
          sprite.setVelocityX(1.44);
        } else if (gamepad.up) {
          sprite.setVelocityY(-1.44);
        } else if (gamepad.down) {
          sprite.setVelocityY(1.44);
        }
        if (gamepad.left) {
          sprite.anims.play("rleft", true);
        } else if (gamepad.left && gamepad.down) {
          sprite.anims.play("rleft", true);
        } else if (gamepad.left &&  gamepad.up) {
          sprite.anims.play("rleft", true);
        } else if (gamepad.right && gamepad.down) {
          sprite.anims.play("rright", true);
        } else if (gamepad.right && gamepad.up) {
          sprite.anims.play("rright", true);
        } else if (gamepad.up) {
          sprite.anims.play("rup", true);
        } else if (gamepad.right) {
          sprite.anims.play("rright", true);
        } else if (gamepad.down) {
          sprite.anims.play("rdown", true);
        } else {
          sprite.stopOnFrame(sprite.anims.currentAnim.getFrameAt(0))
        }
      }
    }
    

  2. My answer is abit too late, but I was working on the demo. 🙂

    Honestly if you are using the matter engine only for the sloped tiles you should remove the matter engine, this is a overkill for that little bit of functionallity, and a solution where an object (the player) uses matter and arcade physics will be very hacky.
    I would suggest living with the non-sloped tiles, But if you want to use arcade and matter here is a hacky solution. I attaching a matter object to a arcade object. With matter – collisions events and this.player.body.reset(...) to set the position of the aracde body.

    document.body.style = 'margin:0;';
    
    class MultiPhysicsExample extends Phaser.Scene {
        constructor () {
            super({
                key: 'MultiPhysicsExample',
                physics: {
                    arcade: {
                        debug: true,
                    },
                    matter: {
                        debug: true,
                        gravity: { y: 0 }
                    }
                }
            });
        }
    
        create () {
            this.add.text(10,10, 'Use W,S,A,D to move the player')
                .setScale(1.5)
                .setOrigin(0)
                .setStyle({fontStyle: 'bold', fontFamily: 'Arial'}); 
                
            let graphics  = this.make.graphics();
            graphics.fillStyle(0x00ff00);
            graphics.fillRect(0, 0, 500, 50);
            graphics.generateTexture('platform', 500, 50);
    
            graphics.fillStyle(0xffffff);
            graphics.fillRect(0, 0, 20, 20);
            graphics.generateTexture('player', 20, 20);
    
            //  Matter JS:
            this.matter.add.image(250, 160, 'platform', null, { isStatic: true })
                .setScale(2, 1)
                .setAngle(10);
    
            this.helper = this.matter.add.image(190, 90, null);
    
            this.helper.setBody({
                type: 'circle',
                radius: 22
            });
    
            //  Arcade Physics:
            this.player = this.physics.add.image(190, 90, 'player');
    
            this.player.setVelocity(100, 200);
            this.player.setBounce(1, 1);
            this.player.setCollideWorldBounds(true);
    
            this.keys = this.input.keyboard.addKeys("W,A,S,D");
    
            this.matter.world.on('collisionactive', (event, bodyA, bodyB) => {
            console.info(bodyA, bodyB, bodyB.label)
                    if([bodyA.label, bodyB.label].indexOf('Circle Body') > -1){
                        this.player.body.reset(this.helper.x, this.helper.y)
                        this.player._colliding = true;
                    } 
                });
                
                this.matter.world.on('collisionend',  (event, bodyA, bodyB) => {
                    if([bodyA.label, bodyB.label].indexOf('Circle Body') > -1){
                        this.player._colliding = false;
                         this.player.body.reset(this.helper.x, this.helper.y)
                    }
                });
        } 
    
        update(){
            if(!this.player._colliding){
                // reseting the matter object on move.
                this.helper.x = this.player.x;
                this.helper.y = this.player.y;           
    
                let speed = 120;
                this.player.setVelocity(0);
                if (this.keys.A.isDown) {
                    this.player.setVelocityX(-speed);
                } else if (this.keys.D.isDown) {
                    this.player.setVelocityX(speed);
                }
    
                if (this.keys.W.isDown) {
                    this.player.setVelocityY(-speed);
                } else if (this.keys.S.isDown ) {
                    this.player.setVelocityY(speed);
                } 
            }
        }
    }
    
    const config = {
        type: Phaser.AUTO,
        width: 500,
        height: 180,
        scene: [ MultiPhysicsExample ],
    };
    
    new Phaser.Game(config);
    <script src="//cdn.jsdelivr.net/npm/phaser/dist/phaser.min.js"></script>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search