ThreeJS第一人称视角处理

简介

第一人称控件指针锁定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;
}

举例:

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

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

    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 );
    },

你可能感兴趣的:(WEBGL/ThreeJS,three.js)