Browse Source

FirstPersonControls: Make keyboard movement world axis aligned.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Mr.doob 1 week ago
parent
commit
f88964a17e
1 changed files with 40 additions and 16 deletions
  1. 40 16
      examples/jsm/controls/FirstPersonControls.js

+ 40 - 16
examples/jsm/controls/FirstPersonControls.js

@@ -262,25 +262,51 @@ class FirstPersonControls extends Controls {
 
 		if ( this.enabled === false ) return;
 
-		const moveForward = this._keyForward || this._pointerForward;
-		const moveBackward = this._keyBackward || this._pointerBackward;
+		let drive = ( this._keyForward ? 1 : 0 ) - ( this._keyBackward ? 1 : 0 );
+		let lookMove = ( this._pointerForward ? 1 : 0 ) - ( this._pointerBackward ? 1 : 0 );
 
-		const forward = moveForward || ( this.autoForward && ! moveBackward );
-
-		// target velocity in the object's local space
-
-		_targetVelocity.set(
-			( this._moveRight ? 1 : 0 ) - ( this._moveLeft ? 1 : 0 ),
-			( this._moveUp ? 1 : 0 ) - ( this._moveDown ? 1 : 0 ),
-			( moveBackward ? 1 : 0 ) - ( forward ? 1 : 0 )
-		).multiplyScalar( this.movementSpeed );
+		if ( this.autoForward && drive === 0 && lookMove === 0 ) lookMove = 1;
 
 		// faster forward movement the higher the camera is
 
-		if ( forward && this.heightSpeed ) {
+		let forwardSpeed = this.movementSpeed;
+
+		if ( this.heightSpeed ) {
 
 			const y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
-			_targetVelocity.z -= ( y - this.heightMin ) * this.heightCoef;
+			forwardSpeed += ( y - this.heightMin ) * this.heightCoef;
+
+		}
+
+		// target velocity in world space: keys are world axis aligned, moving in the
+		// XZ plane from the camera's yaw only (Q / E along world Y), while pointer
+		// and touch input moves along the look direction
+
+		const yaw = MathUtils.degToRad( this._lon );
+		const sinYaw = Math.sin( yaw );
+		const cosYaw = Math.cos( yaw );
+
+		let strafe = ( this._moveRight ? 1 : 0 ) - ( this._moveLeft ? 1 : 0 );
+		let climb = ( this._moveUp ? 1 : 0 ) - ( this._moveDown ? 1 : 0 );
+
+		// normalize combined key input so diagonal movement isn't faster
+
+		const keyScale = 1 / Math.max( 1, Math.sqrt( strafe * strafe + climb * climb + drive * drive ) );
+
+		strafe *= this.movementSpeed * keyScale;
+		climb *= this.movementSpeed * keyScale;
+		drive *= ( drive > 0 ? forwardSpeed : this.movementSpeed ) * keyScale;
+
+		_targetVelocity.set(
+			sinYaw * drive - cosYaw * strafe,
+			climb,
+			cosYaw * drive + sinYaw * strafe
+		);
+
+		if ( lookMove !== 0 ) {
+
+			_lookDirection.set( 0, 0, - 1 ).applyQuaternion( this.object.quaternion );
+			_targetVelocity.addScaledVector( _lookDirection, lookMove * ( lookMove > 0 ? forwardSpeed : this.movementSpeed ) );
 
 		}
 
@@ -288,9 +314,7 @@ class FirstPersonControls extends Controls {
 
 		this._velocity.lerp( _targetVelocity, this.dampingFactor );
 
-		this.object.translateX( this._velocity.x * delta );
-		this.object.translateY( this._velocity.y * delta );
-		this.object.translateZ( this._velocity.z * delta );
+		this.object.position.addScaledVector( this._velocity, delta );
 
 		let verticalLookRatio = 1;
 

粤ICP备19079148号