Three.js 是一个 3D 库,该系列是在使用 three.js 中的一些过程和笔记。本文记录添加按键事件,来控制照相机前后左右移动的基本过程。

# 添加移动控制

# 基本思路

这里我们将添加按键的处理,来控制第一人称(照相机)的移动,基本思路为:

  • 添加监听事件
  • 进行按键处理
  • 进行照相机移动

# 添加监听事件

// 添加按键时走动
document.addEventListener("keydown", handleKeyDown, false);

这里我们监听的是keydown事件,键盘事件有三个,事件执行的顺序是: keydown->keypress->keyup。

但是连续按一个按键的话,会一直触发:keydown keypress。直到你提起按键,会触发 keyup。

而这里我们不使用 keypress,是因为 keypress 对: shift、ctrl、上下键等非字符的输入不会触发。

# 进行按键处理

这里我们需要进行兼容性处理:

  1. event
  2. keyCode
var event = event || window.event;

IE 中有window.event对象,firefox 没有window.event对象。 可以通过给函数的参数传递 event 对象。

var keyCode = event.keyCode
  ? event.keyCode
  : event.which
  ? event.which
  : event.charCode;

IE 使用e.keyCode获取按键,FireFox 和 Opera 等使用e.which获取按键。

然后我们添加按键判断和处理:

// 处理按键
function handleKeyDown(e) {
  var e = e || window.event;
  var keyCode = event.keyCode
    ? event.keyCode
    : event.which
    ? event.which
    : event.charCode;

  if ("37, 38, 39, 40, 65, 87, 68, 83".indexOf(keyCode) === -1) {
    return;
  } else {
    switch (e.keyCode) {
      case 37:
      case 65:
        CameraMove("x", -0.1);
        break;
      case 38:
      case 87:
        CameraMove("y", 0.1);
        break;
      case 39:
      case 68:
        CameraMove("x", 0.1);
        break;
      case 83:
      case 40:
        CameraMove("y", -0.1);
        break;
    }
  }
}

这里的 CameraMove 是用来处理 camera 的移动的。

# 进行照相机移动

function CameraMove(direction, distance) {
  camera.position[direction] += distance;
}

这时候,我们就能通过上下左右或者是 WASD 按键控制照相机的移动。

如图: image

移动后: image

# 完整代码

// 设置场景
var scene = new THREE.Scene();

// 创建正交投影照相机
var camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  1000
);
camera.position.set(0, -5, 2);
camera.lookAt(scene.position);

// 定义着色器
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 告诉渲染器渲染阴影
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;

// 添加地板
function initFloor() {
  var floorGeo = new THREE.PlaneBufferGeometry(12, 8, 1, 1);
  var floorMaterial = new THREE.MeshStandardMaterial({ color: "#aaaaaa" });
  var floor = new THREE.Mesh(floorGeo, floorMaterial);
  floor.position.set(0, 0, -1);
  floor.receiveShadow = true; // 接受阴影
  scene.add(floor);
  return floor;
}
var floor = initFloor();

// 添加物体
function initCube(imageUrl) {
  var geometry = new THREE.BoxGeometry(1, 1, 1);
  var material;
  if (imageUrl) {
    material = new THREE.MeshLambertMaterial({
      map: THREE.ImageUtils.loadTexture(imageUrl)
    });
  } else {
    material = new THREE.MeshLambertMaterial();
  }
  var cube = new THREE.Mesh(geometry, material);
  cube.castShadow = true; // 要产生阴影
  scene.add(cube);
  return cube;
}
// 添加物体
var cube1 = initCube("./img/1.jpg");
var cube2 = initCube("./img/2.png");
var cube3 = initCube();
var cube4 = initCube();
cube1.position.set(2, 0, 0);
cube2.position.set(-2, 0, 0);
cube3.position.set(0, -2, 1);
cube4.position.set(1, 1, 3);

// 添加聚光灯光源
function initLight() {
  var light = new THREE.SpotLight(0xffffff);
  light.position.set(0, -3, 4);
  light.target = floor; // 投射方向指向地板
  light.castShadow = true; // 用于产生阴影
  scene.add(light);
  // 光源的阴影设置
  light.shadow.mapSize.width = 512; // default
  light.shadow.mapSize.height = 512; // default
  light.shadow.camera.near = 0.5; // default
  light.shadow.camera.far = 500; // default
  var helper = new THREE.CameraHelper(light.shadow.camera);
  scene.add(helper);
}
initLight();

// 让世界动起来
function render() {
  requestAnimationFrame(render);

  // 此处可添加动画处理
  cube1.rotation.x += 0.03;
  cube1.rotation.y += 0.03;

  cube2.rotation.x += 0.02;
  cube3.rotation.y += 0.01;
  cube4.rotation.x -= 0.04;

  renderer.render(scene, camera);
}
// 添加文字
var loader = new THREE.FontLoader();
loader.load("./js/font.json", function(font) {
  var mesh = new THREE.Mesh(
    new THREE.TextGeometry("Please press Up/Down/Ledt/Right or W/A/S/D", {
      font: font,
      size: 0.4,
      height: 0.1
    }),
    new THREE.MeshLambertMaterial()
  );
  mesh.position.set(-5, 2, 2);
  mesh.rotation.set(1.2, 0, 0);
  scene.add(mesh);

  render();
});

// 添加按键时走动
document.addEventListener("keydown", handleKeyDown, false);

// 处理按键
function handleKeyDown(e) {
  var e = e || window.event;
  var keyCode = event.keyCode
    ? event.keyCode
    : event.which
    ? event.which
    : event.charCode;

  if ("37, 38, 39, 40, 65, 87, 68, 83".indexOf(keyCode) === -1) {
    return;
  } else {
    switch (e.keyCode) {
      case 37:
      case 65:
        CameraMove("x", -0.1);
        break;
      case 38:
      case 87:
        CameraMove("y", 0.1);
        break;
      case 39:
      case 68:
        CameraMove("x", 0.1);
        break;
      case 83:
      case 40:
        CameraMove("y", -0.1);
        break;
    }
  }
}

function CameraMove(direction, distance) {
  camera.position[direction] += distance;
}

# 结束语

这节主要讲了给第一人称(照相机)添加按键事件处理的基本过程。这里我们只考虑了前后左右的移动,包括视觉角度等的都没有进行计算和控制,后面章节我们会把这些方面也一并添加进去处理。
此处查看项目代码 (opens new window)
此处查看页面效果 (opens new window)

部分文章中使用了一些网站的截图,如果涉及侵权,请告诉我删一下谢谢~
温馨提示喵