top of page
Search
cedarcantab

Pixel Perfect Tile Collision in Phaser 3, Part 2

Updated: May 15, 2022

Introducing gravity and bounce


In the previous post I got as far as recreating basic tile collisions for my CozyPhysics engine. The collision resopnse was simple - to kill of all velocity. In other words when the physics body collided with a tile, it stopped. In this post I talk about how I introduced collision response in the form of "bounce" and also introduced gravity into the world.


Also, together with the introduction of gravity, I introduce jumping into the example code.



Changing the collision response


In the previous version of the code, collision response was simply to set velocity to zero. With the introduction of "bounce", we need to introduce a property of the physics body that determines the "bounciness" (coefficient of restitution) and the responseX and responseY code. The change required is quite straightforward - all we need to do is to reverse the velocity direction and multiply it by the "bounciness" factor. It is as simple as:


 this.velocity.x = -this.velocity.x * this.bounce.x;

However, I have created it in the same way as the Phaser 3 code, by checking for the bounce.x === 0 or not. The result is the same without this check, so I am not sure why this is included.


 responseX() {
    if (this.bounce.x === 0)
    {
      this.velocity.x = 0;
    }
   else 
    {
      this.velocity.x = -this.velocity.x * this.bounce.x;
    }
    this.remainder.x = 0; // kill off any spurious velocity
  }

We need a method for the physics body to actually set the bounce, which is simply below.


  setBounce(x,y) {
    this.bounce.set(x,y)
  }

The gravity of the world is set from the main game scene, calling the setGravity method.


   this.world = new CozyPhysics(this);
   this.world.setGravity(500);

Separating the checkWorldBounds into the x-component and y-component


In the previous version of the code, there was one method checkWorldBounds which checked the horizontal and vertical boundaries in one go, before the processX and processY methods were called. I have now included the boundary checks within the processX and process Y methods.


At the same time, I have cleaned up the boundary checking code a little bit + added another property to CozyBody called checkCollision, just like Phaser arcade physics body (as this will be useful when we come to make the slopes demo).



  processX(body) {
    // check world boundary collision against the vertical walls
    if (body.newPosition.x < this.bounds.x) 
    {
      body.delta.x = this.bounds.x - body.position.x;
      body.responseX();
      return
    }
    else if (body.newPosition.x + body.width > this.bounds.right ) 
    {
      body.delta.x = (this.bounds.right - body.width) - body.position.x;
      body.responseX();
      return
    }
    // tile collision check & separation
    const hitBoxSide = (body.delta.x > 0) ? body.newPosition.x + body.width - 1 : body.newPosition.x;
    const tileToCheck = this.getTilesOnLine(hitBoxSide, body.top, hitBoxSide, body.bottom);

    if (tileToCheck.length > 0) {
      
      if ((body.delta.x > 0) && (tileToCheck.some(e=>e.collideLeft)) && body.checkCollision.right)
      { 
        body.delta.x = (Math.min(...tileToCheck.map(i=>i.pixelX)) - body.width) - body.position.x;
        body.responseX();
      }
      else if ((body.delta.x < 0) && (tileToCheck.some(e=>e.collideRight)) && body.checkCollision.left)
      {
        body.delta.x = (Math.max(...tileToCheck.map(i=>i.right))) - body.position.x;
        body.responseX();
    
      }
    }
  }


Controlling the bounce and gravity


You will see that I have added a hackey adjustment to the y-velocity in the upward direction upon bounce. I have had to put this in, otherwise when the bounce.y factor is set to a non-zero number, y-component of the velocity never reaches zero and even when it becomes negligible and the body is apparently stationary on a tile, onFloor property sporadically switches between true and false, making it difficult for the player to "jump". I saw no such adjustment in the Phaser arcade physics engine, so I can only assume that this happens because of the "whole-pixel" adjustment I am making in the code elsewhere; ie because "non-integer" element of velocity remaining is accumulated.


 responseY() {
    if (this.bounce.y === 0)
    {     
      this.velocity.y = 0; 
    }
    else
    {
      this.velocity.y = -this.velocity.y * this.bounce.y;
      if (this.velocity.y < 0 && Math.abs(this.velocity.y) < 2) this.velocity.y = 0;
    }    
    this.remainder.y = 0; // kill off any spurious velocity
  }


And with that, CozyPhysics engine version 2 is complete. Here's the demo code.



"Compatibility" with Phaser Arcade Physics engine


You would have noticed by now that the I have named the various methods associated with my Cozy Physics engine the same as Phaser's arcade physics engine. Whilst this makes it impossible to use both this engine and the arcade physics engine in parallel (of course you can use the logic/code by changing the method names), I did so because it makes it easy for me to test it, by seeing whether code I created originally using Phaser arcade physics works.


And so far, it does. Like the demo code below. This definitely does not use Phaser arcade physics, but behaves, as far as I can tell, exactly the same.






Version 2A: allowing sub-pixel movement


The logic is exactly the same, except there is none of the sub-pixel adjustment code. Also, I the right and bottom properties of the body is set in the same way as Phaser arcade physics; ie


CozyPhysics: body.right = body.position.x + body.width - 1;

Phaser arcade physics: body.right = body.position.x + body.width;


The code is, quite frankly, a lot simpler.


And, as expected, it behaves very similar to the Phaser 3 arcade physics version, presented in the very first post of these series.








24 views0 comments

Comments


記事: Blog2_Post
bottom of page