I’m trying to build hitboxes which are active only during an animation. To achieve this, I created a compound body with sensors for each hitbox. When a key is pressed, these hitboxes ‘turn on’, which is modelled by listening for a collision event between the sensor and objects in the world. I manage the collision detection via the PhaserMatterCollisionPlugin
plugin (link), then apply the force to the collided object during callback. However, the force is not being applied at all, and I don’t understand why. Any recommendations?
P.S.: I’ve seen this suggestion for using collision categories to achieve dynamic hitboxes. I will revert to that solution if I need to. However, I would like to understand what I’m doing wrong in my use of PhaserMatterCollisionPlugin
.
Here is my code so far:
Define character with hitboxes
/*
Code snippet from the constructor of my `Player` class.
`Player` is instantiate during `create()`
*/
this.sprite = scene.matter.add.sprite(0, 0, spriteKey, 0);
const { Body, Bodies } = Phaser.Physics.Matter.Matter; // native Matter modules
const { width: w, height: h } = this.sprite;
const mainBody = Bodies.rectangle(w / 2, h / 2, w * 0.3, h * 0.6, { chamfer: { radius: 5 } });
// ^ model as rectangular body with rounded corners (chamfers)
// Regions outside of mainBody will have sensors attached to them, but no collision hitboxes.
this.sensors = {
bottom: Bodies.rectangle(w / 2, 0.8 * h, w * 0.25, 2, { isSensor: true }),
left: Bodies.rectangle(0.33 * w, 0.5 * h, w / 16, h / 2, { isSensor: true }),
right: Bodies.rectangle(0.66 * w, 0.5 * h, w / 16, h / 2, { isSensor: true }),
};
// We also create sword hitboxes which extend outwards from the main body. These hitboxes
// will only be activated during the 'swing' animation.
this.hitboxes = {
swordLeft: Bodies.circle(0.23 * w, 0.6 * h, 9, { density: 0, isSensor: true }),
swordRight: Bodies.circle(0.70 * w, 0.6 * h, 9, { density: 0, isSensor: true }),
}
// Put all parts of the body together
const compoundBody = Body.create({
parts: [
mainBody,
this.sensors.bottom,
this.sensors.left,
this.sensors.right,
this.hitboxes.swordLeft,
this.hitboxes.swordRight
],
frictionStatic: 0.5,
frictionAir: 0.02,
friction: 0.1
});
this.sprite.setExistingBody(compoundBody)
Define object I want to collide with
/*
Snippet inside `create()`
/*
this.bomb = this.matter.add.sprite(50, 32, 'bomb', 0);
this.bomb.setBody({
type: 'polygon',
sides: 6,
radius: 8
});
this.bomb.ignoreGravity = true;
this.bomb.setBounce(1);
this.bomb.setVelocity(Phaser.Math.Between(-5, 5), 3);
this.bomb.setFriction(0, 0, 0);
Apply force on collision
export class SwingState extends State {
enter(scene, player) {
const sprite = player.sprite; // short-hand
this.swordHitbox = (player.direction === 'left') ? player.hitboxes.swordLeft : player.hitboxes.swordRight;
this.swingForce = 1;
this.swingAngle = 30;
sprite.setVelocityX(0);
sprite.anims.play(`swing-${player.direction}`);
// Check for collision with sword hitbox
scene.matterCollision.addOnCollideActive({
objectA: this.swordHitbox,
objectB: scene.bomb, // matter.sprite which I want to apply force to
callback: this.hitWithSword,
context: this
});
sprite.once('animationcomplete', () => {
// Stop tracking collision
scene.matterCollision.removeOnCollideActive({
objectA: this.swordHitbox,
objectB: scene.bomb,
callback: this.hitWithSword,
context: this
});
// ... transition to 'idle' state
});
}
hitWithSword({ bodyA, gameObjectA, bodyB, gameObjectB, pair }) {
gameObjectB.applyForce({
x: Math.cos(this.swingAngle) * this.swingForce,
y: Math.sin(this.swingAngle) * this.swingForce
});
// Nothing happens here!
}
}