summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/index.html25
-rw-r--r--src/index.js94
2 files changed, 119 insertions, 0 deletions
diff --git a/src/index.html b/src/index.html
new file mode 100644
index 0000000..aa49141
--- /dev/null
+++ b/src/index.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Document</title>
+ <script type="module" src="index.js"></script>
+ <style>
+ html,
+ body {
+ margin: 0;
+ }
+
+ div#throwable {
+ position: fixed;
+ transform: translate(-50%, -50%);
+ background-color: red;
+ border-radius: 50%;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="throwable"></div>
+ </body>
+</html>
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();
+});