最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - ThreeJS FPS Movement, Stop Velocity When Key Is Released - Stack Overflow

programmeradmin0浏览0评论

For my game I need a simple FPS player character and I have used the ThreeJS FPS example as template for my setup. This is the code used for player WASD movement:

if (this.keyStates['KeyW']) {
    this.playerVelocity.add( getForwardVector().multiplyScalar(speedDelta))
}

and in my update loop I have this:

this.playerVelocity.addScaledVector(this.playerVelocity, damping) // damping is for jumping
const deltaPosition = this.playerVelocity.clone().multiplyScalar(deltaTime)
this.bounds.translate(deltaPosition)
this.camera.position.copy(this.bounds.end)

This works fine but as you can also see in the ThreeJS FPS example, when the key is released the character still moves slightly along before stopping. I'm not entirely against a little bit of easing when movement stops but in this case it's way to extreme, almost like your walking on ice.

How can I stop the movement of the player character once the key is released without sliding along?

For my game I need a simple FPS player character and I have used the ThreeJS FPS example as template for my setup. This is the code used for player WASD movement:

if (this.keyStates['KeyW']) {
    this.playerVelocity.add( getForwardVector().multiplyScalar(speedDelta))
}

and in my update loop I have this:

this.playerVelocity.addScaledVector(this.playerVelocity, damping) // damping is for jumping
const deltaPosition = this.playerVelocity.clone().multiplyScalar(deltaTime)
this.bounds.translate(deltaPosition)
this.camera.position.copy(this.bounds.end)

This works fine but as you can also see in the ThreeJS FPS example, when the key is released the character still moves slightly along before stopping. I'm not entirely against a little bit of easing when movement stops but in this case it's way to extreme, almost like your walking on ice.

How can I stop the movement of the player character once the key is released without sliding along?

Share Improve this question asked Feb 4 at 1:03 MigerMiger 1,2472 gold badges17 silver badges42 bronze badges
Add a comment  | 

1 Answer 1

Reset to default 1

This slipping happens as a result of a gradual reduction in speed through the damping function. To prevent this, you can set a condition to detect no movement input. And reset playerVelocity.x and playerVelocity.z immediately, as a leftover from the damping.

<script type="importmap">
  {
"imports": {
  "three": "https://unpkg/[email protected]/build/three.module.js",
  "three/addons/": "https://unpkg/[email protected]/examples/jsm/"
}
  }
</script>

<div id="container"></div>

<script type="module">

import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { Octree } from 'three/addons/math/Octree.js';
import { OctreeHelper } from 'three/addons/helpers/OctreeHelper.js';
import { Capsule } from 'three/addons/math/Capsule.js';
      
const clock = new THREE.Clock();

const scene = new THREE.Scene();
scene.background = new THREE.Color( 0x88ccee );
scene.fog = new THREE.Fog( 0x88ccee, 0, 50 );

const camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.rotation.order = 'YXZ';

const fillLight1 = new THREE.HemisphereLight( 0x8dc1de, 0x00668d, 1.5 );
fillLight1.position.set( 2, 1, 1 );
scene.add( fillLight1 );

const directionalLight = new THREE.DirectionalLight( 0xffffff, 2.5 );
directionalLight.position.set( -5, 25, -1 );
directionalLight.castShadow = true;
directionalLight.shadow.camera.near = 0.01;
directionalLight.shadow.camera.far = 500;
directionalLight.shadow.camera.right = 30;
directionalLight.shadow.camera.left = -30;
directionalLight.shadow.camera.top = 30;
directionalLight.shadow.camera.bottom = -30;
directionalLight.shadow.mapSize.width = 1024;
directionalLight.shadow.mapSize.height = 1024;
directionalLight.shadow.radius = 4;
directionalLight.shadow.bias = -0.00006;
scene.add( directionalLight );

const container = document.getElementById( 'container' );

const renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.VSMShadowMap;
renderer.toneMapping = THREE.ACESFilmicToneMapping;
container.appendChild( renderer.domElement );

// Physics and movement constants
const GRAVITY = 30;
const STEPS_PER_FRAME = 5;

const playerCollider = new Capsule(
    new THREE.Vector3( 0, 0.35, 0 ),
    new THREE.Vector3( 0, 1, 0 ),
    0.35
);

const playerVelocity = new THREE.Vector3();
const playerDirection = new THREE.Vector3();

let playerOnFloor = false;

const keyStates = {};
const vector1 = new THREE.Vector3();

document.addEventListener( 'keydown', ( event ) => {
    keyStates[ event.code ] = true;
} );

document.addEventListener( 'keyup', ( event ) => {
    keyStates[ event.code ] = false;
} );

// Request pointer lock on mousedown
container.addEventListener( 'mousedown', () => {
    document.body.requestPointerLock();
} );

document.body.addEventListener( 'mousemove', ( event ) => {
    if ( document.pointerLockElement === document.body ) {
        camera.rotation.y -= event.movementX / 500;
        camera.rotation.x -= event.movementY / 500;
    }
} );

window.addEventListener( 'resize', onWindowResize );

function onWindowResize() {
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize( window.innerWidth, window.innerHeight );
}

function playerCollisions() {
    const result = worldOctree.capsuleIntersect( playerCollider );
    playerOnFloor = false;
    if ( result ) {
        playerOnFloor = result.normal.y > 0;
        if ( ! playerOnFloor ) {
            playerVelocity.addScaledVector( result.normal, - result.normal.dot( playerVelocity ) );
        }
        if ( result.depth >= 1e-10 ) {
            playerCollider.translate( result.normal.multiplyScalar( result.depth ) );
        }
    }
}

function updatePlayer( deltaTime ) {
    let damping = Math.exp( - 4 * deltaTime ) - 1;
    if ( ! playerOnFloor ) {
        playerVelocity.y -= GRAVITY * deltaTime;
        // small air resistance
        damping *= 0.1;
    }
    playerVelocity.addScaledVector( playerVelocity, damping );
    const deltaPosition = playerVelocity.clone().multiplyScalar( deltaTime );
    playerCollider.translate( deltaPosition );
    playerCollisions();
    camera.position.copy( playerCollider.end );
}

function getForwardVector() {
    camera.getWorldDirection( playerDirection );
    playerDirection.y = 0;
    playerDirection.normalize();
    return playerDirection;
}

function getSideVector() {
    camera.getWorldDirection( playerDirection );
    playerDirection.y = 0;
    playerDirection.normalize();
    playerDirection.cross( camera.up );
    return playerDirection;
}
// make condition no movement

function controls( deltaTime ) {
    const speedDelta = deltaTime * ( playerOnFloor ? 25 : 8 );
    let moving = false;
    if ( keyStates[ 'KeyW' ] ) {
        playerVelocity.add( getForwardVector().multiplyScalar( speedDelta ) );
      moving = true;
    }
    if ( keyStates[ 'KeyS' ] ) {
        playerVelocity.add( getForwardVector().multiplyScalar( - speedDelta ) );
      moving = true;
    }
    if ( keyStates[ 'KeyA' ] ) {
        playerVelocity.add( getSideVector().multiplyScalar( - speedDelta ) );
      moving = true;
    }

    if ( keyStates[ 'KeyD' ] ) {
        playerVelocity.add( getSideVector().multiplyScalar( speedDelta ) );
      moving = true;
    }
  
    if ( !moving ) {
        playerVelocity.x = 0;
        playerVelocity.z = 0;
    }
    if ( playerOnFloor && keyStates[ 'Space' ] ) {
        playerVelocity.y = 15;
    }
}

const worldOctree = new Octree();
const loader = new GLTFLoader();
loader.load(  'https://raw.githubusercontent/mrdoob/three.js/master/examples/models/gltf/collision-world.glb',
    ( gltf ) => {
        scene.add( gltf.scene );
        worldOctree.fromGraphNode( gltf.scene );
        gltf.scene.traverse( child => {
            if ( child.isMesh ) {
                child.castShadow = true;
                child.receiveShadow = true;
                if ( child.material.map ) {
                    child.material.map.anisotropy = 4;
                }
            }
        } );
        const helper = new OctreeHelper( worldOctree );
        helper.visible = false;
        scene.add( helper );
    }
);

function teleportPlayerIfOob() {
    if ( camera.position.y <= -25 ) {
        playerCollider.start.set( 0, 0.35, 0 );
        playerCollider.end.set( 0, 1, 0 );
        playerCollider.radius = 0.35;
        camera.position.copy( playerCollider.end );
        camera.rotation.set( 0, 0, 0 );
    }
}

function animate() {
    const deltaTime = Math.min( 0.05, clock.getDelta() ) / STEPS_PER_FRAME;
    for ( let i = 0; i < STEPS_PER_FRAME; i++ ) {
        controls( deltaTime );
        updatePlayer( deltaTime );
        teleportPlayerIfOob();
    }
    renderer.render( scene, camera );
}   

</script>

发布评论

评论列表(0)

  1. 暂无评论