Newer
Older
Three.js / test / firstPersonCamera_test.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8" />
  <meta http-equiv="X-UA-Compatible" content="IE=edge" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Document</title>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
  <script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
</head>
<body>
  <script>
    let camera, scene, renderer;
    let moveForward = false;
    let moveBackward = false;
    let moveLeft = false;
    let moveRight = false;
    let moveSpeed = 0.1;
    let yawObject, pitchObject;
    let mouseX = 0;
    let mouseY = 0;
    let sensitivity = 0.002;

    init();
    animate();

    function init() {
      scene = new THREE.Scene();

      camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        1000
      );
      camera.position.set(0, 1.6, 0);

      const axesHelper = new THREE.AxesHelper(100);
      scene.add(axesHelper);
      const gridHelper = new THREE.GridHelper(50, 50);
      scene.add(gridHelper);

      yawObject = new THREE.Object3D();
      yawObject.position.y = 1.6;
      yawObject.add(camera);

      pitchObject = new THREE.Object3D();
      pitchObject.add(yawObject);
      scene.add(pitchObject);

      renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      document.addEventListener("mousedown", onMouseDown, false);
      document.addEventListener("mouseup", onMouseUp, false);
      document.addEventListener("mousemove", onMouseMove, false);
      document.addEventListener("keydown", onKeyDown, false);
      document.addEventListener("keyup", onKeyUp, false);
      window.addEventListener("resize", onWindowResize, false);
    }

    function animate() {
      requestAnimationFrame(animate);
      updateCameraPosition();
      renderer.render(scene, camera);
    }

    function onMouseDown(event) {
      if (event.button === 0) { // 左クリック
        document.body.requestPointerLock(); // マウスをキャプチャして移動を検知する
      }
    }

    function onMouseUp(event) {
      if (event.button === 0) { // 左クリック
        document.exitPointerLock(); // マウスキャプチャを解除
      }
    }

    function onMouseMove(event) {
      if (document.pointerLockElement === document.body) {
        mouseX -= event.movementX * sensitivity;
        mouseY -= event.movementY * sensitivity;
      }
    }

    function onKeyDown(event) {
      switch (event.keyCode) {
        case 87: // W key
          moveBackward = true;
          break;
        case 83: // S key
          moveForward = true;
          break;
        case 65: // A key
          moveRight = true;
          break;
        case 68: // D key
          moveLeft = true;
          break;
      }
    }

    function onKeyUp(event) {
      switch (event.keyCode) {
        case 87: // W key
          moveBackward = false;
          break;
        case 83: // S key
          moveForward = false;
          break;
        case 65: // A key
          moveRight = false;
          break;
        case 68: // D key
          moveLeft = false;
          break;
      }
    }

    function onWindowResize() {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    }

    function updateCameraPosition() {
      const direction = new THREE.Vector3();
      camera.getWorldDirection(direction);

      if (moveForward) {
        pitchObject.position.addScaledVector(direction, -moveSpeed);
      }
      if (moveBackward) {
        pitchObject.position.addScaledVector(direction, moveSpeed);
      }
      if (moveLeft) {
        const left = new THREE.Vector3();
        left.crossVectors(camera.up, direction);
        left.normalize();
        pitchObject.position.addScaledVector(left, -moveSpeed);
      }
      if (moveRight) {
        const right = new THREE.Vector3();
        right.crossVectors(camera.up, direction);
        right.normalize();
        pitchObject.position.addScaledVector(right, moveSpeed);
      }

      pitchObject.rotation.y += mouseX;
      yawObject.rotation.x += mouseY;

      // 垂直方向の回転を制限する
      yawObject.rotation.x = Math.max(
        -Math.PI / 2,
        Math.min(Math.PI / 2, yawObject.rotation.x)
      );

      mouseX = 0;
      mouseY = 0;
    }
  </script>
</body>
</html>