Matter Physics: Sprite with custom hitbox shape gets stuck on the tilemap layer.

Hello Phaser Community,

I’m working on a simple platformer game using Matter Physics. My game includes a tilemap, and a physics enabled sprite called the “player”. I’ve included the function to move the sprite, using A and D keys to move left or right, then using the space bar to jump. This is my current code:

scene1.js

// Import Gameobjects
import Player from "../objects/player.js";
import Enemy from "../objects/enemy.js";

export default class Game extends Phaser.Scene {
    constructor() {
        super("game");
    }

    create() {
        // Debug
        this.debugGraphics = this.add.graphics();
        this.showCollidingTiles = true;
        this.showFaces = true;
        this.showTiles = true;

        // Add Tilemap
        this.map = this.make.tilemap({ key: "test-world" });
        this.tileset = this.map.addTilesetImage("world-transparent", "tileset");

        this.layers = {
            ground: this.map.createDynamicLayer("ground", this.tileset, 0, 0),
            obstacles: this.map.createDynamicLayer(
                "obstacles",
                this.tileset,
                0,
                0
            ),
            functions: this.map.getObjectLayer("functions"),
        };

        // Add Sprites
        // Sprite Shapes
        this.shapes = {
            player: this.cache.json.get('player_shapes')
        }
        // Player
        this.player = new Player(this, 50, 0, "player");

        // Tile Objects
        // Create group for enemies
        this.enemies = this.add.group();
        this.layers.functions.objects.forEach((obj) => {
            this.enemies.add(
                new Enemy(this, obj.x, obj.y, obj.properties[1].value)
            );
        });

        // Physics
        // Tilemap Physics
        this.layers.ground.setCollisionByProperty({ collides: true });
        this.layers.obstacles.setCollisionByProperty({ collides: true });

        this.layers.ground.setCollisionBetween(1, 999);
        this.layers.obstacles.setCollisionBetween(1, 999);

        this.matter.world.convertTilemapLayer(this.layers.ground, {
            label: "ground",
        });
        this.matter.world.convertTilemapLayer(this.layers.obstacles, {
            label: "obstacles",
        });

        

        // Collision Logic
        this.matter.world.on("collisionactive", (e, a, b) => {
            // Check if player is touching the ground
            if (
                e.pairs.some(
                    (pair) =>
                        pair.bodyA.label == "player" &&
                        pair.bodyB.label == "ground"
                )
            ) {
                this.playerOnGround = true;
            }

            // Check if player touches an obstacle
            if (
                e.pairs.some(
                    (pair) =>
                        pair.bodyA.label == "player" &&
                        pair.bodyB.label == "obstacles"
                )
            ) {
                this.scene.pause();
                setTimeout(() => {
                    this.scene.start("game-over");
                }, 1000);
            }
        });

        // Misc
        // Keyboard Input Object
        this.keys = this.input.keyboard.addKeys({
            jump: "SPACE",
            left: "A",
            right: "D",
        });

        // Camera
        this.cameras.main.setBounds(0, 0, this.width, this.height, true);
        this.cameras.main.startFollow(this.player);
        this.cameras.main.zoom = 4;
        this.cameras.main.setLerp(1, 0);

        // Add world bounds
        this.matter.world.setBounds(0, 0, this.scale.width, this.scale.height);

        // Misc Variables

        // Debug
    }

    update() {
        // Misc variables that need to be updated every tick
        this.player.setRotation(0);
        this.enemies.children.entries.forEach((e) => {
            e.setRotation(0);
        });

        // Movement
        if (this.keys.right.isDown) {
            this.player.flipX = false;
            this.player.anims.play("walk", true);
            // this.player.x += 5;
            this.player.setVelocityX(2);
        } else if (this.keys.left.isDown) {
            this.player.flipX = true;
            this.player.anims.play("walk", true);
            // this.player.x -= 5;
            this.player.setVelocityX(-2);
        } else {
            this.player.anims.stop();
            this.player.anims.play("idle");
            // this.player.setVelocity(0, 0);
        }

        // The jump mechanism is seperated from the if chain, and we used .on('down') because unlike the other movements, we only need to trigger the animation once.
        this.keys.jump.on("down", () => {
            if (this.playerOnGround) {
                this.player.anims.play("jump", true);
                this.player.setVelocity(0, -7);
                this.playerOnGround = false;
            }
        });
    }
}

Player.js

export default class Player extends Phaser.Physics.Matter.Sprite {
    constructor(scene, x, y, key, options, frame) {
        super(scene.matter.world, x, y, key, frame, options);
        scene.add.existing(this);
        this.body.label = 'player';
        scene.anims.create({
            key: 'walk',
            frames: this.anims.generateFrameNumbers('player', {start: 8, end: 10}),
            frameRate: 10,
            repeat: -1
        })

        scene.anims.create({
            key: 'jump',
            frames: this.anims.generateFrameNumbers('player', {start: 0, end: 5}),
            frameRate: 10,
            repeat: 0
        })

        scene.anims.create({
            key: 'idle',
            frames: this.anims.generateFrameNumbers('player', {start: 0, end: 0}),
            frameRate: 10,
            repeat: -1
        })
    }
}

And this is a demo without me adding custom hitbox shape to the sprite:
https://drive.google.com/file/d/1ht4vFjaJr-fhDbBvvZULyYFsiODg21Mh/view?usp=sharing

Now, my problem arises when I use custom hitbox shapes in matter. I use this web app called Physics Editor and set my desired polygon. I successfully added the json file it generates to my code, then successfully imported the shape to my scene using:

 // Add Sprites
        // Sprite Shapes
        this.shapes = {
            player: this.cache.json.get('player_shapes')
        }
        // Player
        this.player = new Player(this, 50, 0, "player", {shape: this.shapes.player});

I saw the hitbox shape is now different in-game, but unfortunately, the sprite seems to get stuck on the tilemap, as I cannot perform jumps anymore, even though I’m clicking the space bar. I tried to look at my jump function if there is something wrong with it, but I haven’t changed it before. This is a recording of the problem after I imported the custom hitbox shape…

https://drive.google.com/file/d/1fK6gCPIXnAdk4RXuUenmmpbt3W5gilZo/view?usp=sharing

As you can see, I’ve been hitting my space bar multiple times, but the sprite won’t jump.

I’ve been looking around google for almost an hour now and matter physics really gets me confused (I still need it tho). I’d really appreciate some help right now. Thank you everyone