最全的免费PDF处理软件 中文艺术字体 WEBRTC视频会议 ESP32智能小车 多功能PDF,流水码,二维码,条码 Jpa 使用Example模糊查询,多字段模糊查询 使用Spring Data JPA的查询方法命名规则,查询多条记录的一条记录可以通过以下方式实现 Spring Data JPA中,如果你想根据某个特定值查询单条记录 JSP JpaRepository 模糊查询的几种方式 pdf.js Nginx的 MIME TYPE问题导致的mjs文件加载出错的问题解决 The server responded with a non-JavaScript MIME type of "application/octet-stream". Vue.js中实现拖拽功 在使用JPA的JpaRepository进行模糊查询时,可以使用Like关键字,也可以使用Containing关键字。 three.js 设计一个类似红警的摄像机代码 three.js第三人称控制类代码 要使用FFmpeg将两个视频合成为一个视频 ffmpeg一个文件拆分成多个文件 ffmpeg调用方法,ffmpeg基础命令 java 语音播报 js监听页面所有网络请求 Three.js中文手册 Springboot多数据源配置 国外服务器网站会被百度收录吗?影响百度收录的情况有哪些 Spring Boot中内置Tomcat最大连接数、线程数与等待数 实践调优 jquery附件上传 java开发工具 联系我们 小张介绍 视频会议 文档操作
扫码关注公众号了解更多内容 扫码了解更多

three.js第三人称控制类代码

发布时间: 2023-09-22 预览次数:

以下是一个基本的three.js第三人称控制类代码的示例:

class ThirdPersonControls {
  constructor(camera, target, options = {}) {
    this.camera = camera;
    this.target = target;
    this.enabled = true;
    this.distance = options.distance || 10;
    this.minDistance = options.minDistance || 0.1;
    this.maxDistance = options.maxDistance || Infinity;
    this.minPolarAngle = options.minPolarAngle || 0; // 最小极角
    this.maxPolarAngle = options.maxPolarAngle || Math.PI; // 最大极角
    this.minAzimuthAngle = options.minAzimuthAngle || -Infinity; // 最小方位角
    this.maxAzimuthAngle = options.maxAzimuthAngle || Infinity; // 最大方位角
    this.rotateSpeed = options.rotateSpeed || 1;
    this.zoomSpeed = options.zoomSpeed || 1;
    this.panSpeed = options.panSpeed || 1;
    this.enableRotate = options.enableRotate !== undefined ? options.enableRotate : true;
    this.enableZoom = options.enableZoom !== undefined ? options.enableZoom : true;
    this.enablePan = options.enablePan !== undefined ? options.enablePan : true;
    this.keys = options.keys || {
      LEFT: 37, // 左箭头键
      UP: 38, // 上箭头键
      RIGHT: 39, // 右箭头键
      BOTTOM: 40 // 下箭头键
    };
    this.mouseButtons = options.mouseButtons || {
      ORBIT: THREE.MOUSE.LEFT, // 鼠标左键
      ZOOM: THREE.MOUSE.MIDDLE, // 鼠标中键
      PAN: THREE.MOUSE.RIGHT // 鼠标右键
    };

    this.target0 = this.target.clone();
    this.position0 = this.camera.position.clone();
    this.zoom0 = this.camera.zoom;

    this.rotateStart = new THREE.Vector2();
    this.rotateEnd = new THREE.Vector2();
    this.rotateDelta = new THREE.Vector2();

    this.panStart = new THREE.Vector2();
    this.panEnd = new THREE.Vector2();
    this.panDelta = new THREE.Vector2();

    this.dollyStart = new THREE.Vector2();
    this.dollyEnd = new THREE.Vector2();
    this.dollyDelta = new THREE.Vector2();

    this.update();

    this.domElement.addEventListener('mousedown', this.onMouseDown.bind(this), false);
    this.domElement.addEventListener('mousewheel', this.onMouseWheel.bind(this), false);
    this.domElement.addEventListener('DOMMouseScroll', this.onMouseWheel.bind(this), false); // 兼容Firefox
    this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false);
    this.domElement.addEventListener('mouseup', this.onMouseUp.bind(this), false);
    this.domElement.addEventListener('mouseout', this.onMouseUp.bind(this), false);
    this.domElement.addEventListener('keydown', this.onKeyDown.bind(this), false);
    this.domElement.addEventListener('keyup', this.onKeyUp.bind(this), false);
  }

  update() {
    const offset = new THREE.Vector3();
    const quat = new THREE.Quaternion().setFromUnitVectors(
      this.camera.up, new THREE.Vector3(0, 1, 0)
    );
    const quatInverse = quat.clone().inverse();

    const lastPosition = new THREE.Vector3();
    const lastQuaternion = new THREE.Quaternion();

    return function update() {
      const position = this.camera.position;

      offset.copy(position).sub(this.target);

      offset.applyQuaternion(quat);

      // 方位角和极角
      const azimuthAngle = Math.atan2(offset.x, offset.z);
      const polarAngle = Math.acos(Math.max(-1, Math.min(1, offset.y / this.distance)));

      azimuthAngle += this.rotateDelta.x * this.rotateSpeed;
      polarAngle += this.rotateDelta.y * this.rotateSpeed;

      polarAngle = Math.max(this.minPolarAngle, Math.min(this.maxPolarAngle, polarAngle));
      azimuthAngle = Math.max(this.minAzimuthAngle, Math.min(this.maxAzimuthAngle, azimuthAngle));

      const radius = offset.length() * Math.cos(polarAngle);

      offset.x = radius * Math.sin(polarAngle) * Math.sin(azimuthAngle);
      offset.y = radius * Math.cos(polarAngle);
      offset.z = radius * Math.sin(polarAngle) * Math.cos(azimuthAngle);

      offset.applyQuaternion(quatInverse);

      position.copy(this.target).add(offset);

      this.camera.lookAt(this.target);

      this.rotateDelta.set(0, 0);
    };
  }

  onMouseDown(event) {
    if (!this.enabled) return;

    event.preventDefault();

    if (event.button === this.mouseButtons.ORBIT) {
      if (this.enableRotate === false) return;

      this.rotateStart.set(event.clientX, event.clientY);
    } else if (event.button === this.mouseButtons.ZOOM) {
      if (this.enableZoom === false) return;

      this.dollyStart.set(event.clientX, event.clientY);
    } else if (event.button === this.mouseButtons.PAN) {
      if (this.enablePan === false) return;

      this.panStart.set(event.clientX, event.clientY);
    }

    this.domElement.addEventListener('mousemove', this.onMouseMove.bind(this), false);
    this.domElement.addEventListener('mouseup', this.onMouseUp.bind(this), false);
    this.domElement.addEventListener('mouseout', this.onMouseUp.bind(this), false);
  }

  onMouseMove(event) {
    if (!this.enabled) return;

    event.preventDefault();

    if (event.button === this.mouseButtons.ORBIT) {
      if (this.enableRotate === false) return;

      this.rotateEnd.set(event.clientX, event.clientY);
      this.rotateDelta.subVectors(this.rotateEnd, this.rotateStart);

      this.rotateStart.copy(this.rotateEnd);
    } else if (event.button === this.mouseButtons.ZOOM) {
      if (this.enableZoom === false) return;

      this.dollyEnd.set(event.clientX, event.clientY);
      this.dollyDelta.subVectors(this.dollyEnd, this.dollyStart);

      if (this.dollyDelta.y > 0) {
        this.zoomIn();
      } else if (this.dollyDelta.y < 0) {
        this.zoomOut();
      }

      this.dollyStart.copy(this.dollyEnd);
    } else if (event.button === this.mouseButtons.PAN) {
      if (this.enablePan === false) return;

      this.panEnd.set(event.clientX, event.clientY);
      this.panDelta.subVectors(this.panEnd, this.panStart);

      this.pan(this.panDelta.x, this.panDelta.y);

      this.panStart.copy(this.panEnd);
    }
  }

  onMouseUp(event) {
    if (!this.enabled) return;

    event.preventDefault();

    this.domElement.removeEventListener('mousemove', this.onMouseMove.bind(this), false);
    this.domElement.removeEventListener('mouseup', this.onMouseUp.bind(this), false);
    this.domElement.removeEventListener('mouseout', this.onMouseUp.bind(this), false);
  }

  onMouseWheel(event) {
    if (!this.enabled || this.enableZoom === false || (this.state !== STATE.NONE && this.state !== STATE.ROTATE)) return;

    event.preventDefault();
    event.stopPropagation();

    const delta = 0;

    if (event.wheelDelta !== undefined) {
      delta = event.wheelDelta;
    } else if (event.detail !== undefined) {
      delta = -event.detail;
    }

    if (delta > 0) {
      this.zoomOut();
    } else if (delta < 0) {
      this.zoomIn();
    }
  }

  onKeyDown(event) {
    if (!this.enabled || !this.enableKeys || !this.enablePan) return;

    switch (event.keyCode) {
      case this.keys.UP:
        this.pan(0, this.panSpeed);
        break;
      case this.keys.BOTTOM:
        this.pan(0, -this.panSpeed);
        break;
      case this.keys.LEFT:
        this.pan(this.panSpeed, 0);
        break;
      case this.keys.RIGHT:
        this.pan(-this.panSpeed, 0);
        break;
    }
  }

  onKeyUp(event) {
    if (!this.enabled || !this.enableKeys || !this.enablePan) return;

    switch (event.keyCode) {
      case this.keys.UP:
      case this.keys.BOTTOM:
        this.pan(0, 0);
        break;
      case this.keys.LEFT:
      case this.keys.RIGHT:
        this.pan(0, 0);
        break;
    }
  }

  zoomIn() {
    const factor = Math.pow(0.95, this.zoomSpeed);

    if (this.camera.isPerspectiveCamera) {
      this.distance *= factor;
      this.distance = Math.max(this.minDistance, Math.min(this.maxDistance, this.distance));
    } else if (this.camera.isOrthographicCamera) {
      this.camera.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.camera.zoom * factor));
      this.camera.updateProjectionMatrix();
    }
  }

  zoomOut() {
    const factor = Math.pow(0.95, this.zoomSpeed);

    if (this.camera.isPerspectiveCamera) {
      this.distance /= factor;
      this.distance = Math.max(this.minDistance, Math.min(this.maxDistance, this.distance));
    } else if (this.camera.isOrthographicCamera) {
      this.camera.zoom = Math.max(this.minZoom, Math.min(this.maxZoom, this.camera.zoom / factor));
      this.camera.updateProjectionMatrix();
    }
  }

  pan(deltaX, deltaY) {
    const offset = new THREE.Vector3();
    const te = this.camera.matrix.elements;

    offset.set(
      te[0] * deltaX + te[4] * deltaY,
      te[1] * deltaX + te[5] * deltaY,
      te[2] * deltaX + te[6] * deltaY
    );

    this.target.add(offset);
    this.camera.position.add(offset);
  }
}
在使用该控制类时,你需要传入相机对象和目标对象,并可以通过options参数来设置一些控制选项。例如:

复制代码
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const target = new THREE.Vector3(0, 0, 0);

const controls = new ThirdPersonControls(camera, target, {
  distance: 10,
  minDistance: 0.1,
  maxDistance: 20,
  minPolarAngle: 0,
  maxPolarAngle: Math.PI,
  minAzimuthAngle: -Infinity,
  maxAzimuthAngle: Infinity,
  rotateSpeed: 1,
  zoomSpeed: 1,
  panSpeed: 1,
  enableRotate: true,
  enableZoom: true,
  enablePan: true,
  keys: {
    LEFT: 37,
    UP: 38,
    RIGHT: 39,
    BOTTOM: 40
  },
  mouseButtons: {
    ORBIT: THREE.MOUSE.LEFT,
    ZOOM: THREE.MOUSE.MIDDLE,
    PAN: THREE.MOUSE.RIGHT
  }
});

function animate() {
  requestAnimationFrame(animate);

  controls.update();

  renderer.render(scene, camera);
}
这样,你就可以通过鼠标和键盘来控制相机的位置和视角了。