Make physics more fun

This commit is contained in:
schmelczerandras 2020-10-16 11:48:52 +02:00
commit 89fafeafd3
6 changed files with 509 additions and 332 deletions

View file

@ -0,0 +1,24 @@
import { vec2 } from 'gl-matrix';
import { Circle } from 'shared';
import { CirclePhysical } from '../../objects/circle-physical';
export const applySpringForce = (
a: CirclePhysical | Circle,
b: CirclePhysical | Circle,
distance: number,
strength: number,
deltaTimeInSeconds: number,
) => {
const length = vec2.dist(a.center, b.center) - distance;
const abDirection = vec2.subtract(vec2.create(), b.center, a.center);
vec2.normalize(abDirection, abDirection);
const force = vec2.scale(abDirection, abDirection, strength * length);
if (a instanceof CirclePhysical) {
a.applyForce(force, deltaTimeInSeconds);
}
if (b instanceof CirclePhysical) {
vec2.scale(force, force, -1);
b.applyForce(force, deltaTimeInSeconds);
}
};

View file

@ -0,0 +1,7 @@
import { vec2 } from 'gl-matrix';
import { PhysicalBase } from '../physicals/physical-base';
export const evaluateSdf = (target: vec2, objects: Array<PhysicalBase>) =>
objects
.filter((i) => i.canCollide)
.reduce((min, i) => (min = Math.min(min, i.distance(target))), 1000);

View file

@ -0,0 +1,9 @@
import { vec2 } from 'gl-matrix';
import { Physical } from '../physicals/physical';
export const forceAtPosition = (position: vec2, objects: Array<Physical>) =>
objects.reduce(
(sum: vec2, o: Physical) =>
!o.canMove ? vec2.add(sum, sum, o.getForce(position)) : sum,
vec2.create(),
);

View file

@ -0,0 +1,10 @@
import { Circle } from 'shared';
import { BoundingBoxBase } from '../bounding-boxes/bounding-box-base';
export const getBoundingBoxOfCircle = (circle: Circle): BoundingBoxBase =>
new BoundingBoxBase(
circle.center.x - circle.radius,
circle.center.x + circle.radius,
circle.center.y - circle.radius,
circle.center.y + circle.radius,
);

View file

@ -0,0 +1,8 @@
import { Circle } from 'shared';
import { evaluateSdf } from './evaluate-sdf';
import { PhysicalBase } from '../physicals/physical-base';
export const isCircleIntersecting = (
circle: Circle,
intersectors: Array<PhysicalBase>,
): boolean => evaluateSdf(circle.center, intersectors) < circle.radius;

View file

@ -0,0 +1,70 @@
import { vec2 } from 'gl-matrix';
import { Circle, GameObject, rotate90Deg } from 'shared';
import { CirclePhysical } from '../../objects/circle-physical';
import { reactsToCollision } from '../physicals/reacts-to-collision';
import { evaluateSdf } from './evaluate-sdf';
import { Physical } from '../physicals/physical';
export const moveCircle = (
circle: CirclePhysical,
delta: vec2,
possibleIntersectors: Array<Physical>,
): {
realDelta: vec2;
hitSurface: boolean;
normal?: vec2;
tangent?: vec2;
hitObject?: GameObject;
} => {
const nextCircle = new Circle(vec2.clone(circle.center), circle.radius);
vec2.add(nextCircle.center, nextCircle.center, delta);
possibleIntersectors = possibleIntersectors.filter(
(b) => b.gameObject !== circle.gameObject && b.canCollide,
);
const sdfAtCenter = evaluateSdf(nextCircle.center, possibleIntersectors);
if (sdfAtCenter > nextCircle.radius) {
circle.center = vec2.add(circle.center, circle.center, delta);
return {
realDelta: delta,
hitSurface: false,
};
}
const intersecting = possibleIntersectors.find(
(i) => i.distance(nextCircle.center) <= circle.radius,
)!;
if (reactsToCollision(intersecting)) {
intersecting.onCollision(circle.gameObject);
}
if (reactsToCollision(circle)) {
circle.onCollision(intersecting.gameObject);
}
const dx =
evaluateSdf(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(0.01, 0)), [
intersecting,
]) - sdfAtCenter;
const dy =
evaluateSdf(vec2.add(vec2.create(), nextCircle.center, vec2.fromValues(0, 0.01)), [
intersecting,
]) - sdfAtCenter;
const normal = vec2.fromValues(dx, dy);
vec2.normalize(normal, normal);
const rotatedNormal = rotate90Deg(normal);
return {
realDelta: delta,
hitSurface: true,
normal,
hitObject: intersecting?.gameObject,
tangent:
vec2.dot(rotatedNormal, delta) < 0
? vec2.scale(rotatedNormal, rotatedNormal, -1)
: rotatedNormal,
};
};