diff options
author | Arjun Satarkar <me@arjunsatarkar.net> | 2024-10-31 02:27:17 +0000 |
---|---|---|
committer | Arjun Satarkar <me@arjunsatarkar.net> | 2024-10-31 02:27:17 +0000 |
commit | bcb38594492aec4f1116b3fdf0e9821d4016903a (patch) | |
tree | 300afa3870ef80e37586f03ed35b2bd0ff1e83b6 /src | |
download | throw_simulation-bcb38594492aec4f1116b3fdf0e9821d4016903a.tar throw_simulation-bcb38594492aec4f1116b3fdf0e9821d4016903a.tar.gz throw_simulation-bcb38594492aec4f1116b3fdf0e9821d4016903a.zip |
Implement the physics
Diffstat (limited to 'src')
-rw-r--r-- | src/index.html | 25 | ||||
-rw-r--r-- | src/index.js | 94 |
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(); +}); |