summaryrefslogtreecommitdiff
path: root/src/index.js
blob: 1275360ee2320f04d46f60252b09baaea053ebf9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
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();
});