ThreeJS first person perspective processing

Posted by mummana on Wed, 05 Jan 2022 10:31:46 +0100

brief introduction

The first person control pointer locking API allows you to lock the mouse or other pointer devices in the game interface, so that you can obtain the coordinate change value without absolutely positioning the cursor, so as to accurately judge what the user is doing, and also prevent the user from accidentally entering another screen or somewhere else, resulting in misoperation.

There is a control to implement the first person operation in ThreeJs FirstPersonControl , but there are some disadvantages in using this control; So we use it here PointerLockControls Wrote a control.

Common method properties

Method propertiesdescribe
getObject().translateXLock the control object, which allows you to lock the position of the control
isLockedDetermine whether the current mouse is locked
LockMouse lock start

usage method:

1. First, you need to introduce the PointerLockControls control file

2. Create a PointerLockControls object, pass in the camera and put it in our scene
let controls = new THREE.PointerLockControls( camera );
scene.add(controls );

3. The mouse lock can only be triggered by the user, so you need to add a dom tag on the page and lock it after clicking

Click control to implement the following method lock();

When we lock this control, the mouse movement will only change the viewing angle in the scene. We can't select anything else. Quit and click Esc on the keyboard.

4. The instantiated object can get the control object through getObject(), and its position can be set to adjust the position of entering the scene. Finally, the object is placed in the scene.

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

5. Finally, in render, we let controls call the update function to update

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

Here, let's pay attention to controls getObject(). Translatex (velocity. X * delta) method and direct position change
controls. getObject(). position. For the difference between X + = (velocity. X * delta), the translate method will readjust the front, rear, left and right according to the viewing angle, while the direct modification position will be subject to the first viewing angle, and the main viewing angle will not be modified.

Realize key movement

To adjust the position of the controller through the keyboard case, we first need to listen to events, so we listen to keyboard pressing events and keyboard lifting events:

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

When the mouse is pressed, it starts moving

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

When the mouse is raised, stop moving

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

Finally, in render rendering, judge the current movement direction and realize the current movement,

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

Simple collision detection using Raycaster

For example, when we hit a wall in the scene, we can't cross the wall. Here we need to use collision detection. The principle he uses is that if it is selected, it means to contact him. If the contact is set, it can't go any further. This is such a detection. For example, we only use up-down (vertical) collision.

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

give an example:

Step 1: add a simulated tour button

<button id="tour" type="button">Simulated Tour</button>

Step 2: click the button to lock the first person view tour control.

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

Step 3:

function Store3D() {
    this.spriteIsShow=1; // There are sprite systems, such as labels
}

Turn on mouse lock

    Store3D.prototype.lockControl=function() {
        if(this.spriteIsShow==1) // If there is energy in the scene during locking, the system will report an error 
        {
            this.changeSpriteShow(); // Therefore, before locking, call the method to hide the wizard system. The specific wizard system can understand it by itself
        }
        // Because we want to simulate the first person entering the scene, we change the position of the camera
        this.camera.position.y = 100; 
        // Change the camera's angle of view
        this.camera.lookAt(0,100,0);
        // Simulate human eyes. Human eyes are equivalent to the current position of the camera
        this.lockcontrols.getObject().position.x =0;
        this.lockcontrols.getObject().position.y =100;
        this.lockcontrols.getObject().position.z =580;
        this.lockcontrols.lock(); // Call lock to lock the mouse control
    },

Step 4: first person perspective movement

Store3D.prototype.firstPersonMove=function(){
    // If it is currently locked, move the first view
    if ( this.lockcontrols.isLocked === true ) {
        // First, set up rays for collision test
        // The initial position of the ray is the position of the first person object, so its direction is the direction of the object in front of me
        this.raycaster.ray.origin.copy( this.lockcontrols.getObject().position );
        // For example, we only detect the vertical direction
        // Then the position he is facing is down from the current position
        this.raycaster.ray.origin.y -= 10;
        // Check whether they intersect. If they intersect, put all objects into the variable intersections
        var intersections = this.raycaster.intersectObjects( this.objects );
        // If intersections If the length is greater than 0, it indicates that it intersects with the following objects. At this time, set onObject to true
        var onObject = intersections.length > 0;
        // Get the last interval every time, because the time of each call to the loop function is different according to different performance
        var time = performance.now();
        // In order to prevent performance from affecting the speed of operation, we set it to a factor. If the performance is high, the interval is short
        var delta = ( time - this.prevTime ) / 1000;
        // Set the (10) deceleration factor. The faster he is, the slower the whole process will be, and the faster the deceleration process will be
        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-axis jump speed 100.0 = mass 
        
        // The variable that controls the direction when the mouse is operated
        // If a positive number is forward, a negative number is backward
        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(); 
        //Note the velocity vector, which is a buffer value. In order to ensure that the scene does not pause directly after the mouse is raised, but has a short transition effect:
        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;

// If onObject is true,
        if ( onObject === true ) {
            // At this time, you need to set the speed of the y-axis to 0 and the maximum value of the previous speed
            this.velocity.y = Math.max( 0, this.velocity.y );
            this.canJump = true;
        }
// Calculate the distance of each movement on the X, y and Z axes
        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 );
// In order to ensure that I am always above the ground, I set y equal to 100 when y is less than 100
        if ( this.lockcontrols.getObject().position.y < 100 ) {
            this.velocity.y = 0;
            this.lockcontrols.getObject().position.y = 100;
            this.canJump = true;
        }
        this.prevTime = time;
    }
},

Step 5: set the mouse control

    /**
     * Initialize PointLockControl
     * Set mouse control
     */
    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 );
        // Set keyboard press listening event
        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;
            }
        };
        // Set keyboard monitoring events
        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 );
    },

Topics: Three.js