summaryrefslogtreecommitdiff
path: root/src/index.js
diff options
context:
space:
mode:
authorArjun Satarkar <me@arjunsatarkar.net>2024-10-31 02:27:17 +0000
committerArjun Satarkar <me@arjunsatarkar.net>2024-10-31 02:27:17 +0000
commitbcb38594492aec4f1116b3fdf0e9821d4016903a (patch)
tree300afa3870ef80e37586f03ed35b2bd0ff1e83b6 /src/index.js
downloadthrow_simulation-bcb38594492aec4f1116b3fdf0e9821d4016903a.tar
throw_simulation-bcb38594492aec4f1116b3fdf0e9821d4016903a.tar.gz
throw_simulation-bcb38594492aec4f1116b3fdf0e9821d4016903a.zip
Implement the physics
Diffstat (limited to 'src/index.js')
-rw-r--r--src/index.js94
1 files changed, 94 insertions, 0 deletions
diff --git a/src/index.js b/src/index.js
new file mode 100644
index 0000000..1275360
--- /dev/null
+++ b/src/index.js
@@ -0,0 +1,94 @@
+import RAPIER from "./rapier.es.js";
+
+RAPIER.init().then(() => {
+ const CLICK_IMPULSE_MULTIPLIER = 300_000;
+ const GRAVITY = { x: 0.0, y: 2000 };
+ const LINEAR_DAMPING = 0.5;
+ const OBJ_BASE_RADIUS = 30;
+ const OBJ_BASE_SIZE = { x: OBJ_BASE_RADIUS * 2, y: OBJ_BASE_RADIUS * 2 };
+ const OBJ_MASS = 100;
+ const STEP_MS = 33;
+ const TARGET_WIDTH_FACTOR = 0.05;
+ const VIEWPORT_SIZE = {
+ x: document.documentElement.clientWidth,
+ y: document.documentElement.clientHeight,
+ };
+ const WIDTH_JUMP_FACTOR = 5;
+
+ let world = new RAPIER.World(GRAVITY);
+
+ const wallColliderDescs = {
+ x: RAPIER.ColliderDesc.cuboid(VIEWPORT_SIZE.x, 1).setRestitution(1),
+ y: RAPIER.ColliderDesc.cuboid(1, VIEWPORT_SIZE.y).setRestitution(1),
+ };
+ world.createCollider(wallColliderDescs.x);
+ world.createCollider(wallColliderDescs.x.setTranslation(0, VIEWPORT_SIZE.y));
+ world.createCollider(wallColliderDescs.y);
+ world.createCollider(wallColliderDescs.y.setTranslation(VIEWPORT_SIZE.x, 0));
+
+ let objRigidBodyDesc = RAPIER.RigidBodyDesc.dynamic()
+ .setTranslation(VIEWPORT_SIZE.x / 2, VIEWPORT_SIZE.y / 3)
+ .setLinearDamping(LINEAR_DAMPING)
+ .setCcdEnabled(true);
+ let obj = world.createRigidBody(objRigidBodyDesc);
+ world.createCollider(
+ RAPIER.ColliderDesc.ball(OBJ_BASE_RADIUS).setMass(OBJ_MASS),
+ obj,
+ );
+
+ const objElement = document.querySelector("div#throwable");
+ const objSize = {
+ x: OBJ_BASE_SIZE.x,
+ y: OBJ_BASE_SIZE.y,
+ };
+ let objVelocityLastFrame = null;
+
+ let mainLoop = () => {
+ world.step();
+
+ const objPosition = obj.translation();
+ objElement.style.left = `${objPosition.x}px`;
+ objElement.style.top = `${objPosition.y}px`;
+
+ const objVelocity = obj.linvel();
+ if (objVelocityLastFrame !== null) {
+ // Just delta velocity across a frame, technically
+ const objAcceleration = {
+ x: objVelocity.x - objVelocityLastFrame.x,
+ y: objVelocity.y - objVelocityLastFrame.y,
+ };
+ let targetWidth =
+ OBJ_BASE_SIZE.x + TARGET_WIDTH_FACTOR * Math.abs(objAcceleration.y);
+ let targetHeight =
+ OBJ_BASE_SIZE.y + TARGET_WIDTH_FACTOR * Math.abs(objAcceleration.x);
+
+ objSize.x = objSize.x + (targetWidth - objSize.x) / WIDTH_JUMP_FACTOR;
+ objSize.y = objSize.y + (targetHeight - objSize.y) / WIDTH_JUMP_FACTOR;
+ }
+ objVelocityLastFrame = objVelocity;
+
+ objElement.style.width = `${objSize.x}px`;
+ objElement.style.height = `${objSize.y}px`;
+
+ setTimeout(mainLoop, STEP_MS);
+ };
+
+ document.addEventListener("click", (e) => {
+ const clickPosition = { x: e.clientX, y: e.clientY };
+ const objPosition = obj.translation();
+ const direction = {
+ x: clickPosition.x - objPosition.x,
+ y: clickPosition.y - objPosition.y,
+ };
+ const distance = Math.sqrt(direction.x ** 2 + direction.y ** 2);
+ obj.setLinvel({x: 0, y: 0});
+ if (distance > 0) {
+ obj.applyImpulse({
+ x: (direction.x * CLICK_IMPULSE_MULTIPLIER) / distance,
+ y: (direction.y * CLICK_IMPULSE_MULTIPLIER) / distance,
+ });
+ }
+ });
+
+ mainLoop();
+});