Skip to content

Instantly share code, notes, and snippets.

@Jelleebeen
Last active February 4, 2024 14:24
Show Gist options
  • Select an option

  • Save Jelleebeen/fb274f5c9082626184917a8b82b7dea8 to your computer and use it in GitHub Desktop.

Select an option

Save Jelleebeen/fb274f5c9082626184917a8b82b7dea8 to your computer and use it in GitHub Desktop.
Resolving collisions using Matter physics with Phaser 3 in constant O(1) time - Port to Typescript
/*
This code shows how to resolve collisions using Matter physics with Phaser 3 in constant O(1) time.
Going through all the collision pairs is still linear O(n) time depending on how many collisions happened this frame
but the code that handles the resolution of the collision is constant and will not change with the total number of CollisionCategories / collision-lookups.
The way the code works is by generating a number based on the each of the collision combinations, use that number as a key for storing a pointer to the respective collision handler function,
and then when a collision happens, calculate the number again of both bodies' collision categories and use that number to fetch the collision handler function. Simple.
// dreasgrech - https://github.com/dreasgrech/
// TS example - Jelleebeen - https://github.com/jelleebeen/
*/
/*
When a Matter GameObject is created, its body is assigned one of the below CollisionCategories to the collision category
and its collision mask set to a combination of a number of Collision Categories.
Ex:
const collisionFilter = robotBody.collisionFilter;
collisionFilter.category = CollisionCategories.Robot;
collisionFilter.mask = CollisionCategories.RobotBody | CollisionCategories.Arena; // A Robot can collide with another robot and the arena
*/
enum CollisionCategories {
RobotBody = 1,
RobotTurret = 2,
Arena = 4,
RobotProjectile = 8,
RobotProjectileSensor = 9
};
// A type for the collision handler functions created later, to store in the collision handler hash map.
type CollisionHandler = (bodyA: MatterJS.BodyType, bodyB: MatterJS.BodyType) => void
// Collision handler hash map, holds the keys that map to the different collision handlers that can happen
const collisionHandlers: Map<number, CollisionHandler> = new Map<number, CollisionHandler>()
// This create() is called from a Phaser create() function
const create = function() {
// Set up all the collision handlers lookups. This is the matrix which allows for the collision handler resolution
collisionHandlers.set(
createLookupKey(CollisionCategories.RobotBody, CollisionCategories.RobotBody),
handleCollision_RobotToRobot
) // A robot can collide with another robot
collisionHandlers.set(
createLookupKey(CollisionCategories.RobotProjectileSensor, CollisionCategories.RobotProjectile),
handleCollision_RobotProjectileSensorToProjectile
) // A projectile can collide with a projectile sensor
collisionHandlers.set(
createLookupKey(CollisionCategories.RobotBody, CollisionCategories.Arena), handleCollision_RobotToArena
) // A robot can collide with the arena
collisionHandlers.set(
createLookupKey(CollisionCategories.RobotProjectile, CollisionCategories.RobotProjectile),
handleCollision_ProjectileToProjectile
) // A projectile can collide with another projectile
collisionHandlers.set(
createLookupKey(CollisionCategories.RobotProjectile, CollisionCategories.Arena),
handleCollision_ProjectileToArena
) // A projectile can collide with the arena
}
// The 'collisionstart' callback
const handleEvent_CollisionStart = function(event: Phaser.Physics.Matter.Events.CollisionStartEvent) {
const eventPairs = event.pairs
const eventPairsLength = eventPairs.length
for (let i = 0; i < eventPairsLength; ++i) {
const matterCollisionData = eventPairs[i]
// Fetch bodyA's collision category
const bodyA = matterCollisionData.bodyA
const bodyA_parent = bodyA.parent
const bodyA_CollisionCategory = bodyA_parent.collisionFilter.category
// Fetch bodyB's collision category
const bodyB = matterCollisionData.bodyB
const bodyB_parent = bodyB.parent
const bodyB_CollisionCategory = bodyB_parent.collisionFilter.category
// Resolve the lookup key based on the physics category
const collisionLookupKey = createLookupKey(bodyA_CollisionCategory, bodyB_CollisionCategory)
const collisionHandler = collisionHandlers.get(collisionLookupKey)
if (collisionHandler == undefined) {
console.error('Unable to find collision handler for', bodyA_CollisionCategory, 'and', bodyB_CollisionCategory, '. Key:', collisionLookupKey)
return
}
// Execute the collision handler
collisionHandler(bodyA, bodyB)
}
};
// Hook to the collisionstart event, placed in your scene and then uncommented.
//this.matter.world.on('collisionstart', handleEvent_CollisionStart);
// COLLISION HANDLERS:
// Robot to Robot collision handler
const handleCollision_RobotToRobot: CollisionHandler = function(robotBodyA, robotBodyB) {
// Both matter bodies belong to robots
// ...
}
// Robot Projectile Sensor to Projectile collision handler
const handleCollision_RobotProjectileSensorToProjectile: CollisionHandler = function(matterBodyA, matterBodyB) {
// Determine which body is which
const isBodyA_RobotProjectileSensor = matterBodyA.collisionFilter.category == CollisionCategories.RobotProjectileSensor
const robotProjectileSensorMatterBody = isBodyA_RobotProjectileSensor ? matterBodyA : matterBodyB
const projectileMatterBody = isBodyA_RobotProjectileSensor ? matterBodyB : matterBodyA
const projectileMatterGameObject = projectileMatterBody.parent.gameObject
// ...
}
// Robot to Arena collision handler
const handleCollision_RobotToArena: CollisionHandler = function(matterBodyA, matterBodyB) {
// Determine which body is which
const isBodyA_Arena = matterBodyA.collisionFilter.category == CollisionCategories.Arena
const robotMatterBody = isBodyA_Arena ? matterBodyB : matterBodyA
const arenaMatterBody = isBodyA_Arena ? matterBodyA : matterBodyB
// ...
}
// Projectile to Projectile collision handler
const handleCollision_ProjectileToProjectile: CollisionHandler = function(projectileMatterBodyA, projectileMatterBodyB) {
// Both matter bodies belong to projectiles
// ...
}
// Projectile to Arena collision handler
const handleCollision_ProjectileToArena: CollisionHandler = function(matterBodyA, matterBodyB) {
// Determine which body is which
const isBodyA_Arena = matterBodyA.collisionFilter.category == CollisionCategories.Arena
const projectileMatterBody = isBodyA_Arena ? matterBodyB : matterBodyA
const arenaBody = isBodyA_Arena ? matterBodyA : matterBodyB
// ...
}
// HELPER FUNCTIONS:
// Creates a lookup key based on two numbers.
// Inversing the numbers returns the same results
const createLookupKey = function(number1: number, number2: number) {
var key = Math.min(number1, number2) * 1000 + Math.max(number1, number2)
if (Number.isNaN(key)) {
throw `${number1} and ${number2} have created a NaN key!`
}
return key
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment