ThreeJS第一人称视角处理

2023-11-02

简介

第一人称控件指针锁定API允许您在游戏界面中锁定鼠标或其他指针设备,以便您不用绝对定位光标就可以获得坐标变化值,从而准确地判断用户正在做什么,并且还可以防止用户意外地进入另一块屏幕或别的什么地方,从而导致误操作。

在ThreeJs中实现第一人称操作有一个控件FirstPersonControl, 但是就这个控件使用起来有一些弊端; 所以我们这边使用PointerLockControls写了一个控件。

常用方法属性

方法属性 描述
getObject().translateX 锁定控件对象,可通过此设置锁定控件的位置
isLocked 判断当前鼠标是否锁定
Lock 鼠标锁定开始

使用方法:

1、首先需要引入 PointerLockControls 控件文件

2、创建PointerLockControls对象,并把相机传入,放到我们的场景中
let controls = new THREE.PointerLockControls( camera );
scene.add(controls );

3、鼠标锁定只有通过用户才能触发,所以需要在页面上添加dom标签,并实现点击之后锁定

点击要实现如下方法 control.lock();

当我们锁定了这个控件之后,鼠标移动只会改变在场景中的视角,是不能够选中其他东西的,退出点击键盘Esc键。

4、通过实例化的对象可以通过 getObject() 获取到控制对象,可以设置它的位置来调整进入场景的位置。最后将对象放置到 scene 场景当中。

controls.getObject().position.y = 50;
controls.getObject().position.x = 100;
scene.add( controls.getObject() );

5、最后,在 render 当中,我们让 controls 调用 update 函数实现更新

controls.getObject().translateX( velocity.x * delta );
controls.getObject().translateY( velocity.y * delta );
controls.getObject().translateZ( velocity.z * delta );

这里我们注意一下controls.getObject().translateX( velocity.x * delta )方法和直接改变位置
controls.getObject().position.x += ( velocity.x * delta )的区别,translate方法会根据视角重新调整前后左右,而直接修改位置则以最先视角准,不会修改主视角。

实现按键移动

我们要实现可以通过键盘案例进行控制器位置调整,首先就是要监听事件,所以我们监听的键盘按下事件和键盘抬起事件:

document.addEventListener( 'keydown', onKeyDown, false );
document.addEventListener( 'keyup', onKeyUp, false );

当鼠标按下时,开始移动

var onKeyDown = function ( event ) {
  switch ( event.keyCode ) {
  case 38: // up
  case 87: // w
  moveForward = true;
  break;
  case 37: // left
  case 65: // a
  moveLeft = true;
  break;
  case 40: // down
  case 83: // s
  moveBackward = true;
  break;
  case 39: // right
  case 68: // d
  moveRight = true;
  break;
  case 32: // space
  if ( canJump === true ) velocity.y += 350;
    canJump = false;
    break;
  }
};

当鼠标抬起来时,停止移动

var onKeyUp = function ( event ) {
switch ( event.keyCode ) {
case 38: // up
case 87: // w
moveForward = false;
break;
case 37: // left
case 65: // a
moveLeft = false;
break;
case 40: // down
case 83: // s
moveBackward = false;
break;
case 39: // right
case 68: // d
moveRight = false;
break;
}
};

最后,在 render 渲染中,进行判断当前的移动方向,实现的当前的移动,

var time = performance.now();
var delta = ( time - prevTime ) / 1000;
velocity.x -= velocity.x * 10.0 * delta;
velocity.z -= velocity.z * 10.0 * delta;
velocity.y -= 9.8 * 100.0 * delta; // 100.0 = mass
direction.z = Number( moveForward ) - Number( moveBackward );
direction.x = Number( moveLeft ) - Number( moveRight );
direction.normalize(); // this ensures consistent movements in all directions
if ( moveForward || moveBackward ) velocity.z -= direction.z * 400.0 * delta;
if ( moveLeft || moveRight ) velocity.x -= direction.x * 400.0 * delta;
controls.getObject().translateX( velocity.x * delta );
//controls.getObject().position.x += ( velocity.x * delta );
controls.getObject().translateY( velocity.y * delta );
//controls.getObject().position.y += ( velocity.y * delta ); // new behavior
//controls.getObject().position.z += ( velocity.z * delta ); // new behavior
controls.getObject().translateZ( velocity.z * delta );

使用Raycaster实现简单的碰撞检测

比如当我们在场景中撞到墙的时候是不能穿越墙的,这里就要用到碰撞检测。他所用的原理就是如果选中了就代表着要和他接触,如果接触就设定不能再往前了,就是这样一个检测,我们这里举例只使用了上下(垂直方向)碰撞。

raycaster.ray.origin.copy( controls.getObject().position );
raycaster.ray.origin.y -= 10;
var intersections = raycaster.intersectObjects( objects );
var onObject = intersections.length > 0;
if ( onObject === true ) {
velocity.y = Math.max( 0, velocity.y );
canJump = true;
}
if ( controls.getObject().position.y < 10 ) {
velocity.y = 0;
controls.getObject().position.y = 10;
canJump = true;
}

举例:

第一步:添加模拟游览按钮

<button id="tour" type="button">模拟游览</button>

第二步:点击按钮,锁定第一人称视角游览控件

    document.getElementById("tour").addEventListener('click', function () {
        store.lockControl();
    }, false);

第三步:

function Store3D() {
    this.spriteIsShow=1; // 存在精灵系统 如:标签之类的
}

  开启鼠标锁定

    Store3D.prototype.lockControl=function() {
        if(this.spriteIsShow==1) // 如果锁定的时候场景中有精力系统会报错 
        {
            this.changeSpriteShow(); // 所以锁定之前的时候调用方法 隐藏精灵系统,具体精灵系统自行了解
        }
        // 因为我们要模拟第一人称进入场景 所以我们改变相机的位置
        this.camera.position.y = 100; 
        // 改变相机的视角
        this.camera.lookAt(0,100,0);
        // 模拟人的眼睛 人的眼睛就相当于现在相机的位置
        this.lockcontrols.getObject().position.x =0;
        this.lockcontrols.getObject().position.y =100;
        this.lockcontrols.getObject().position.z =580;
        this.lockcontrols.lock(); // 调用lock这个时候就锁定控件鼠标了
    },

第四步:第一人称视角移动

Store3D.prototype.firstPersonMove=function(){
    // 如果当前是锁定的,就要进行第一视角的移动
    if ( this.lockcontrols.isLocked === true ) {
        // 首先设置射线 用来做碰撞测试
        // 射线的初始位置就是第一人称对象的位置 那么他的方向就是我前面对象的方向
        this.raycaster.ray.origin.copy( this.lockcontrols.getObject().position );
        // 我们这里举例只检测垂直方向
        // 那么他朝向的位置 是当前位置往下
        this.raycaster.ray.origin.y -= 10;
        // 查看是否相交 如果相交将所有物体放入intersections这个变量
        var intersections = this.raycaster.intersectObjects( this.objects );
        // 如果intersections.length大于0 则说明他与下面的物体相交了 这时将onObject设置为true
        var onObject = intersections.length > 0;
        // 每次都获取上一次的间隔时间 因为根据性能不同每次调用循环函数的时间都是不一样的
        var time = performance.now();
        // 为了不让性能影响操作的速度 我们将他设置为一个因数 所谓性能高那么间隔时间就短
        var delta = ( time - this.prevTime ) / 1000;
        // 设置一下(10)减速因子 他越快整个过程中速度就会越慢 减速的过程就会越快
        this.velocity.x -= this.velocity.x * 10.0 * delta;
        this.velocity.z -= this.velocity.z * 10.0 * delta;
        this.velocity.y -= 9.8 * 100.0 * delta; // y轴跳跃的速度 100.0 = mass 
        
        // 鼠标操作的时候控制方向 的变量
        // 如果正数就是往前 负数就是往后
        this.direction.z = Number( this.moveForward ) - Number( this.moveBackward );
        this.direction.x = Number( this.moveLeft ) - Number( this.moveRight );
        // this ensures consistent movements in all directions
        this.direction.normalize(); 
        //注意velocity向量,这是一个缓冲值,为了保证鼠标抬起后,场景不直接暂停,而是有一个简短的过渡效果:
        if ( this.moveForward || this.moveBackward ) this.velocity.z -= this.direction.z * 2000.0 * delta;
        if ( this.moveLeft || this.moveRight ) this.velocity.x -= this.direction.x * 2000.0 * delta;

// 如果onObject为true,
        if ( onObject === true ) {
            // 此时需要将y轴的速度设置为0和之前速度的最大值
            this.velocity.y = Math.max( 0, this.velocity.y );
            this.canJump = true;
        }
// 计算每次在x,y,z轴移动的距离
        this.lockcontrols.getObject().translateX( this.velocity.x * delta );
        this.lockcontrols.getObject().position.y += ( this.velocity.y * delta ); // new behavior
        this.lockcontrols.getObject().translateZ( this.velocity.z * delta );
// 为了保证我永远是高于地面的 所以我设置当y小于100时就让y等于100
        if ( this.lockcontrols.getObject().position.y < 100 ) {
            this.velocity.y = 0;
            this.lockcontrols.getObject().position.y = 100;
            this.canJump = true;
        }
        this.prevTime = time;
    }
},

第五步:设置鼠标控制

    /**
     * 初始化PointLockControl
     * 设置鼠标控制
     */
    Store3D.prototype.initPointLockControl=function(object){
        this.lockcontrols = new THREE.PointerLockControls( this.camera );
        this.raycaster = new THREE.Raycaster( new THREE.Vector3(), new THREE.Vector3( 0, - 1, 0 ), 0, 10 );
        // 设置键盘按下监听事件
        var onKeyDown = function ( event ) {
            switch ( event.keyCode ) {
                case 38: // up
                case 87: // w
                    object.moveForward = true;
                    break;
                case 37: // left
                case 65: // a
                    object.moveLeft = true;
                    break;
                case 40: // down
                case 83: // s
                    object.moveBackward = true;
                    break;
                case 39: // right
                case 68: // d
                    object.moveRight = true;
                    break;
                case 32: // space
                    if ( object.canJump === true ) object.velocity.y += 350;
                    object.canJump = false;
                    break;
            }
        };
        // 设置键盘抬起监听事件
        var onKeyUp = function ( event ) {
            switch ( event.keyCode ) {
                case 38: // up
                case 87: // w
                    object.moveForward = false;
                    break;
                case 37: // left
                case 65: // a
                    object.moveLeft = false;
                    break;
                case 40: // down
                case 83: // s
                    object.moveBackward = false;
                    break;
                case 39: // right
                case 68: // d
                    object.moveRight = false;
                    break;
            }
        };
        document.addEventListener( 'keydown', onKeyDown, false );
        document.addEventListener( 'keyup', onKeyUp, false );
    },

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ThreeJS第一人称视角处理 的相关文章

  • Three.js:基于图像的光照(IBL)

    I m searching to add an IBL to my scene and objects But I can t find anything on the web There are some examples with an
  • 在 Three.js 中使用多种材质来合并几何体

    我想使用 2 个网格创建一棵松树 其中 1 个用于树干 另一个用于灌木 这就是我所做的 var pine geometry new THREE Geometry var pine texture 1 THREE ImageUtils loa
  • THREE.JS,忽略父级的轮换

    我试图使子对象跟随父级位置并表现得像一个普通的子对象 但是我希望它保持其旋转不变 在不影响性能的情况下 最好的方法是什么 我的CPU预算很紧张 已经运行了2个工作线程并且有很多对象 是否有设置只允许孩子的位置受到影响 同样重要的是 当父级旋
  • 解决 Three.js / webGL 中的 gl_PointSize 限制

    我正在使用 Three js 创建交互式数据可视化 此可视化涉及渲染 68000 个节点 其中每个不同的节点具有不同的大小和颜色 最初我尝试通过渲染网格来实现此目的 但事实证明这非常昂贵 我当前的尝试是使用 Three js 粒子系统 每个
  • 渲染 ThreeJS 应用程序第一帧时的性能问题

    目前 当我渲染以下内容时 我的 ThreeJS 应用程序的性能受到很大影响第一帧 它会导致 Edge 和 IE 11 浏览器冻结 5 秒 并弹出窗口指示 此窗口没有响应 这可能会吓到我的用户 使用 Chrome 的性能分析器 问题似乎来自几
  • CSS3DObject 始终位于 WebGL Mesh 前面

    我正在混合CSS3D Renderer with WebGL Renderer to add HTML3D 空间中的元素WebGL场景 这CSS3DObject在前面WebGL网格即使WebGL Renderer具有较高的 z index
  • ThreeJS 中阴影的奇怪行为

    所以我有一个 ThreeJS 场景 并且添加了一些球体 多材质 我还添加了定向光 this light new THREE DirectionalLight 0xFFFFFF 1 this light position set 2 10 2
  • 如何优化 Three.js 中多个 sphereGeometry 的渲染?

    我想优化 Three js 中 sphereGeometry 的渲染 因为它成为我的程序的瓶颈 javascript程序如下所示 var sphereThree for var idSphere 0 idSphere lt numSpher
  • .points 不透明度/大小在三个.js 内

    我回来回答有关 points 的第二个问题 这次想知道如何将不透明度从 0 更改为 1 然后又回到距发射器的特定像素距离内 var particleCount 14 particles new THREE Geometry pMateria
  • 三.js如何通过id(name)获取场景

    我创造了一个场景 如何通过 id name 获取它 喜欢jquery test In three js医生 我刚刚找到scene getObjectById 获取子对象 有一个getObjectByName方法用于此这里在Object3D
  • Three.js - 推开然后恢复“鼠标移动”上的元素位置

    大家好 我正在开发一个项目Three js用户可以在哪里hover一个镶嵌的面 每个网格在与不可见球体相交时都应该被推开 并且当它在其区域之外时返回到其原始位置 我在用this http codepen io soulwire pen Ff
  • 使用 Three.js 中的设备方向控件进行对象旋转

    我正在迈出使用 JavaScript 进行编码并使用 Three js 的第一步 我正在尝试 Threejs org 的这个例子 http trijs org examples misc controls deviceorientation
  • 使用矩阵变换 Three.js 场景图

    我正在尝试将文件中的场景加载到 Three js 中 自定义格式 不是 Three js 支持的格式 这种特殊格式描述了一个场景图 其中树中的每个节点都有一个指定为 4x4 矩阵的变换 将其推送到 Three js 的过程如下所示 Yeah
  • 将 FBX 文件转换为 .gltf 后,模型非常小,为什么?

    问题 将 FBX 文件转换为 gltf 后 模型非常小 为什么 我尝试用以下方法缩放模型frontObject scale set 1000 1000 1000 但我收到以下错误 TypeError Cannot read property
  • 使用 Three.js 制作带孔的圆角盒

    我需要创建一个 4 个侧面但顶部和底部都有圆角的盒子 Three js 有一个很好的例子webgl 几何 形状 https threejs org examples webgl geometry shapes我正在尝试复制它以在绿色圆框上添
  • Three.js、自定义着色器和具有透明度的 png 纹理

    我有一个非常简单的 PNG 纹理 一个带有透明背景的灰色圆圈 我用它作为制服map for a THREE ShaderMaterial var uniforms THREE UniformsUtils merge basicShader
  • Morph 目标为 Three.js

    我正在尝试开始使用变形目标和 Three js 然而 关于这个主题的文档似乎并不多 当我查看源代码时 morphTargetInfluences 似乎是有魔力 这是如何运作的 我该如何使用这个 值为1就是全力吗 如何区分同一模型上的不同变形
  • Three.js WebGL 从着色器绘制圆形自定义填充和边框颜色

    我将 Three js 与 WebGLRenderer 一起使用 我试图找出或查看如何使用 CircleGeometry 绘制圆圈的示例 并能够从顶点或片段着色器控制其填充和边框颜色 如果不使用图像作为纹理 这是否可能 抱歉 如果这真的很简
  • 如何清除 THREE.JS 场景

    我正在尝试找到清除场景中所有对象而不破坏场景本身的方法 我知道命名对象是一种方法 然后当我们想要删除该对象时 我们只需通过它的名称 获取 它即可 但是 我想找到一种快速方法来清除场景中的所有对象 无论它们的名称如何 有简单的方法吗 谢谢 你
  • 三个 JS Orbitcontrols 设置目标而不用 LookAt

    我正在尝试制作一个用于构建模型的 3D 查看器 我们已加载模型并尝试与模型进行某种交互 因此 我们使用 OrbiControls 来旋转 平移和缩放模型 我们希望在查看器中具有这样的行为 当用户单击并拖动 从而旋转 时 旋转中心位于用户单击

随机推荐

  • Android UI架构(十三)--App请求切换帧率(4)之SurfaceFlinger切换帧率.md

    文章目录 参考资料 简述 一 SurfaceFlinger接受帧率变化 1 1 SurfaceFlinger setDesiredActiveConfig 1 2 SurfaceFlinger repaintEverythingForHWC
  • 用C语言编写简化版银行系统:ATM取款机

    1 问题描述 用c语言编写一个简化的银行自动存款系统 适合刚接触C语言 尝试编写100多行代码的初学者作为参考 该代码编写围绕着银行ATM机器的4个业务 分别是查询 取款 存款 修改密码 其中需要两个文件 一个为DrawMoney txt文
  • 阿里巴巴开源限流系统 Sentinel 全解析

    今年下半年阿里开源了自研的限流系统 Sentinel 官方对 Sentinel 的介绍中用到了一系列高大山的名词诸如 限流 熔断降级 流量塑形 系统负载保护等 还有漂亮的形容词诸如 轻巧 专业 实时等 作为技术消费者看到这样的广告词之后禁不
  • 掌握GDB调试工具,轻松排除bug!

    一 什么是GDB gdb是GNU debugger的缩写 是编程调试工具 GDB官网 https www gnu org software gdb GDB适用的编程语言 Ada C C objective c Pascal 等 GDB的工作
  • Python-第三方库requests详解

    Requests 是用Python语言编写 基于 urllib 采用 Apache2 Licensed 开源协议的 HTTP 库 它比 urllib 更加方便 可以节约我们大量的工作 完全满足 HTTP 测试需求 Requests 的哲学是
  • C++中的namespace(using namespace)的详细理解

    在C 语言编写的程序中 变量和函数等的作用范围是有一定限制的 比如 在函数体中定义的一个临时变量就不可以在函数体外使用 为了解决变量和函数等的作用范围 在C 语言中引入了名空间的概念 并增加了关键字namespace和using 在一个名空
  • 《STL源码剖析》(四)——序列式容器

    1 序列式容器 所谓序列式容器 其中的元素都可序 但未必有序 C 本身提供了一个序列式容器array STL另外提供了vector list deque stack queue priority queue等序列式容器 其中stack和qu
  • C++知识总结(内附超详细知识框架图)

    C 知识总结 由于在学习C 的同时博主也在学习Linux系统编程 Linux网络编程以及数据库 所以从开始学习C 到现在结束已经有一年多时间了 C 是博主学习的第一个面向对象的语言 在学习过程中也是逐渐体会到了面向对象编程的种种好处 但C
  • 第十二章 YOLO的部署实战篇(下篇)

    文章目录 前言 专栏概括 1 cuda教程目录 2 cuda教程背景 3 cuda教程内容 一 yolov5部署整体逻辑 1 yolov5的main函数 2 yolo h头文件 3 整体架构显示 二 yolov5模型的onnx转engne代
  • oracle tmp表空间不足,临时表空间不足引起的ORA-01114错误

    数据库程序突遇ORA 01114错误 提示内容如下 错误原因 Select error ORA 01114 将块写入文件 时出现 IO 错误 块 ORA 27069 skgfdisp 尝试在文件范围外执行 I O OSD 04026 无效的
  • Unity + vuforia 开发虚拟按钮

    首先在创建虚拟按钮之前 还是要首先了解imageTarget的创建方式 我自己时参考dzyi的博客弄的 推荐大家 写的很详细 地址 http blog csdn net dzyi article details 22898929 创建虚拟按
  • 有了Auto Layout,为什么你还是害怕写UITabelView的自适应布局?

    Apple 算是最重视应用开发体验的公司了 从Xib到StoryBoard 从Auto Layout到Size Class 每一次的更新 都会给iOS应用的开发带来不小的便利 但是 对于绝对多数iOS攻城狮来说 我们依然还是很害怕写UITa
  • 解决docker pull 很慢的问题

    再docker拉取镜像时 花费时间太长 用如下方法可以解决 root localhost mkdir p etc docker root localhost cat etc docker daemon json registry mirro
  • mysql Undo Tablespaces

    分离 undo 表空间相关的参数 innodb undo directory 指定单独存放 undo 表空间的目录 默认为 即 datadir 可以设置相对路径或者绝对路径 该参数实例初始化之后虽然不可直接改动 但是可 以通过先停库 修改配
  • 联想工作站光盘或WinPE重装Win7找不到SSD和另外一个2T磁盘

    工作站比较老了 苏州重启系统修了 应该是原来的SSD硬盘损坏 修复工具无法修复 只能重装了 由于某一应用软件要求 只能装Win7系统 使用老毛桃制作的WinPE进去看了分区 安装系统时无法找到新的SSD盘和之前的2T磁盘
  • PLC的通信模式

    说到PLC的通讯模式 主要有两种 一种是并行通讯模式 一种是串行通讯模式 这两种通信模式有什么区别呢 PLC串行通信 串行通信一般是用二进制的位 BIT 为单位的数据传输方式 每次都只传送一位 然后除了地线之外 在一个数据传输方向上面只要一
  • element tree树形结构接口不支持搜索、懒加载情况下实现搜索

    目前项目中 element tree树形结构由于数据太多使用了懒加载 并且接口不支持搜索 只能由前端实现搜索功能 暂时按照自己思路实现功能 思路为 输入搜索内容后 直接遍历树形结构的数据 通过filter筛选出结果之后再赋值给树形结构 de
  • matlab径向分布函数作图_分子动力学的径向分布函数绘制

    这是过冷水之前在工作中的一张图片 这张图片的实际含义是以坐标原点为参考点 距离原点距离x处的小球个数f x 分布图 这个问题理解起来好容易 统计距离和小球个数的关系 but实际问题是过冷水要处理的是这样的问题 请问请告诉我随意找一个小球为原
  • 小团队管理核心(一)

    主管应该投身具体业务还是专注于管理 投身业务 or 专注管理 主管应该投身具体业务还是专注于管理 一 主管的定位 二 如何实现老板目标 三 主管应当专注管理 一 主管的定位 定位简述 通过下属实现经营者目标的人 定位解析 由此看来我们的目标
  • ThreeJS第一人称视角处理

    简介 第一人称控件指针锁定API允许您在游戏界面中锁定鼠标或其他指针设备 以便您不用绝对定位光标就可以获得坐标变化值 从而准确地判断用户正在做什么 并且还可以防止用户意外地进入另一块屏幕或别的什么地方 从而导致误操作 在ThreeJs中实现