import type { Circle } from "#/engine/Circle.ts"; import type { Polygon } from "#/engine/Polygon.ts"; import { createRect, type Rect } from "#/engine/Rect.ts"; import $ from "#/engine/Scene.ts"; import { createVector, type Vector } from "#/engine/Vector.ts"; export enum Shape { Cirle, Rect, Polygon, } const CIRCLE = Shape.Cirle; const RECT = Shape.Rect; const POLYGON = Shape.Polygon; type CollisionBuffer = { size: number; mask: number; cursor: number; pool: Array; slice: Array; }; type Collide = (collision: Collision, cid: string) => void; export type Collider = { shape: Shape; body: Circle | Rect | Polygon; aabb: Rect; buffer: CollisionBuffer; }; export type Collision = { cid: string; normal: Vector; position: Vector; depth: 0; angle: 0; }; export function createCollider( shape: Shape, body: Circle | Rect | Polygon, bufferSize: number = 8, ): Collider { let aabb = createRect(createVector(), 0, 0); switch (shape) { case CIRCLE: { const radius = (body as Circle).radius; const size = radius * 2; aabb.width = size; aabb.height = size; aabb.position.x = body.position.x - radius; aabb.position.y = body.position.y - radius; break; } case RECT: { aabb = body as Rect; break; } case POLYGON: { break; } } return { shape, body, aabb, buffer: createCollisionBuffer(bufferSize), }; } const infAABB = createRect(createVector(), 0, 0); function sweptAABB(self: Collider, other: Collider, velocity: Vector): void { infAABB.position.x = other.aabb.position.x - self.aabb.width / 2; infAABB.position.y = other.aabb.position.y - self.aabb.height / 2; infAABB.width = other.aabb.width - self.aabb.width; infAABB.height = other.aabb.height - self.aabb.height; // } function collideWithCirle(collision: Collision, cid: string) { collision.cid = cid; } function collideWithRect(collision: Collision, cid: string) { collision.cid = cid; } function collideWithPolygon(collision: Collision, cid: string) { collision.cid = cid; } const circleCollide: Array = [ collideWithCirle, collideWithRect, collideWithPolygon, ]; const rectCollide: Array = [ collideWithCirle, collideWithRect, collideWithPolygon, ]; const polygonCollide: Array = [ collideWithCirle, collideWithRect, collideWithPolygon, ]; function createCollisionBuffer(size: number): CollisionBuffer { if ((size & (size - 1)) !== 0) { throw new Error("size must be a power of two"); } const pool: Array = []; for (let i = 0; i < size; i++) { pool.push(createCollision()); } return { size, mask: size - 1, cursor: 0, pool, slice: [], }; } function next(buffer: CollisionBuffer): Collision { return buffer.pool[buffer.cursor++ & buffer.mask]; } function createCollision(): Collision { return { cid: "", normal: createVector(), position: createVector(), depth: 0, angle: 0, }; } export function moveAndCollide( id: string, velocity: Vector, delta: number, ): Array { const colliders = $("collider")!; const positions = $("position")!; const self = colliders.get(id)!.value; const pos = positions.get(id)!.value; self.aabb.position.x = pos.x; self.aabb.position.y = pos.y; const slice = self.buffer.slice; slice.length = 0; for (const [cid, collider] of colliders.entries()) { if (cid === id) { continue; } const other = collider.value; const collision = next(self.buffer); switch (self.shape) { case CIRCLE: { circleCollide[other.shape](collision, cid); break; } case RECT: { rectCollide[other.shape](collision, cid); break; } case POLYGON: { polygonCollide[other.shape](collision, cid); break; } } if (collision.cid.length > 0) { slice.push(collision); } } return slice; }