Skip to content

Commit

Permalink
feat: Hit test and collision between blocks / block and ball
Browse files Browse the repository at this point in the history
  • Loading branch information
NriotHrreion committed Nov 10, 2024
1 parent eaf85f8 commit 3dbf15c
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 38 deletions.
13 changes: 4 additions & 9 deletions src/simulator/hitbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,7 @@ import type { Point } from "./render/render";
import { Emitter, type Event } from "@/common/event";
import { Disposable, type IDisposable } from "@/common/lifecycle";

export interface OnHitListenerData {
obj: CanvasObject
depth: number
}

export interface IHitbox extends IDisposable {
export interface IHitbox<D> extends IDisposable {
anchor: Point

/**
Expand All @@ -33,12 +28,12 @@ export interface IHitbox extends IDisposable {
*/
cancelNextTest(): void

onHit: Event<OnHitListenerData>
onHit: Event<D & { obj: CanvasObject }>
}

export abstract class Hitbox extends Disposable implements IHitbox {
export abstract class Hitbox<D = any> extends Disposable implements IHitbox<D> {
// events
protected _onHit = new Emitter<OnHitListenerData>();
protected _onHit: Emitter<D & { obj: CanvasObject }> = new Emitter();

protected _isNextTestCancelled: boolean = false;

Expand Down
53 changes: 49 additions & 4 deletions src/simulator/hitboxes/convexHitbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,17 @@ import { Hitbox, type IHitbox } from "@/simulator/hitbox";

import { RoundHitbox } from "./roundHitbox";

interface IConvexHitbox extends IHitbox {
interface OnHitListenerData {
overlayX: number
overlayY: number
}

interface IConvexHitbox extends IHitbox<OnHitListenerData> {
width: number
height: number
}

export class ConvexHitbox extends Hitbox implements IConvexHitbox {
export class ConvexHitbox extends Hitbox<OnHitListenerData> implements IConvexHitbox {

public constructor(
public width: number,
Expand All @@ -36,7 +41,27 @@ export class ConvexHitbox extends Hitbox implements IConvexHitbox {
this.anchor.y > hitbox.anchor.y - this.height &&
this.anchor.y < hitbox.anchor.y + hitbox.height
) {
this._onHit.fire({ obj, depth: 0 });
this._onHit.fire({
obj,
overlayX: (
this.anchor.x === hitbox.anchor.x
? 0
: (
this.anchor.x > hitbox.anchor.x
? -(hitbox.width - this.anchor.x + hitbox.anchor.x)
: this.width - hitbox.anchor.x + this.anchor.x
)
),
overlayY: (
this.anchor.y === hitbox.anchor.y
? 0
: (
this.anchor.y > hitbox.anchor.y
? -(hitbox.height - this.anchor.y + hitbox.anchor.y)
: this.height - hitbox.anchor.y + this.anchor.y
)
)
});
}
} else if(hitbox instanceof RoundHitbox) {
const diameter = 2 * hitbox.radius;
Expand All @@ -47,7 +72,27 @@ export class ConvexHitbox extends Hitbox implements IConvexHitbox {
hitbox.anchor.y > this.anchor.y - diameter &&
hitbox.anchor.y < this.anchor.y + this.height
) {
this._onHit.fire({ obj, depth: 0 });
this._onHit.fire({
obj,
overlayX: (
this.anchor.x === hitbox.anchor.x
? 0
: (
this.anchor.x > hitbox.anchor.x
? -(diameter - this.anchor.x + hitbox.anchor.x)
: this.width - hitbox.anchor.x + this.anchor.x
)
),
overlayY: (
this.anchor.y === hitbox.anchor.y
? 0
: (
this.anchor.y > hitbox.anchor.y
? -(diameter - this.anchor.y + hitbox.anchor.y)
: this.height - hitbox.anchor.y + this.anchor.y
)
)
});
}
}
}
Expand Down
8 changes: 6 additions & 2 deletions src/simulator/hitboxes/groundHitbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,15 @@ import { Block } from "@/simulator/objects/block";

import { RoundHitbox } from "./roundHitbox";

interface IGroundHitbox extends IHitbox {
interface OnHitListenerData {
depth: number
}

interface IGroundHitbox extends IHitbox<OnHitListenerData> {

}

export class GroundHitbox extends Hitbox implements IGroundHitbox {
export class GroundHitbox extends Hitbox<OnHitListenerData> implements IGroundHitbox {
public constructor(anchor: Point) { // anchor = (0, canvas.height - GROUND_HEIGHT)
super(anchor);
}
Expand Down
9 changes: 7 additions & 2 deletions src/simulator/hitboxes/roundHitbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@ import type { Canvas } from "@/ui/canvas/canvas";
import { Hitbox, type IHitbox } from "@/simulator/hitbox";
import { getPointDistance } from "@/common/utils/utils";

interface IRoundHitbox extends IHitbox {
interface OnHitListenerData {
depth: number
}

interface IRoundHitbox extends IHitbox<OnHitListenerData> {
radius: number
}

export class RoundHitbox extends Hitbox implements IRoundHitbox {
export class RoundHitbox extends Hitbox<OnHitListenerData> implements IRoundHitbox {

public constructor(public radius: number, anchor: Point) {
super(anchor);

Expand Down
30 changes: 24 additions & 6 deletions src/simulator/objects/ball.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,18 +85,36 @@ export class Ball extends CanvasObject<RoundHitbox> {
const massSum = this.mass + obj.mass;
/** *m1 - m2* */
const massDiff = this.mass - obj.mass;

const vx1 = this.velocity.getComponent(new Vector(1, 0));
const vy1 = this.velocity.getComponent(new Vector(0, 1));
const vx2 = obj.velocity.getComponent(new Vector(1, 0));
const vy2 = obj.velocity.getComponent(new Vector(0, 1));

// X direction

/** *((m2 - m1) v2) / (m1 + m2)* */
const va = Vector.multiplyScalar(obj.velocity, -massDiff / massSum);
const vxa = Vector.multiplyScalar(vx2, -massDiff / massSum);
/** *(2 m1 v1) / (m1 + m2)* */
const vxb = Vector.multiplyScalar(vx1, (2 * this.mass) / massSum);
/** *((m1 - m2) v1) / (m1 + m2)* */
const vxc = Vector.multiplyScalar(vx1, massDiff / massSum);
/** *(2 m2 v2) / (m1 + m2)* */
const vxd = Vector.multiplyScalar(vx2, (2 * obj.mass) / massSum);

// Y direction

/** *((m2 - m1) v2) / (m1 + m2)* */
const vya = Vector.multiplyScalar(vy2, -massDiff / massSum);
/** *(2 m1 v1) / (m1 + m2)* */
const vb = Vector.multiplyScalar(this.velocity, (2 * this.mass) / massSum);
const vyb = Vector.multiplyScalar(vy1, (2 * this.mass) / massSum);
/** *((m1 - m2) v1) / (m1 + m2)* */
const vc = Vector.multiplyScalar(this.velocity, massDiff / massSum);
const vyc = Vector.multiplyScalar(vy1, massDiff / massSum);
/** *(2 m2 v2) / (m1 + m2)* */
const vd = Vector.multiplyScalar(obj.velocity, (2 * obj.mass) / massSum);
const vyd = Vector.multiplyScalar(vy2, (2 * obj.mass) / massSum);

obj.velocity = Vector.add(va, vb); // v2'
this.velocity = Vector.add(vc, vd); // v1'
obj.velocity = Vector.add(Vector.add(vxa, vxb), Vector.add(vya, vyb)); // v2'
this.velocity = Vector.add(Vector.add(vxc, vxd), Vector.add(vyc, vyd)); // v1'
}
}));

Expand Down
48 changes: 33 additions & 15 deletions src/simulator/objects/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,22 +55,22 @@ export class Block extends CanvasObject<ConvexHitbox> {
}));
this.applyGravity();

this._register(this.hitbox.onHit(({ obj, depth }) => {
this._register(this.hitbox.onHit(({ obj, overlayX, overlayY }) => {
if(obj instanceof Block || obj instanceof Ball) {
obj.hitbox.cancelNextTest();

/**
* To prevent objects from going through each other
*/

const p1 = this.hitbox.anchor;
const p2 = obj.hitbox.anchor;
const movement = Vector.multiplyScalar(Vector.fromPoints(p1, p2).getUnitVector(), depth);

this.obj.x -= movement.x / 2;
this.obj.y -= movement.y / 2;
obj.obj.x += movement.x / 2;
obj.obj.y += movement.y / 2;
// Test which side the hit happens (left right side / top side)
if(Math.abs(overlayX) < Math.abs(overlayY) && overlayX !== 0) {
this.obj.x -= overlayX / 2;
obj.obj.x += overlayX / 2;
} else {
this.obj.y -= overlayY / 2;
obj.obj.y += overlayY / 2;
}

this.updateHitboxAnchor();
obj.updateHitboxAnchor();
Expand All @@ -87,17 +87,35 @@ export class Block extends CanvasObject<ConvexHitbox> {
/** *m1 - m2* */
const massDiff = this.mass - obj.mass;

const vx1 = this.velocity.getComponent(new Vector(1, 0));
const vy1 = this.velocity.getComponent(new Vector(0, 1));
const vx2 = obj.velocity.getComponent(new Vector(1, 0));
const vy2 = obj.velocity.getComponent(new Vector(0, 1));

// X direction

/** *((m2 - m1) v2) / (m1 + m2)* */
const vxa = Vector.multiplyScalar(vx2, -massDiff / massSum);
/** *(2 m1 v1) / (m1 + m2)* */
const vxb = Vector.multiplyScalar(vx1, (2 * this.mass) / massSum);
/** *((m1 - m2) v1) / (m1 + m2)* */
const vxc = Vector.multiplyScalar(vx1, massDiff / massSum);
/** *(2 m2 v2) / (m1 + m2)* */
const vxd = Vector.multiplyScalar(vx2, (2 * obj.mass) / massSum);

// Y direction

/** *((m2 - m1) v2) / (m1 + m2)* */
const va = Vector.multiplyScalar(obj.velocity, -massDiff / massSum);
const vya = Vector.multiplyScalar(vy2, -massDiff / massSum);
/** *(2 m1 v1) / (m1 + m2)* */
const vb = Vector.multiplyScalar(this.velocity, (2 * this.mass) / massSum);
const vyb = Vector.multiplyScalar(vy1, (2 * this.mass) / massSum);
/** *((m1 - m2) v1) / (m1 + m2)* */
const vc = Vector.multiplyScalar(this.velocity, massDiff / massSum);
const vyc = Vector.multiplyScalar(vy1, massDiff / massSum);
/** *(2 m2 v2) / (m1 + m2)* */
const vd = Vector.multiplyScalar(obj.velocity, (2 * obj.mass) / massSum);
const vyd = Vector.multiplyScalar(vy2, (2 * obj.mass) / massSum);

obj.velocity = Vector.add(va, vb); // v2'
this.velocity = Vector.add(vc, vd); // v1'
obj.velocity = Vector.add(Vector.add(vxa, vxb), Vector.add(vya, vyb)); // v2'
this.velocity = Vector.add(Vector.add(vxc, vxd), Vector.add(vyc, vyd)); // v1'
}
}));

Expand Down

0 comments on commit 3dbf15c

Please sign in to comment.