Physics bug in ARCADE mode with angled sprite?

I’m using Phaser 3 with ARCADE physics. I have created a static sprite group using code from the part8.html sample. If I don’t angle a sprite that is a child of this “platform” group then everything behaves properly. However, if I angle one of the child sprites, other objects interact with the object as if it wasn’t angled. They still walk over the platform as if it were parallel to the ground. I know this because if I walk the player sprite over the platform (“dude.png”), it walks parallel to the ground and through the visible area of the sprite that is angled. However, when it reaches the end of the sprite/platform, the player drops down right at the point the sprite would end at if it were not angled.

My code is below. Is this a bug in the ARCADE physics code?

function MyPointXY(x, y) {
    this.x = x;
    this.y = y;
}

var config = {
    type: Phaser.AUTO,
    width: 800,
    height: 600,
    parent: 'house-div',
    physics: {
        default: 'arcade',
        arcade: {
            gravity: { y: 300 },
            debug: false
        }
    },
    scene: {
        preload: preload,
        create: create,
        update: update
    },
    "transparent": true
};

var background;
var platformsGroup;
var stairs;
var player;

var game = new Phaser.Game(config);
var cursors;

var bIsFirstUpdate = true;
var playerStartPoint = new MyPointXY(100, 50);

function newPlatformForGroup(phaserObj, platformsGroup, x, y, angleOfRotation = 0, width, height) {
    let newPlatform = platformsGroup.create(x, y, 'platform');

    newPlatform.displayWidth = width;
    newPlatform.displayHeight = height;
    newPlatform.angle = angleOfRotation,
    newPlatform.my_type = 'platform';

    return newPlatform;
}

function preload ()
{
    this.load.image('background', '/app/dist/assets/tsll-house-cropped-1024w-by-610h.png');
    this.load.image('platform', '/app/dist/assets/platform-green-100x-x-10h.png');
    this.load.spritesheet(
        'dude', '/app/dist/assets/dude.png', 
        { frameWidth: 32, frameHeight: 48 });
    
    this.load.json('shapes', '/app/dist/sprites.json');
}

function create ()
{
    // Make the texture used with all our platforms.
    const x = 400;
    const y = 600;
    const width = 10;
    const height = 10;
    
    // Get the shapes data from the loader cache.
    var shapes = this.cache.json.get('shapes');
    
    background = this.add.image(512, 310, 'background');
    background.isStatic = true;

    // Make the platforms.    
    platformsGroup = this.physics.add.staticGroup();
    
    // 1st floor stairs.
    newPlatformForGroup(this, platformsGroup, 364, 520, 43, 200, 5);
    // 2nd floor stairs.
    newPlatformForGroup(this, platformsGroup, 380, 520, 43, 200, 5);
    
    player = this.physics.add.sprite(400, 0, 'dude');
    player.setBounce(0.2);
    player.setCollideWorldBounds(true);
    
    this.anims.create({
        key: 'left',
        frames: this.anims.generateFrameNumbers('dude', { start: 0, end: 3 }),
        frameRate: 10,
        repeat: -1
    });

    this.anims.create({
        key: 'turn',
        frames: [ { key: 'dude', frame: 4 } ],
        frameRate: 20
    });

    this.anims.create({
        key: 'right',
        frames: this.anims.generateFrameNumbers('dude', { start: 5, end: 8 }),
        frameRate: 10,
        repeat: -1
    });
    
    cursors = this.input.keyboard.createCursorKeys();
    this.physics.add.collider(player, platformsGroup);
    
    // Development only: Show cursor XY position for help with element positioning.
    this.label = this.add.text(0, 0, '(x, y)', { fontFamily: '"Monospace"'});
    this.pointer = this.input.activePointer;
}

function update ()
{
    // If the player is not defined yet then we wait until it is.
    if (player) {
        this.physics.world.collide(player, platformsGroup);
        
        const labelText =
            '(Cursor: ' + this.pointer.x + ', ' + this.pointer.y + ')'
            +  ' <-> '
            + '(Player: ' + Math.trunc(player.x) + ', ' + Math.trunc(player.y) + ')';
        this.label.setText(labelText);
        
        if (bIsFirstUpdate) {
            bIsFirstUpdate = false;
        }
        
        if (cursors.left.isDown)
        {
            player.setVelocityX(-160);
            player.anims.play('left', true);
        }
        else if (cursors.right.isDown)
        {
            player.setVelocityX(160);
            player.anims.play('right', true);
        }
        else
        {
            player.setVelocityX(0);
            player.anims.play('turn');
        }
        
        if (cursors.up.isDown && player.body.touching.down) {
            player.setVelocityY(-330);
        }
    }
}

I could be wrong here, but I believe I remember reading that Arcade physics doesn’t support angles in its physics mode. Arcade is mostly just a high-performance AABB collision physics system. You may have to move up to the middle-tier physics system, Impact, or write your own angle-handling logic.

Maybe someone with more experience can confirm or correct this.

That’s too bad and thanks for letting me know. I’ve gone back to MatterJS for the moment. Is Impact easier to use than MatterJS? I’m finding these libraries to be powerful but very quirky to use so I end up losing a lot of time chasing tutorials and scratching my head.

Actually, after looking at the examples, I am not 100% sure Impact handles angled platforms, either. It likely is easier to use than Matter.js (they tend to grow more complex as you step from Arcade to Impact to Matter). I know Impact isn’t used extremely often by the community, just as Ninja wasn’t used very often in Phaser 2. I guess most people either need basic collision detection or they need advanced physics with few needs landing in the middle.

I would suggest some more research to confirm this, or if Impact does handle angled platforms. I have not had a chance to really dig into the more advanced physics engines of Phaser 3 yet.

Well I guess I landed on “green” then. I do have angled platforms working with Matter-JS right now. My main problem with Matter-JS is finding decent docs, especially tutorials that work with Phaser 3. All the Matter-JS docs/examples work off the top-level Matter namespaces instead of an instance that lives in the “.matter” property that hangs off of a Phaser 3 object.