Search code examples
javascripthtmlgame-physicsphaser-framework

How can I make a collider for only the bottom of objects?


For a 2D top-down game made with Phaser, the player should be able to walk past the front and back of furniture, which I've achieved by changing the depth of the player according to the y value of their position. However, they should not be able to walk through furniture by moving down through it (or moving up through it). They also be blocked from walking through it by walking at the furniture from the sides when at the same y value (coming at the furniture from a higher/lower value is fine, they then just go behind/in front of the furniture).

I figure this could be made by blocking collisions between the bottom of the player sprite and the bottom x pixels of furniture. But I don't know how to do this and can't find anything in the documentation or online. I figure I can make small invisible objects that go in the bottom of the player and furniture, but is there a simpler way to achieve this effect?

I have the furniture grouped by depth, and for each depth have code like this:

create {
    furnitureBack = this.physics.add.staticGroup();
    furnitureBack.create(300, 80, 'furniture').setFrame(60).refreshBody();
    furnitureBack.setDepth(2);
    colliderBack = this.physics.add.collider(player, furnitureBack);
}

update {
    colliderBack.active = false;
    if (player.y > 75 && player.y < 85) {
        colliderBack.active = true;
    }
}

The problem with this is that it only works when the player enters the 75-85 y range without already overlapping with the furniture. If they enter that range by walking towards the furniture from the bottom, then the collider becomes active but the player is still able to walk through the item.


Solution

  • An easy and fast solution would be to use phaser physics, and just alter the size of the body.
    Here the link to the body Documentation (you could even make the collision box small and round with setCircle, here is the link Documentation)

    Here a working demo:
    (the white box is the player the brown box, is a box)

    document.body.style = 'margin:0;';
    
    var config = {
        type: Phaser.AUTO,
        width: 536/2,
        height: 183/2,
        zoom: 2,
        physics: {
            default: 'arcade',
            arcade: {            
                debug: true
            }
        },
        scene: {
            create,
            update
        },
        banner: false
    }; 
    
    function create () {
    
        this.add.text(10, 10, 'Use cursor-keys to move')
        this.player = this.add.rectangle(50, 80, 20, 40, 0xffffff)
          .setOrigin(.5, 1)
          .setDepth(1);
          
        this.box = this.add.rectangle(100, 80, 20, 20, 0x933B26)
          .setOrigin(.5, 1)
          .setDepth(2);
          
        this.physics.add.existing(this.box, true)
           .body
               //Setup physics-body size and position
               .setSize(20, 10, false)
               .setOffset(0, 10);
        
          
        this.physics.add.existing(this.player)
          .body
              //Setup physics-body size and position
              .setSize(20, 20, false)
              .setOffset(0, 20);
          
        this.keys = this.input.keyboard.createCursorKeys();
        
        this.physics.add.collider(this.player, this.box)
            
    }
    
    
    function update(){
    
      if(!this.player || !this.player.body){
        return;
      }
      
      let body = this.player.body;
      let speed = 50;
      
      body.setVelocity(0, 0);
      
      // Since the box depth is set to "2" 
      // you just have to alternate the depth of the player
      let newDepth = (this.player.y > this.box.y) ? 3 : 1;
      
      this.player.setDepth(newDepth);
      
      if(this.keys.right.isDown){
         body.setVelocity(speed, 0);
      }
      
      if(this.keys.left.isDown){
         body.setVelocity(-speed, 0);
      }
      
      if(this.keys.up.isDown){
         body.setVelocity(0, -speed);
      }
      
      if(this.keys.down.isDown){
         body.setVelocity(0, speed);
      }
    
    }
    
    new Phaser.Game(config);
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/phaser.js"></script>