如何在 Three.js 中创建一个 moving/sliding 元素?

How to make a moving/sliding element in Three.js?

我发现 three.js 我现在很喜欢用它,但是我用键盘箭头构建了一个旋转的汽车动画来控制它,但我找不到如何制作滑动动画(例如,

-> -> ->

) 而不是旋转动画。我尝试搜索,但除了滑动动画外,我什至不知道如何解释它。有人知道如何/如果你能做到这一点吗?

这是我的代码:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>ThreeJS Animation</title>
        <style>            
            body {
              margin: 0;
              font-weight: 100;
              float: none;
              overflow: hidden;
            }
            canvas {
              width: 100%;
              height: 100%;
              margin: 0;
              padding: 0;
            }
        </style>
    </head>
    <body>
        <script src="https://threejs.org/build/three.js"></script>
        <script>
let carBottomColor = "#999";
let carTopColor = "#FFF";
let carWindowColor = "#666";
const scene = new THREE.Scene();
scene.background = new THREE.Color("#f1f1f1");
const car = createCar();
scene.add(car);

const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);

const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(200, 500, 300);
scene.add(dirLight);

const aspectRatio = window.innerWidth / window.innerHeight;
const cameraWidth = 300;
const cameraHeight = cameraWidth / aspectRatio;
const camera = new THREE.OrthographicCamera(
  cameraWidth / -2, // left
  cameraWidth / 2, // right
  cameraHeight / 2, // top
  cameraHeight / -2, // bottom
  200, // near plane
  800 // far plane
);
camera.position.set(200, 200, 200);
camera.lookAt(0, 10, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);

let x = 0;
let y = 0;
let keydown = '';
document.body.addEventListener('keydown', e => {
  keydown = e.key;
});
document.body.addEventListener('keyup', e => {
  keydown = '';
});
const update = () => {
    switch (keydown) {
    case 'ArrowUp':
      x -= 0.1;
      car.rotation.x = x;
      break;
    case 'ArrowDown':
      x += 0.1;
      car.rotation.x = x;
      break;
    case 'ArrowLeft':
      y -= 0.1;
      car.rotation.y = y;
      break;
    case 'ArrowRight':
      y += 0.1;
      car.rotation.y = y;
      break;
  }
  window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);


renderer.setAnimationLoop(() => {
  renderer.render(scene, camera);
});

document.body.appendChild(renderer.domElement);

function createCar() {
  const car = new THREE.Group();
  const backWheel = createWheels();
  backWheel.position.y = 6;
  backWheel.position.x = -18;
  car.add(backWheel);

  const frontWheel = createWheels();
  frontWheel.position.y = 6;
  frontWheel.position.x = 18;
  car.add(frontWheel);

  const main = new THREE.Mesh(
    new THREE.BoxBufferGeometry(60, 15, 30),
    new THREE.MeshLambertMaterial({ color: carBottomColor })
  );
  main.position.y = 12;
  car.add(main);

  const carFrontTexture = getCarFrontTexture();

  const carBackTexture = getCarFrontTexture();

  const carRightSideTexture = getCarSideTexture();

  const carLeftSideTexture = getCarSideTexture();
  carLeftSideTexture.center = new THREE.Vector2(0.5, 0.5);
  carLeftSideTexture.rotation = Math.PI;
  carLeftSideTexture.flipY = false;

  const cabin = new THREE.Mesh(new THREE.BoxBufferGeometry(33, 12, 24), [
    new THREE.MeshLambertMaterial({ map: carFrontTexture }),
    new THREE.MeshLambertMaterial({ map: carBackTexture }),
    new THREE.MeshLambertMaterial({ color: carTopColor }), // top
    new THREE.MeshLambertMaterial({ color: carTopColor }), // bottom
    new THREE.MeshLambertMaterial({ map: carRightSideTexture }),
    new THREE.MeshLambertMaterial({ map: carLeftSideTexture })
  ]);
  cabin.position.x = -6;
  cabin.position.y = 25.5;
  car.add(cabin);
  return car;
}

function createWheels() {
  const geometry = new THREE.BoxBufferGeometry(12, 12, 33);
  const material = new THREE.MeshLambertMaterial({ color: "#333" });
  const wheel = new THREE.Mesh(geometry, material);
  return wheel;
}

function getCarFrontTexture() {
  const canvas = document.createElement("canvas");
  canvas.width = 64;
  canvas.height = 32;
  const context = canvas.getContext("2d");

  context.fillStyle = "#ffffff";
  context.fillRect(0, 0, 64, 32);

  context.fillStyle = carWindowColor || carBaseColor; // Wheel color
  context.fillRect(8, 8, 48, 24);

  return new THREE.CanvasTexture(canvas);
}

function getCarSideTexture() {
  const canvas = document.createElement("canvas");
  canvas.width = 128;
  canvas.height = 32;
  const context = canvas.getContext("2d");

  context.fillStyle = "#ffffff";
  context.fillRect(0, 0, 128, 32);

  context.fillStyle = carWindowColor || carBaseColor; // Wheel color
  context.fillRect(10, 8, 38, 24);
  context.fillRect(58, 8, 60, 24);

  return new THREE.CanvasTexture(canvas);
}
        </script>
    </body>
</html>

我做了一些假设,但我认为你想要做的是在按下 left/right 箭头时沿着 y 轴旋转汽车,并且你试图在按下时向前移动卡片up/down 箭头。如果是这种情况,那么您可以执行 car.translateX() 来创建 forward/backward 运动,然后执行 car.rotation.y 让汽车转弯。

修改后的示例如下:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>ThreeJS Animation</title>
        <style>            
            body {
              margin: 0;
              font-weight: 100;
              float: none;
              overflow: hidden;
            }
            canvas {
              width: 100%;
              height: 100%;
              margin: 0;
              padding: 0;
            }
        </style>
    </head>
    <body>
        <script src="https://threejs.org/build/three.js"></script>
        <script>
let carBottomColor = "#999";
let carTopColor = "#FFF";
let carWindowColor = "#666";
const scene = new THREE.Scene();
scene.background = new THREE.Color("#f1f1f1");
const car = createCar();
scene.add(car);

const ambientLight = new THREE.AmbientLight(0xffffff, 0.6);
scene.add(ambientLight);

const dirLight = new THREE.DirectionalLight(0xffffff, 0.8);
dirLight.position.set(200, 500, 300);
scene.add(dirLight);

const aspectRatio = window.innerWidth / window.innerHeight;
const cameraWidth = 300;
const cameraHeight = cameraWidth / aspectRatio;
const camera = new THREE.OrthographicCamera(
  cameraWidth / -2, // left
  cameraWidth / 2, // right
  cameraHeight / 2, // top
  cameraHeight / -2, // bottom
  200, // near plane
  800 // far plane
);
camera.position.set(200, 200, 200);
camera.lookAt(0, 10, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.render(scene, camera);

let x = 0;
let y = 0;
let keydown = '';
document.body.addEventListener('keydown', e => {
  e.preventDefault();
  keydown = e.key;
});
document.body.addEventListener('keyup', e => {
  keydown = '';
});
const update = () => {
    switch (keydown) {
    case 'ArrowUp':
      car.translateX(1.0);
      break;
    case 'ArrowDown':
      car.translateX(-1.0);
      break;
    case 'ArrowLeft':
      y += 0.1;
      car.rotation.y = y;
      break;
    case 'ArrowRight':
      y -= 0.1;
      car.rotation.y = y;
      break;
  }
  window.requestAnimationFrame(update);
}
window.requestAnimationFrame(update);


renderer.setAnimationLoop(() => {
  renderer.render(scene, camera);
});

document.body.appendChild(renderer.domElement);

function createCar() {
  const car = new THREE.Group();
  const backWheel = createWheels();
  backWheel.position.y = 6;
  backWheel.position.x = -18;
  car.add(backWheel);

  const frontWheel = createWheels();
  frontWheel.position.y = 6;
  frontWheel.position.x = 18;
  car.add(frontWheel);

  const main = new THREE.Mesh(
    new THREE.BoxBufferGeometry(60, 15, 30),
    new THREE.MeshLambertMaterial({ color: carBottomColor })
  );
  main.position.y = 12;
  car.add(main);

  const carFrontTexture = getCarFrontTexture();

  const carBackTexture = getCarFrontTexture();

  const carRightSideTexture = getCarSideTexture();

  const carLeftSideTexture = getCarSideTexture();
  carLeftSideTexture.center = new THREE.Vector2(0.5, 0.5);
  carLeftSideTexture.rotation = Math.PI;
  carLeftSideTexture.flipY = false;

  const cabin = new THREE.Mesh(new THREE.BoxBufferGeometry(33, 12, 24), [
    new THREE.MeshLambertMaterial({ map: carFrontTexture }),
    new THREE.MeshLambertMaterial({ map: carBackTexture }),
    new THREE.MeshLambertMaterial({ color: carTopColor }), // top
    new THREE.MeshLambertMaterial({ color: carTopColor }), // bottom
    new THREE.MeshLambertMaterial({ map: carRightSideTexture }),
    new THREE.MeshLambertMaterial({ map: carLeftSideTexture })
  ]);
  cabin.position.x = -6;
  cabin.position.y = 25.5;
  car.add(cabin);
  return car;
}

function createWheels() {
  const geometry = new THREE.BoxBufferGeometry(12, 12, 33);
  const material = new THREE.MeshLambertMaterial({ color: "#333" });
  const wheel = new THREE.Mesh(geometry, material);
  return wheel;
}

function getCarFrontTexture() {
  const canvas = document.createElement("canvas");
  canvas.width = 64;
  canvas.height = 32;
  const context = canvas.getContext("2d");

  context.fillStyle = "#ffffff";
  context.fillRect(0, 0, 64, 32);

  context.fillStyle = carWindowColor || carBaseColor; // Wheel color
  context.fillRect(8, 8, 48, 24);

  return new THREE.CanvasTexture(canvas);
}

function getCarSideTexture() {
  const canvas = document.createElement("canvas");
  canvas.width = 128;
  canvas.height = 32;
  const context = canvas.getContext("2d");

  context.fillStyle = "#ffffff";
  context.fillRect(0, 0, 128, 32);

  context.fillStyle = carWindowColor || carBaseColor; // Wheel color
  context.fillRect(10, 8, 38, 24);
  context.fillRect(58, 8, 60, 24);

  return new THREE.CanvasTexture(canvas);
}
        </script>
    </body>
</html>