From f65951830d322019734898b1b2ccabfd85e31dde Mon Sep 17 00:00:00 2001 From: Schluffe Date: Sun, 22 Mar 2026 17:49:12 +0100 Subject: [PATCH] 1st working swept --- engine/Rect.ts | 17 +++--- engine/system/physics.ts | 91 +++++++++++++++++++++++++++------ game/entities/hero/entity.ts | 16 ++++-- game/entities/portal/entity.ts | 6 ++- game/scenes/testScene/data.json | 4 +- 5 files changed, 101 insertions(+), 33 deletions(-) diff --git a/engine/Rect.ts b/engine/Rect.ts index 3a2d1a8..64d3d8d 100644 --- a/engine/Rect.ts +++ b/engine/Rect.ts @@ -38,10 +38,10 @@ export function strokeRect( ctx.globalAlpha = alpha; ctx.lineWidth = lineWidth; ctx.strokeRect( - rect.position.x - 0.5, - rect.position.y - 0.5, - rect.width, - rect.height, + rect.position.x + 0.5, + rect.position.y + 0.5, + rect.width - 1, + rect.height - 1, ); ctx.restore(); } @@ -53,13 +53,8 @@ export function fillRect( alpha: number = 1.0, ): void { ctx.save(); - ctx.strokeStyle = color; + ctx.fillStyle = color; ctx.globalAlpha = alpha; - ctx.fillRect( - rect.position.x - 0.5, - rect.position.y - 0.5, - rect.width, - rect.height, - ); + ctx.fillRect(rect.position.x, rect.position.y, rect.width, rect.height); ctx.restore(); } diff --git a/engine/system/physics.ts b/engine/system/physics.ts index 38d5ec8..2a45115 100644 --- a/engine/system/physics.ts +++ b/engine/system/physics.ts @@ -1,8 +1,9 @@ import type { Circle } from "#/engine/Circle.ts"; import type { Polygon } from "#/engine/Polygon.ts"; -import { createRect, type Rect } from "#/engine/Rect.ts"; +import { createRect, fillRect, type Rect, strokeRect } from "#/engine/Rect.ts"; import $ from "#/engine/Scene.ts"; import { createVector, type Vector } from "#/engine/Vector.ts"; +import { viewport } from "#/engine/Viewport.ts"; export enum Shape { Cirle, @@ -22,7 +23,13 @@ type CollisionBuffer = { slice: Array; }; -type Collide = (collision: Collision, cid: string) => void; +type Collide = ( + collision: Collision, + cid: string, + self: Collider, + velocity: Vector, + other: Collider, +) => void; export type Collider = { shape: Shape; @@ -73,25 +80,77 @@ export function createCollider( }; } -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; +const inflAABB = createRect(createVector(), 0, 0); +function sweptAABB( + self: Collider, + velocity: Vector, + other: Collider, +): null | number { + inflAABB.position.x = other.aabb.position.x - self.aabb.width; + inflAABB.position.y = other.aabb.position.y - self.aabb.height; + inflAABB.width = other.aabb.width + self.aabb.width; + inflAABB.height = other.aabb.height + self.aabb.height; - // + let txNear = (inflAABB.position.x - self.aabb.position.x) / velocity.x; + let txFar = + (inflAABB.position.x + inflAABB.width - self.aabb.position.x) / velocity.x; + + let tyNear = (inflAABB.position.y - self.aabb.position.y) / velocity.y; + let tyFar = + (inflAABB.position.y + inflAABB.height - self.aabb.position.y) / velocity.y; + + if (txNear > txFar) { + [txNear, txFar] = [txFar, txNear]; + } + if (tyNear > tyFar) { + [tyNear, tyFar] = [tyFar, tyNear]; + } + + // no collision at all + if (txNear > tyFar || tyNear > txFar) { + return null; + } + + const thNear = Math.max(txNear, tyNear); + const thFar = Math.min(txFar, tyFar); + + // thMax < 0, collision is behind a frame + // thMin > 1, collision is ahead a frame + if (thFar < 0 || thNear > 1) { + return null; + } + + return thNear; } -function collideWithCirle(collision: Collision, cid: string) { +function collideWithCirle( + collision: Collision, + cid: string, + self: Collider, + velocity: Vector, + other: Collider, +) { collision.cid = cid; } -function collideWithRect(collision: Collision, cid: string) { - collision.cid = cid; +function collideWithRect( + collision: Collision, + cid: string, + self: Collider, + velocity: Vector, + other: Collider, +) { + const th = sweptAABB(self, velocity, other); + collision.cid = th !== null ? cid : ""; } -function collideWithPolygon(collision: Collision, cid: string) { +function collideWithPolygon( + collision: Collision, + cid: string, + self: Collider, + velocity: Vector, + other: Collider, +) { collision.cid = cid; } @@ -172,15 +231,15 @@ export function moveAndCollide( const collision = next(self.buffer); switch (self.shape) { case CIRCLE: { - circleCollide[other.shape](collision, cid); + circleCollide[other.shape](collision, cid, self, velocity, other); break; } case RECT: { - rectCollide[other.shape](collision, cid); + rectCollide[other.shape](collision, cid, self, velocity, other); break; } case POLYGON: { - polygonCollide[other.shape](collision, cid); + polygonCollide[other.shape](collision, cid, self, velocity, other); break; } } diff --git a/game/entities/hero/entity.ts b/game/entities/hero/entity.ts index 26980ec..8c1007c 100644 --- a/game/entities/hero/entity.ts +++ b/game/entities/hero/entity.ts @@ -1,7 +1,7 @@ import { defineEntity } from "#/engine/Entity.ts"; import { keyboardInput } from "#/engine/Keyboard.ts"; import { pointer } from "#/engine/Pointer.ts"; -import { createRect } from "#/engine/Rect.ts"; +import { createRect, fillRect, strokeRect } from "#/engine/Rect.ts"; import $ from "#/engine/Scene.ts"; import { createSprite, @@ -9,6 +9,8 @@ import { defineSpriteSheet, } from "#/engine/Sprite.ts"; import { + type Collider, + type Collision, createCollider, moveAndCollide, Shape, @@ -19,6 +21,7 @@ import { scale, type Vector, } from "#/engine/Vector.ts"; +import { viewport } from "#/engine/Viewport.ts"; import charIdle from "#/game/assets/char-idle.png"; const speed = 0.05; @@ -35,6 +38,7 @@ const process = defineEntity("hero", { process((id, delta) => { const position = $("position").get(id)!.value; const velocity = $("velocity").get(id)!.value; + const collider = $("collider").get(id)!.value; if (pointer.isDown) { position.x = pointer.position.x; @@ -49,12 +53,18 @@ process((id, delta) => { normalize(velocity); scale(velocity, speed); + const collisions = moveAndCollide(id, velocity, delta); + // console.log(collisions); + position.x += velocity.x * delta; position.y += velocity.y * delta; velocity.x = 0; velocity.y = 0; - const collisions = moveAndCollide(id, velocity, delta); - // console.log(collisions); + fillRect(viewport.ctx, collider.aabb, "orange"); + strokeRect(viewport.ctx, collider.aabb, "red"); + if (collisions.some((c: Collision) => c.cid.length > 0)) { + fillRect(viewport.ctx, collider.aabb, "red"); + } }); diff --git a/game/entities/portal/entity.ts b/game/entities/portal/entity.ts index 8fa42fa..e4f7914 100644 --- a/game/entities/portal/entity.ts +++ b/game/entities/portal/entity.ts @@ -1,5 +1,5 @@ import { defineEntity } from "#/engine/Entity.ts"; -import { createRect } from "#/engine/Rect.ts"; +import { createRect, fillRect, strokeRect } from "#/engine/Rect.ts"; import $ from "#/engine/Scene.ts"; import { createSprite, @@ -12,6 +12,7 @@ import { Shape, } from "#/engine/system/physics.ts"; import { createVector, type Vector } from "#/engine/Vector.ts"; +import { viewport } from "#/engine/Viewport"; import portal from "#/game/assets/portal.png"; const portalSrc = await defineSpriteSheet(portal, 3, 1); @@ -30,4 +31,7 @@ process((id, delta) => { cPos.x = position.x; cPos.y = position.y; + + fillRect(viewport.ctx, collider.aabb, "orange"); + strokeRect(viewport.ctx, collider.aabb, "red"); }); diff --git a/game/scenes/testScene/data.json b/game/scenes/testScene/data.json index 257f969..e52001d 100644 --- a/game/scenes/testScene/data.json +++ b/game/scenes/testScene/data.json @@ -16,8 +16,8 @@ "name": "portal", "init": { "position": { - "x": 16, - "y": 16 + "x": 24, + "y": 24 } } },