Skip to content

Commit

Permalink
Big refactor to speed up body -> entity detection.
Browse files Browse the repository at this point in the history
  • Loading branch information
Half-Shot committed Sep 16, 2024
1 parent 3e82d5c commit 62e6f49
Show file tree
Hide file tree
Showing 13 changed files with 180 additions and 127 deletions.
20 changes: 8 additions & 12 deletions src/entities/bitmapTerrain.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Composite, Body, Vector, Bodies, Query, Collision } from "matter-js";
import { Body, Vector, Bodies, Query, Collision } from "matter-js";
import { UPDATE_PRIORITY, Container, Graphics, Rectangle, Texture, Sprite } from "pixi.js";
import { IMatterEntity } from "./entity";
import { generateQuadTreeFromTerrain, imageDataToTerrainBoundaries } from "../terrain";
import Flags from "../flags";
import { GameWorld } from "../world";

export type OnDamage = () => void;
export class BitmapTerrain implements IMatterEntity {
Expand All @@ -27,11 +28,11 @@ export class BitmapTerrain implements IMatterEntity {
private readonly spriteBackdrop: Sprite;
private registeredDamageFunctions = new Map<string,OnDamage>();

static create(viewWidth: number, viewHeight: number, composite: Composite, texture: Texture) {
return new BitmapTerrain(viewWidth, viewHeight, composite, texture);
static create(viewWidth: number, viewHeight: number, gameWorld: GameWorld, texture: Texture) {
return new BitmapTerrain(viewWidth, viewHeight, gameWorld, texture);
}

private constructor(viewWidth: number, viewHeight: number, private readonly composite: Composite, texture: Texture) {
private constructor(viewWidth: number, viewHeight: number, private readonly gameWorld: GameWorld, texture: Texture) {
this.canvas = document.createElement('canvas');
this.canvas.width = viewWidth;
this.canvas.height = viewHeight;
Expand Down Expand Up @@ -87,7 +88,7 @@ export class BitmapTerrain implements IMatterEntity {

console.log("Removing", removableBodies.length, "bodies");
for (const body of removableBodies) {
Composite.remove(this.composite, body);
this.gameWorld.removeBody(body);
const key = body.position.x + "," + body.position.y;
const damageFn = this.registeredDamageFunctions.get(key);
if (damageFn) {
Expand All @@ -109,12 +110,12 @@ export class BitmapTerrain implements IMatterEntity {
// Now create the pieces
const newParts: Body[] = [];
for (const quad of quadtreeRects) {
const body = Bodies.rectangle(quad.x + this.sprite.x, quad.y + this.sprite.y, quad.width, quad.height, { isStatic: true });
const body = Bodies.rectangle(quad.x + this.sprite.x, quad.y + this.sprite.y, quad.width, quad.height, { label: 'terrain', isStatic: true });
newParts.push(body);
}
this.parts.push(...newParts);

Composite.add(this.composite, newParts);
this.gameWorld.addBody(this, ...newParts);
console.timeEnd("Generating terrain");
}

Expand Down Expand Up @@ -193,11 +194,6 @@ export class BitmapTerrain implements IMatterEntity {
this.gfx.rect(this.nearestTerrainPositionPoint.x, this.nearestTerrainPositionPoint.y, 1, 1).stroke({width: 5, color: 0xFF0000});
}

public entityOwnsBody(bodyId: number) {
// TODO: Use a set
return this.parts.some(b => b.id === bodyId);
}

public getNearestTerrainPosition(point: Vector, width: number, maxHeightDiff: number, xDirection = 0): {point?: Vector, fell: boolean} {
// This needs a rethink, we really want to have it so that the character's "platform" is visualised
// by this algorithm. We want to figure out if we can move left or right, and if not if we're going to fall.
Expand Down
5 changes: 4 additions & 1 deletion src/entities/entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,15 @@ export interface IGameEntity {
export interface IMatterEntity extends IGameEntity {
// TODO: Wrong shape?
explodeHandler?: (point: Vector, radius: number) => void;
entityOwnsBody(bodyId: number): boolean;
/**
*
* @param other
* @param contactPoint
* @returns True if the collision should stop being processed
*/
onCollision?(other: IMatterEntity, contactPoint: Vector): boolean;
}

export interface IMatterPluginInfo {
wormgineEntity: IMatterEntity,
}
13 changes: 7 additions & 6 deletions src/entities/phys/bazookaShell.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Container, Graphics, Sprite, Texture } from 'pixi.js';
import grenadePaths from "../../assets/bazooka.svg";
import { Body, Bodies, Composite, Vector } from "matter-js";
import { Body, Bodies, Vector } from "matter-js";
import { TimedExplosive } from "./timedExplosive";
import { loadSvg } from '../../loadSvg';
import { Game } from '../../game';

Check warning on line 6 in src/entities/phys/bazookaShell.ts

View workflow job for this annotation

GitHub Actions / lint

'Game' is defined but never used
import { GameWorld } from '../../world';

// TODO: This is buggy as all hell.

Expand All @@ -14,22 +15,22 @@ export class BazookaShell extends TimedExplosive {
private readonly force: Vector = Vector.create(0);
private readonly gfx = new Graphics();

static async create(game: Game, parent: Container, composite: Composite, position: {x: number, y: number}, initialAngle: number, initialForce: number, wind: number) {
const ent = new BazookaShell(game, position, await BazookaShell.bodyVertices, initialAngle, composite, initialForce, wind);
Composite.add(composite, ent.body);
static async create(parent: Container, gameWorld: GameWorld, position: {x: number, y: number}, initialAngle: number, initialForce: number, wind: number) {
const ent = new BazookaShell(position, await BazookaShell.bodyVertices, initialAngle, gameWorld, initialForce, wind);
gameWorld.addBody(ent, ent.body);
parent.addChild(ent.sprite);
parent.addChild(ent.wireframe.renderable);
console.log("New zooka", ent.body);
console.log(ent.sprite.x, ent.sprite.position.x);
return ent;
}

private constructor(game: Game, position: { x: number, y: number }, bodyVerticies: Vector[][], initialAngle: number, composite: Composite, initialForce: number, private readonly wind: number) {
private constructor(position: { x: number, y: number }, bodyVerticies: Vector[][], initialAngle: number, gameWorld: GameWorld, initialForce: number, private readonly wind: number) {
const body = Bodies.fromVertices(position.x, position.y, bodyVerticies, {
position,
});
const sprite = new Sprite(BazookaShell.texture);
super(game, sprite, body, composite, {
super(sprite, body, gameWorld, {
explosionRadius: 100,
explodeOnContact: true,
timerSecs: 30,
Expand Down
15 changes: 8 additions & 7 deletions src/entities/phys/grenade.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import { Container, Sprite, Text, Texture, Ticker } from 'pixi.js';
import grenadePaths from "../../assets/grenade.svg";
import { Bodies, Composite, Vector } from "matter-js";
import { Bodies, Vector } from "matter-js";
import { TimedExplosive } from "./timedExplosive";
import { IMatterEntity } from '../entity';
import { BitmapTerrain } from '../bitmapTerrain';
import { IMediaInstance, Sound } from '@pixi/sound';
import { loadSvg } from '../../loadSvg';
import { Game } from '../../game';
import { GameWorld } from '../../world';

/**
* Standard grenade projectile.
Expand All @@ -20,9 +20,8 @@ export class Grenade extends TimedExplosive {
public static bounceSoundsLight: Sound;
public static boundSoundHeavy: Sound;

static async create(game: Game, parent: Container, composite: Composite, position: {x: number, y: number}, initialForce: { x: number, y: number}) {
const ent = new Grenade(game, position, await Grenade.bodyVertices, initialForce, composite);
Composite.add(composite, ent.body);
static async create(parent: Container, world: GameWorld, position: {x: number, y: number}, initialForce: { x: number, y: number}) {
const ent = new Grenade(position, await Grenade.bodyVertices, initialForce, world);
parent.addChild(ent.sprite, ent.wireframe.renderable);
return ent;
}
Expand All @@ -34,19 +33,21 @@ export class Grenade extends TimedExplosive {
}
public bounceSoundPlayback?: IMediaInstance;

private constructor(game: Game, position: { x: number, y: number }, bodyVerticies: Vector[][], initialForce: { x: number, y: number}, parent: Composite) {
private constructor(position: { x: number, y: number }, bodyVerticies: Vector[][], initialForce: { x: number, y: number}, world: GameWorld) {
const body = Bodies.fromVertices(position.x, position.y, bodyVerticies, {
sleepThreshold: 60*(5+2),
friction: Grenade.FRICTION,
restitution: Grenade.RESITITUTION,
density: Grenade.DENSITY,
isSleeping: false,
isStatic: false,
label: "Grenade",
});
console.log("Created grenade body", body.id);
const sprite = new Sprite(Grenade.texture);
sprite.scale.set(0.5, 0.5);
sprite.anchor.set(0.5, 0.5);
super(game, sprite, body, parent, {
super(sprite, body, world, {
explosionRadius: 60,
explodeOnContact: false,
timerSecs: 3,
Expand Down
13 changes: 7 additions & 6 deletions src/entities/phys/physicsEntity.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Composite, Body, Vector } from "matter-js";

Check warning on line 1 in src/entities/phys/physicsEntity.ts

View workflow job for this annotation

GitHub Actions / lint

'Composite' is defined but never used
import { UPDATE_PRIORITY, Sprite } from "pixi.js";
import { IMatterEntity } from "../entity";
import { IMatterEntity, IMatterPluginInfo } from "../entity";
import { Water } from "../water";
import { BodyWireframe } from "../../mixins/bodyWireframe.";
import globalFlags from "../../flags";
import { IMediaInstance, Sound } from "@pixi/sound";
import { GameWorld } from "../../world";

/**
* Any object that is physically present in the world i.e. a worm.
Expand All @@ -27,20 +28,20 @@ export abstract class PhysicsEntity implements IMatterEntity {
return this.body.id === bodyId || this.body.parent.id === bodyId;
}

constructor(public readonly sprite: Sprite, protected body: Body, protected parent: Composite) {
constructor(public readonly sprite: Sprite, protected body: Body, protected gameWorld: GameWorld) {
this.wireframe = new BodyWireframe(this.body, globalFlags.DebugView);
globalFlags.on('toggleDebugView', (on) => {
this.wireframe.enabled = on;
})
});
(body.plugin as IMatterPluginInfo).wormgineEntity = this;
}

destroy(): void {
console.log('destroyed');
if (this.parent) {
Composite.remove(this.parent, this.body);
}
this.sprite.destroy();
this.wireframe.renderable.destroy();
this.gameWorld.removeBody(this.body);
this.gameWorld.removeEntity(this);
}

update(dt: number): void {
Expand Down
24 changes: 10 additions & 14 deletions src/entities/phys/timedExplosive.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { Composite, Body, Vector, Bodies, Query } from "matter-js";

Check warning on line 1 in src/entities/phys/timedExplosive.ts

View workflow job for this annotation

GitHub Actions / lint

'Composite' is defined but never used

Check warning on line 1 in src/entities/phys/timedExplosive.ts

View workflow job for this annotation

GitHub Actions / lint

'Query' is defined but never used
import { UPDATE_PRIORITY, Ticker, Sprite } from "pixi.js";
import { BitmapTerrain } from "../bitmapTerrain";
import { IMatterEntity } from "../entity";
import { IMatterEntity, IMatterPluginInfo } from "../entity";

Check warning on line 4 in src/entities/phys/timedExplosive.ts

View workflow job for this annotation

GitHub Actions / lint

'IMatterPluginInfo' is defined but never used
import { PhysicsEntity } from "./physicsEntity";
import { Game } from "../../game";

Check warning on line 6 in src/entities/phys/timedExplosive.ts

View workflow job for this annotation

GitHub Actions / lint

'Game' is defined but never used
import { Explosion } from "../explosion";
import { GameWorld } from "../../world";

interface Opts {
explosionRadius: number,
Expand All @@ -22,13 +23,14 @@ export abstract class TimedExplosive extends PhysicsEntity implements IMatterEnt

priority: UPDATE_PRIORITY = UPDATE_PRIORITY.NORMAL;

constructor(private readonly game: Game, sprite: Sprite, body: Body, parent: Composite, public readonly opts: Opts) {
super(sprite, body, parent)
constructor(sprite: Sprite, body: Body, gameWorld: GameWorld, public readonly opts: Opts) {
super(sprite, body, gameWorld);
this.gameWorld.addBody(this, body);
this.timer = Ticker.targetFPMS * opts.timerSecs * 1000;
}

onTimerFinished() {
if (!this.body || !this.parent) {
if (!this.body || !this.gameWorld) {
throw Error('Timer expired without a body');
}
this.onExplode();
Expand All @@ -38,17 +40,11 @@ export abstract class TimedExplosive extends PhysicsEntity implements IMatterEnt
const point = this.body.position;
const radius = this.opts.explosionRadius;
// Detect if anything is around us.
const circ = Bodies.circle(point.x, point.y, radius);
const hit = Query.collides(circ, this.game.matterEngine.world.bodies).sort((a,b) => b.depth - a.depth);
this.game.addEntity(Explosion.create(this.game.viewport, point, radius, 15, 35));
console.log("Timed explosive hit", hit);
const hitOtherEntity = this.gameWorld.checkCollision(Bodies.circle(point.x, point.y, radius), this);
this.gameWorld.addEntity(Explosion.create(this.gameWorld.viewport, point, radius, 15, 35));
// Find contact point with any terrain
for (const hitBody of hit) {
const ents = (this.game.findEntityByBodies(hitBody.bodyA, hitBody.bodyB)).filter(e => e !== this);
if (ents[0]) {
// TODO: Cheating massively
this.onCollision(ents[0], hitBody.bodyA.position);
}
if (hitOtherEntity) {
this.onCollision(hitOtherEntity, point);
}

}
Expand Down
14 changes: 7 additions & 7 deletions src/entities/phys/worm.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Container, Sprite, Texture } from 'pixi.js';
import { Body, Bodies, Composite, Vector, Sleeping } from "matter-js";

Check warning on line 2 in src/entities/phys/worm.ts

View workflow job for this annotation

GitHub Actions / lint

'Composite' is defined but never used
import { IMatterEntity } from '../entity';
import { IMatterEntity, IMatterPluginInfo } from '../entity';

Check warning on line 3 in src/entities/phys/worm.ts

View workflow job for this annotation

GitHub Actions / lint

'IMatterPluginInfo' is defined but never used
import { BitmapTerrain } from '../bitmapTerrain';
import { PhysicsEntity } from './physicsEntity';
import { IWeaponDefiniton } from '../../weapons/weapon';
import { WeaponGrenade } from '../../weapons/grenade';
import Controller, { InputKind } from '../../input';
import { GameWorld } from '../../world';

enum WormState {
Idle = 0,
Expand All @@ -29,9 +30,9 @@ export class Worm extends PhysicsEntity {
private terrainPosition: Vector = Vector.create(0,0);
private facingAngle = 0;

static async create(parent: Container, composite: Composite, position: { x: number, y: number }, terrain: BitmapTerrain, onFireWeapon: FireWeaponFn) {
const ent = new Worm(position, composite, terrain, onFireWeapon);
Composite.add(composite, ent.body);
static async create(parent: Container, world: GameWorld, position: { x: number, y: number }, terrain: BitmapTerrain, onFireWeapon: FireWeaponFn) {
const ent = new Worm(position, world, terrain, onFireWeapon);
world.addBody(ent, ent.body);
parent.addChild(ent.sprite);
parent.addChild(ent.wireframe.renderable);
return ent;
Expand All @@ -41,7 +42,7 @@ export class Worm extends PhysicsEntity {
return this.body.position;
}

private constructor(position: { x: number, y: number }, composite: Composite,
private constructor(position: { x: number, y: number }, world: GameWorld,
private readonly terrain: BitmapTerrain,
private readonly onFireWeapon: FireWeaponFn,
) {
Expand All @@ -62,9 +63,8 @@ export class Worm extends PhysicsEntity {
// timeScale: 1,
// density: 50,
});
super(sprite, body, composite);
super(sprite, body, world);
this.state = WormState.InMotion;
this.parent = composite;

// TODO: Unbind.
Controller.on('inputBegin', (inputKind: InputKind) => {
Expand Down
6 changes: 3 additions & 3 deletions src/entities/water.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Container, Filter, Geometry, Mesh, Shader, UPDATE_PRIORITY } from "pixi
import { IGameEntity } from "./entity";
import vertex from '../shaders/water.vert?raw';
import fragment from '../shaders/water.frag?raw';
import { GameWorld } from "../world";

export class Water implements IGameEntity {
public readonly priority: UPDATE_PRIORITY = UPDATE_PRIORITY.LOW;
Expand Down Expand Up @@ -34,7 +35,6 @@ export class Water implements IGameEntity {
]
}
});
console.log(indexBuffer);
this.geometry = new Geometry({
attributes: {
aPosition: [
Expand Down Expand Up @@ -67,9 +67,9 @@ export class Water implements IGameEntity {
this.waterMesh.scale.set(14, 2);
}

async create(parent: Container, engine: Composite) {
async create(parent: Container, world: GameWorld) {
parent.addChild(this.waterMesh);
Composite.add(engine, this.body);
world.addBody(this, this.body);
}

update(): void {
Expand Down
Loading

0 comments on commit 62e6f49

Please sign in to comment.