Three. js OrbitControls, вращающиеся вокруг кольца - PullRequest
0 голосов
/ 22 февраля 2020

У меня есть камера OrbitControls, которая вращается вокруг точки: target = player.position.x, y и z. Это довольно хорошо, но плеер находится прямо в центре экрана. Мне нужно, чтобы камера вращалась вокруг кольца ... первое изображение: цель находится в центре, второе: цель всегда расположена слева от центра

Позвольте мне объяснить: здесь мой код:

controls.target.x = player.position.x;
controls.target.y = player.position.y+3;
controls.target.z = player.position.z;

Итак, мы вращаемся вокруг фиксированной точки.

Но мне нужна точка, которая меняет свое положение в зависимости от controls.getAzimuthalAngle () или чего-то еще. Камера должна быть такой же, как в GTA V или других играх, где игрок находится не по центру экрана, а немного левее, поэтому удобнее, например, прицеливаться, чтобы игрок не мешал цель в центре.

Помощь ...

1 Ответ

0 голосов
/ 23 февраля 2020

Как правило, для камеры от третьего лица вы постоянно нацеливаете камеру на игрока (или на какое-то смещение от игрока, или на какой-либо объект, прикрепленный к плееру)

В таком случае камера будет следить за игроком с течением времени.

В приведенном ниже коде есть cameraRig, который следует за игроком. Там есть камера, поэтому нам не нужно ничего делать, чтобы держать камеру над землей. Точно так же есть camTarget, который присоединен к игроку. Так выглядит камера примерно на уровне плеч.

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const keys = {};
  const scene = new THREE.Scene();

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 500;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  
  // put the camera 5 units above the rig
  const cameraRig = new THREE.Object3D();
  cameraRig.add(camera);
  camera.position.y = 5;
  scene.add(cameraRig);
  cameraRig.position.z = 5;

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }
  
  {
    const size = 200;
    const divisions = 100;
    const gridHelper = new THREE.GridHelper(size, divisions);
    scene.add(gridHelper);
  }

  const boxWidth = 0.5;
  const boxHeight = 2;
  const boxDepth = 0.5;
  const geometry = new THREE.CylinderGeometry(boxWidth, boxDepth, boxHeight);

  const material = new THREE.MeshPhongMaterial({color:'red'});
  const cube = new THREE.Mesh(geometry, material);
  const player = new THREE.Object3D();
  const camTarget = new THREE.Object3D();

  cube.position.y = boxHeight / 2;  // move cube above ground
  player.add(cube);
  camTarget.position.y = boxHeight * 3 / 2;  // target 2/3ds up the player
  player.add(camTarget);
  scene.add(player);
  
  

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }
  
  const moveDir = new THREE.Vector3();
  const camTargetPos = new THREE.Vector3();
  
  let then = 0;
  function render(now) {
    now *= 0.001;
    deltaTime = now - then;
    then = now;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    // left, right, a, d
    const dx = ((keys[37] || keys[65]) ?  1 : 0) + 
               ((keys[39] || keys[68]) ? -1 : 0);
    // up, down, w, s
    const dy = ((keys[38] || keys[87]) ?  1 : 0) + 
               ((keys[40] || keys[83]) ? -1 : 0);
    const playerMoveSpeed = 10; // units per second
    camera.getWorldDirection(moveDir);
    moveDir.y = 0; // no up down movement
    moveDir.normalize();
    // move player forward/back
    player.position.addScaledVector(moveDir, dy * playerMoveSpeed * deltaTime);
    // rotate direction 90 degrees
    const t = moveDir.x;
    moveDir.x = moveDir.z;
    moveDir.z = -t;
    // move player left/right
    player.position.addScaledVector(moveDir, dx * playerMoveSpeed * deltaTime);
    
    // if the cameraRig is too far from
    // player then move it
    const maxDistance = 6;
    const maxCamMoveSpeed = 0.015;
    const distance = cameraRig.position.distanceTo(player.position);
    if (distance > maxDistance) {
      const amount = maxCamMoveSpeed;
      cameraRig.position.lerp(player.position, amount);
    }

    camTarget.getWorldPosition(camTargetPos);
    camera.lookAt(camTargetPos);
     

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }

  requestAnimationFrame(render);
  
  window.addEventListener('keydown', (e) => {
    e.preventDefault();
    keys[e.keyCode] = true;
  });
  window.addEventListener('keyup', (e) => {
    e.preventDefault();
    keys[e.keyCode] = false;
  });
}



main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.min.js"></script>
<canvas id="c" tabindex="0"></canvas>

Камеры от третьего лица могут быть трудными. Тот, что выше, слишком прост. Вы можете думать об этом так, как будто cameraRig перетаскивается на строку позади проигрывателя. Если вы делаете резервную копию, камера не двигается. Он перемещается только в том случае, если камера находится на максимальном расстоянии.

Обычно вместо этого помещают камеру на палку, поэтому, если вы идете назад, палка отталкивает камеру назад.

function main() {
  const canvas = document.querySelector('#c');
  const renderer = new THREE.WebGLRenderer({canvas});

  const keys = {};
  const scene = new THREE.Scene();

  const fov = 75;
  const aspect = 2;  // the canvas default
  const near = 0.1;
  const far = 500;
  const camera = new THREE.PerspectiveCamera(fov, aspect, near, far);
  
  // put the camera 5 units above the rig
  const cameraRig = new THREE.Object3D();
  cameraRig.add(camera);
  camera.position.y = 5;
  scene.add(cameraRig);
  cameraRig.position.z = 5;

  {
    const color = 0xFFFFFF;
    const intensity = 1;
    const light = new THREE.DirectionalLight(color, intensity);
    light.position.set(-1, 2, 4);
    scene.add(light);
  }
  
  {
    const size = 200;
    const divisions = 100;
    const gridHelper = new THREE.GridHelper(size, divisions);
    scene.add(gridHelper);
  }

  const boxWidth = 0.5;
  const boxHeight = 2;
  const boxDepth = 0.5;
  const geometry = new THREE.CylinderGeometry(boxWidth, boxDepth, boxHeight);

  const material = new THREE.MeshPhongMaterial({color:'red'});
  const cube = new THREE.Mesh(geometry, material);
  const player = new THREE.Object3D();
  const camTarget = new THREE.Object3D();

  cube.position.y = boxHeight / 2;  // move cube above ground
  player.add(cube);
  camTarget.position.y = boxHeight * 3 / 2;  // target 2/3ds up the player
  player.add(camTarget);
  scene.add(player);
  
  

  function resizeRendererToDisplaySize(renderer) {
    const canvas = renderer.domElement;
    const width = canvas.clientWidth;
    const height = canvas.clientHeight;
    const needResize = canvas.width !== width || canvas.height !== height;
    if (needResize) {
      renderer.setSize(width, height, false);
    }
    return needResize;
  }
  
  const moveDir = new THREE.Vector3();
  const camTargetPos = new THREE.Vector3();
  const rigTargetPos = new THREE.Vector3();
  
  function moveCameraToDistance(desiredDistance) {
    const maxCamMoveSpeed = 0.02;
    
    // delta from player to rig
    rigTargetPos.subVectors(cameraRig.position, player.position);
    // remove up/down
    rigTargetPos.y = 0; // no up/down
    // make unit vector
    rigTargetPos.normalize();
    // make desiredDistance long
    rigTargetPos.multiplyScalar(desiredDistance);
    // add player position
    rigTargetPos.add(player.position);
    // move rig toward that position
    const curDistance = cameraRig.position.distanceTo(player.position);
    cameraRig.position.lerp(
        rigTargetPos,
        THREE.MathUtils.lerp(
            maxCamMoveSpeed,
            1, 
            THREE.MathUtils.clamp(1 - curDistance / desiredDistance, 0, 1)));
  }
  
  let then = 0;
  function render(now) {
    now *= 0.001;
    deltaTime = now - then;
    then = now;

    if (resizeRendererToDisplaySize(renderer)) {
      const canvas = renderer.domElement;
      camera.aspect = canvas.clientWidth / canvas.clientHeight;
      camera.updateProjectionMatrix();
    }
    
    // left, right, a, d
    const dx = ((keys[37] || keys[65]) ?  1 : 0) + 
               ((keys[39] || keys[68]) ? -1 : 0);
    // up, down, w, s
    const dy = ((keys[38] || keys[87]) ?  1 : 0) + 
               ((keys[40] || keys[83]) ? -1 : 0);
    const playerMoveSpeed = 10; // units per second
    camera.getWorldDirection(moveDir);
    moveDir.y = 0; // no up down movement
    moveDir.normalize();
    // move player forward/back
    player.position.addScaledVector(moveDir, dy * playerMoveSpeed * deltaTime);
    // rotate direction 90 degrees
    const t = moveDir.x;
    moveDir.x = moveDir.z;
    moveDir.z = -t;
    // move player left/right
    player.position.addScaledVector(moveDir, dx * playerMoveSpeed * deltaTime);
    
    // keep camera at distance
    const minDistance = 4;
    const maxDistance = 6;
    const distance = cameraRig.position.distanceTo(player.position);
    if (distance > maxDistance) {
      moveCameraToDistance(maxDistance);
    } else if (distance < minDistance) {
      moveCameraToDistance(minDistance);
    }

   // compute point from player in direction of rig 
    // at desired distance

    camTarget.getWorldPosition(camTargetPos);
    camera.lookAt(camTargetPos);
     

    renderer.render(scene, camera);

    requestAnimationFrame(render);
  }
  
  requestAnimationFrame(render);
  
  window.addEventListener('keydown', (e) => {
    e.preventDefault();
    keys[e.keyCode] = true;
  });
  window.addEventListener('keyup', (e) => {
    e.preventDefault();
    keys[e.keyCode] = false;
  });
}



main();
body {
  margin: 0;
}
#c {
  width: 100vw;
  height: 100vh;
  display: block;
}
<script src="https://threejsfundamentals.org/threejs/resources/threejs/r113/build/three.js"></script>
<canvas id="c" tabindex="0"></canvas>

Затем вам нужно разобраться с такими вещами, как вещи, попадающие между камерой и плеером. (некоторые игры постепенно исчезают) Вы также должны иметь дело с, скажем, ходить через дверь. Какое-то время игрок будет находиться на одной стороне двери, а камера - на другой (или, скорее, на противоположных сторонах дверного проема, поэтому камера опускается, чтобы она могла видеть сквозь дверь? Стена исчезает? Камера прыгает? через дверь и начните смотреть с некоторых других позиций. Ходят слухи, что в Mario 64 один программист работал над камерой целый год и ничего больше.)

Обратите внимание, что часть приведенного выше кода работает только в особом случае, когда someObject.position - это положение в мировом пространстве (у объекта нет родительских объектов, или, если это так, все родители имеют положение = 0,0,0 вращения = 0,0,0, масштаб = 1,1,1). Если у объекта есть родители, то вам нужно получить мировую позицию с помощью

const wp = new THREE.Vector3();
someObject.getWorldPosition(wp);

. И если вы хотите применить мировую позицию, вам нужно будет проделать больше работы, чтобы сделать положение относительно родитель. А пока, для простоты, я просто использовал объекты, чье мировое положение - это их положение.

...