1
0
Fork 0

Plane collision

This commit is contained in:
Pabloader 2025-12-16 15:48:02 +00:00
parent 99a306c10b
commit ee9c4b0290
4 changed files with 65 additions and 29 deletions

View File

@ -33,9 +33,11 @@ export function gameLoop<T extends Record<string, unknown> | void>(setupOrFrame:
const now = performance.now();
const dt = (now - prevFrame) / 1000;
const newState = await frame(dt, state);
if (newState) {
state = newState;
if (dt < 1) {
const newState = await frame(dt, state);
if (newState) {
state = newState;
}
}
prevFrame = performance.now();

View File

@ -36,8 +36,8 @@ typedef struct rigid_body_t {
////////// Prototypes
void rigid_body_resolve_collision(rigid_body* rb);
int rigid_body_collide(rigid_body* a, rigid_body* b);
void rigid_body_handle_collision(rigid_body* a, rigid_body* b);
float point_to_line_dist(vec2 point, vec2 line_point, vec2 line_norm);
////////// Globals
@ -84,6 +84,15 @@ EXPORT(rigid_body_new_circle) rigid_body* rigid_body_new_circle(float x, float y
return rb;
}
EXPORT(rigid_body_new_plane) rigid_body* rigid_body_new_plane(float x, float y, float nx, float ny) {
rigid_body* rb = rigid_body_new(x, y, 0, 0, INFINITY);
rb->type = TYPE_PLANE;
rb->plane.normal = vec2_normalize((vec2){nx, ny});
return rb;
}
EXPORT(rigid_body_free) void rigid_body_free(rigid_body* rb) {
if (rb == rigid_body_head) {
rigid_body_head = rb->next;
@ -102,9 +111,9 @@ EXPORT(rigid_body_free) void rigid_body_free(rigid_body* rb) {
}
EXPORT(rigid_body_update) void rigid_body_update(rigid_body* rb, float dt) {
if (!isinf(rb->mass)) {
rigid_body_resolve_collision(rb);
rigid_body_resolve_collision(rb);
if (!isinf(rb->mass)) {
rb->vel.x += rb->force.x * dt / rb->mass;
rb->vel.y += rb->force.y * dt / rb->mass;
@ -131,35 +140,30 @@ EXPORT(rigid_body_add_force) void rigid_body_add_force(rigid_body* rb, float fx,
}
void rigid_body_resolve_collision(rigid_body* rb) {
rigid_body* current = rigid_body_head;
rigid_body* current = rb->next;
int is_static = isinf(rb->mass);
while (current) {
if (current != rb && rigid_body_collide(rb, current)) {
if (!is_static || !isinf(current->mass)) {
rigid_body_handle_collision(rb, current);
}
current = current->next;
}
}
int rigid_body_collide(rigid_body* rb1, rigid_body* rb2) {
if (rb1->type == TYPE_CIRCLE && rb2->type == TYPE_CIRCLE) {
float dx = rb2->pos.x - rb1->pos.x;
float dy = rb2->pos.y - rb1->pos.y;
float distance = sqrtf(dx * dx + dy * dy);
return distance < rb1->circle.radius + rb2->circle.radius;
}
return 0; // Handle other types later
}
void rigid_body_handle_collision(rigid_body* rb1, rigid_body* rb2) {
if (rb1->type == TYPE_CIRCLE && rb2->type == TYPE_CIRCLE) {
vec2 d = vec2_sub(rb2->pos, rb1->pos);
vec2 n = vec2_normalize(d);
float distance = vec2_mag(d);
float overlap = rb1->circle.radius + rb2->circle.radius - distance;
vec2 n_overlap = vec2_mul(n, overlap / 2);
if (overlap < 0) {
return;
}
vec2 n = vec2_normalize(d);
float factor = (isinf(rb1->mass) || isinf(rb2->mass)) ? 1 : 0.5;
vec2 n_overlap = vec2_mul(n, overlap * factor);
if (!isinf(rb1->mass)) {
rb1->pos = vec2_sub(rb1->pos, n_overlap);
@ -194,5 +198,28 @@ void rigid_body_handle_collision(rigid_body* rb1, rigid_body* rb2) {
if (!isinf(rb2->mass)) {
rb2->vel = vec2_add(v2t, vec2_mul(n, v2n_new));
}
} else if (rb1->type == TYPE_PLANE && rb2->type == TYPE_CIRCLE) {
float distance = point_to_line_dist(rb2->pos, rb1->pos, rb1->plane.normal) - rb2->circle.radius;
float overlap = -distance;
if (overlap < 0) {
return;
}
vec2 n = rb1->plane.normal;
vec2 n_overlap = vec2_mul(n, overlap);
rb2->pos = vec2_add(rb2->pos, n_overlap);
float vn = vec2_dot(rb2->vel, n);
vec2 vt = vec2_sub(rb2->vel, vec2_mul(n, vn));
rb2->vel = vec2_add(vt, vec2_mul(n, -vn));
} else if (rb1->type == TYPE_CIRCLE && rb2->type == TYPE_PLANE) {
rigid_body_handle_collision(rb2, rb1);
}
}
float point_to_line_dist(vec2 p, vec2 line_point, vec2 normal) {
vec2 diff = vec2_sub(p, line_point);
return vec2_dot(diff, normal);
}

View File

@ -6,9 +6,14 @@ namespace Physics {
const bodies = new Set<number>();
export function newCircle(x: number, y: number, radius: number, mass: number = 1.0): number {
const circlePtr = E.rigid_body_new_circle(x, y, 0, 0, mass, radius);
bodies.add(circlePtr);
return circlePtr;
const body = E.rigid_body_new_circle(x, y, 0, 0, mass, radius);
bodies.add(body);
return body;
}
export function newPlane(x: number, y: number, nx: number, ny: number): number {
const body = E.rigid_body_new_plane(x, y, nx, ny);
bodies.add(body);
return body;
}
export function deleteBody(body: number) {

View File

@ -24,25 +24,27 @@ const setup = () => {
event.preventDefault();
const { x, y } = getRealPoint(canvas, event);
console.log(event.button);
const mass = event.button === 0 ? 1 : Number.POSITIVE_INFINITY;
const mass = event.button === 0 ? Math.random() : Number.POSITIVE_INFINITY;
const r = event.button === 0 ? 16 : 4;
Physics.newCircle(x, y, r, mass);
return false;
});
Physics.newPlane(0, 0, 1, 0);
Physics.newPlane(0, 0, 0, 1);
Physics.newPlane(0, canvas.height, 0, -1);
Physics.newPlane(canvas.width, 0, -1, 0);
return { canvas, ctx };
}
const frame = (dt: number, state: ReturnType<typeof setup>) => {
Physics.addGlobalForce(0, 98);
Physics.addGlobalForce(0, 100);
Physics.update(dt);
const { ctx, canvas } = state;
ctx.clearRect(0, 0, canvas.width, canvas.height);
for (const { id, x, y, radius } of Physics.getBodies()) {
if (y >= canvas.height) {
Physics.deleteBody(id);
}
ctx.fillStyle = `hsl(${id % 360}, 100%, 50%)`;
if (radius) {
ctx.beginPath();