collisions: some fixes
This commit is contained in:
+17
-13
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user