collisions: some fixes

This commit is contained in:
2026-08-26 19:20:43 +02:00
parent c94c2dc757
commit f1205e3851
+17 -13
View File
@@ -6,7 +6,9 @@ interface Ball {
vel: Vec2d; vel: Vec2d;
} }
const gravity = new Vec2d(0, 1000); // px/s^2 const GRAVITY = new Vec2d(0, 1000);
const BALL_RADIUS = 25;
let lastTime: number | undefined = undefined; let lastTime: number | undefined = undefined;
const box = [ const box = [
@@ -27,26 +29,26 @@ function update(ctx: CanvasRenderingContext2D, timestamp: number) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
// update // update
for (let i = balls.length - 1; i >= 0; i--) { outer: for (let i = balls.length - 1; i >= 0; i--) {
let ball = balls[i]; let ball = balls[i];
const steps = 10; const steps = 10;
const stepDt = dt / steps; const stepDt = dt / steps;
for (let step = 0; step < steps; step++) { // subdive the time step to improve accuracy for (let step = 0; step < steps; step++) { // subdivide the time step to improve accuracy
balls[i].pos = ball.pos.add(ball.vel.scale(stepDt)); // update pos balls[i].pos = ball.pos.add(ball.vel.scale(stepDt)); // update pos
if (balls[i].pos.y > ctx.canvas.height) { if (balls[i].pos.y > ctx.canvas.height) {
balls.splice(i, 1); // remove if out of bounds balls.splice(i, 1); // remove if out of bounds
continue; continue outer;
} }
ball.vel = ball.vel.add(gravity.scale(stepDt)); // apply gravity ball.vel = ball.vel.add(GRAVITY.scale(stepDt)); // apply gravity
// check collisions with each segment of the box // check collisions with each segment of the box
const friction = 0.5; const friction = 0.5;
const restitution = 0.8; const restitution = 0.8;
ball = resolve_collision(ball, a, b, { friction, restitution}); ball = resolveBallSegmentCollision(ball, [a, b], { friction, restitution});
ball = resolve_collision(ball, b, c, { friction, restitution}); ball = resolveBallSegmentCollision(ball, [b, c], { friction, restitution});
ball = resolve_collision(ball, c, d, { friction, restitution}); ball = resolveBallSegmentCollision(ball, [c, d], { friction, restitution});
ball = resolve_collision(ball, d, a, { friction, restitution}); ball = resolveBallSegmentCollision(ball, [d, a], { friction, restitution});
} }
balls[i] = ball; balls[i] = ball;
@@ -54,7 +56,7 @@ function update(ctx: CanvasRenderingContext2D, timestamp: number) {
// drawing // drawing
for (const p of balls) { for (const p of balls) {
fillCircle(ctx, p.pos, 5, 0xff00ff); fillCircle(ctx, p.pos, BALL_RADIUS, 0xff00ff);
} }
// draw box // draw box
@@ -70,7 +72,9 @@ function update(ctx: CanvasRenderingContext2D, timestamp: number) {
window.requestAnimationFrame((t) => update(ctx, t)); window.requestAnimationFrame((t) => update(ctx, t));
} }
function resolve_collision(ball: Ball, a: Vec2d, b: Vec2d, { restitution = 1, friction = 0 }: { restitution?: number, friction?: number } = {}) {
function resolveBallSegmentCollision(ball: Ball, segment: [Vec2d, Vec2d], { restitution = 1, friction = 0 }: { restitution?: number, friction?: number } = {}) {
const [a, b] = segment;
const ab = b.sub(a); const ab = b.sub(a);
const ab_sqlen = ab.dot(ab); const ab_sqlen = ab.dot(ab);
const point = ball.pos.sub(a); // pos of the ball relative to a const point = ball.pos.sub(a); // pos of the ball relative to a
@@ -80,8 +84,7 @@ function resolve_collision(ball: Ball, a: Vec2d, b: Vec2d, { restitution = 1, fr
const closest_point = a.add(ab.scale(t)); const closest_point = a.add(ab.scale(t));
const offset = ball.pos.sub(closest_point); // vector from the point to the segment const offset = ball.pos.sub(closest_point); // vector from the point to the segment
if (offset.length() < 5) { if (offset.length() < BALL_RADIUS) {
console.log("collide")
const normal = offset.normalize(); const normal = offset.normalize();
// Decompose velocity into normal and tangent components // Decompose velocity into normal and tangent components
const norm_speed = ball.vel.dot(normal); const norm_speed = ball.vel.dot(normal);
@@ -122,6 +125,7 @@ window.onload = () => {
const y = evt.clientY - rect.top; const y = evt.clientY - rect.top;
addBall(x, y); addBall(x, y);
}; };
canvas.ontouchstart = (evt: TouchEvent) => { canvas.ontouchstart = (evt: TouchEvent) => {
const rect = canvas.getBoundingClientRect(); const rect = canvas.getBoundingClientRect();
console.assert(evt.touches.length == 1, "mutiple touches are not supported"); console.assert(evt.touches.length == 1, "mutiple touches are not supported");