Просмотр исходного кода

FirstPersonControls: Derive from `Controls`. (#29097)

Michael Herzog 1 год назад
Родитель
Сommit
0983a1a9ed

+ 1 - 31
docs/examples/en/controls/FirstPersonControls.html

@@ -7,6 +7,7 @@
 		<link type="text/css" rel="stylesheet" href="page.css" />
 	</head>
 	<body>
+		[page:Controls] &rarr;
 
 		<h1>[name]</h1>
 
@@ -61,17 +62,6 @@
 			Whether or not looking around is vertically constrained by [[page:.verticalMin], [page:.verticalMax]]. Default is `false`.
 		</p>
 
-		<h3>[property:HTMLDOMElement domElement]</h3>
-		<p>
-			The HTMLDOMElement used to listen for mouse / touch events. This must be passed in the constructor; changing it here will
-			not set up new event listeners.
-		</p>
-
-		<h3>[property:Boolean enabled]</h3>
-		<p>
-			Whether or not the controls are enabled. Default is `true`.
-		</p>
-
 		<h3>[property:Number heightCoef]</h3>
 		<p>
 			Determines how much faster the camera moves when it's y-component is near [page:.heightMax]. Default is *1*.
@@ -113,11 +103,6 @@
 			The movement speed. Default is *1*.
 		</p>
 
-		<h3>[property:Camera object]</h3>
-		<p>
-			The camera to be controlled.
-		</p>
-
 		<h3>[property:Number verticalMax]</h3>
 		<p>
 			How far you can vertically look around, upper limit. Range is 0 to Math.PI radians. Default is `Math.PI`.
@@ -130,11 +115,6 @@
 
 		<h2>Methods</h2>
 
-		<h3>[method:undefined dispose] ()</h3>
-		<p>
-			Should be called if the controls is no longer required.
-		</p>
-
 		<h3>[method:undefined handleResize] ()</h3>
 		<p>
 			Should be called if the application window is resized.
@@ -156,16 +136,6 @@
 			</p>
 		</p>
 
-		<h3>[method:undefined update] ( [param:Number delta] )</h3>
-		<p>
-			<p>
-				[page:Number delta]: Time delta value.
-			</p>
-			<p>
-				Updates the controls. Usually called in the animation loop.
-			</p>
-		</p>
-
 		<h2>Source</h2>
 
 		<p>

+ 175 - 163
examples/jsm/controls/FirstPersonControls.js

@@ -3,22 +3,21 @@ import {
 	Spherical,
 	Vector3
 } from 'three';
+import { Controls } from './Controls.js';
 
 const _lookDirection = new Vector3();
 const _spherical = new Spherical();
 const _target = new Vector3();
+const _targetPosition = new Vector3();
 
-class FirstPersonControls {
+class FirstPersonControls extends Controls {
 
-	constructor( object, domElement ) {
+	constructor( object, domElement = null ) {
 
-		this.object = object;
-		this.domElement = domElement;
+		super( object, domElement );
 
 		// API
 
-		this.enabled = true;
-
 		this.movementSpeed = 1.0;
 		this.lookSpeed = 0.005;
 
@@ -40,283 +39,296 @@ class FirstPersonControls {
 
 		// internals
 
-		this.autoSpeedFactor = 0.0;
+		this._autoSpeedFactor = 0.0;
+
+		this._pointerX = 0;
+		this._pointerY = 0;
 
-		this.pointerX = 0;
-		this.pointerY = 0;
+		this._moveForward = false;
+		this._moveBackward = false;
+		this._moveLeft = false;
+		this._moveRight = false;
 
-		this.moveForward = false;
-		this.moveBackward = false;
-		this.moveLeft = false;
-		this.moveRight = false;
+		this._viewHalfX = 0;
+		this._viewHalfY = 0;
 
-		this.viewHalfX = 0;
-		this.viewHalfY = 0;
+		this._lat = 0;
+		this._lon = 0;
 
-		// private variables
+		// event listeners
 
-		let lat = 0;
-		let lon = 0;
+		this._onPointerMove = onPointerMove.bind( this );
+		this._onPointerDown = onPointerDown.bind( this );
+		this._onPointerUp = onPointerUp.bind( this );
+		this._onContextMenu = onContextMenu.bind( this );
+		this._onKeyDown = onKeyDown.bind( this );
+		this._onKeyUp = onKeyUp.bind( this );
 
 		//
 
-		this.handleResize = function () {
+		if ( domElement !== null ) {
 
-			if ( this.domElement === document ) {
+			this.connect();
 
-				this.viewHalfX = window.innerWidth / 2;
-				this.viewHalfY = window.innerHeight / 2;
+			this.handleResize();
 
-			} else {
+		}
 
-				this.viewHalfX = this.domElement.offsetWidth / 2;
-				this.viewHalfY = this.domElement.offsetHeight / 2;
+		this._setOrientation();
 
-			}
+	}
 
-		};
+	connect() {
 
-		this.onPointerDown = function ( event ) {
+		window.addEventListener( 'keydown', this._onKeyDown );
+		window.addEventListener( 'keyup', this._onKeyUp );
 
-			if ( this.domElement !== document ) {
+		this.domElement.addEventListener( 'pointermove', this._onPointerMove );
+		this.domElement.addEventListener( 'pointerdown', this._onPointerDown );
+		this.domElement.addEventListener( 'pointerup', this._onPointerUp );
+		this.domElement.addEventListener( 'contextmenu', this._onContextMenu );
 
-				this.domElement.focus();
+	}
 
-			}
+	disconnect() {
 
-			if ( this.activeLook ) {
+		window.removeEventListener( 'keydown', this._onKeyDown );
+		window.removeEventListener( 'keyup', this._onKeyUp );
 
-				switch ( event.button ) {
+		this.domElement.removeEventListener( 'pointerdown', this._onPointerMove );
+		this.domElement.removeEventListener( 'pointermove', this._onPointerDown );
+		this.domElement.removeEventListener( 'pointerup', this._onPointerUp );
+		this.domElement.removeEventListener( 'contextmenu', this._onContextMenu );
 
-					case 0: this.moveForward = true; break;
-					case 2: this.moveBackward = true; break;
+	}
 
-				}
+	dispose() {
 
-			}
+		this.disconnect();
 
-			this.mouseDragOn = true;
+	}
 
-		};
+	handleResize() {
 
-		this.onPointerUp = function ( event ) {
+		if ( this.domElement === document ) {
 
-			if ( this.activeLook ) {
+			this._viewHalfX = window.innerWidth / 2;
+			this._viewHalfY = window.innerHeight / 2;
 
-				switch ( event.button ) {
+		} else {
 
-					case 0: this.moveForward = false; break;
-					case 2: this.moveBackward = false; break;
+			this._viewHalfX = this.domElement.offsetWidth / 2;
+			this._viewHalfY = this.domElement.offsetHeight / 2;
 
-				}
+		}
 
-			}
+	}
 
-			this.mouseDragOn = false;
+	lookAt( x, y, z ) {
 
-		};
+		if ( x.isVector3 ) {
 
-		this.onPointerMove = function ( event ) {
+			_target.copy( x );
 
-			if ( this.domElement === document ) {
+		} else {
 
-				this.pointerX = event.pageX - this.viewHalfX;
-				this.pointerY = event.pageY - this.viewHalfY;
+			_target.set( x, y, z );
 
-			} else {
+		}
 
-				this.pointerX = event.pageX - this.domElement.offsetLeft - this.viewHalfX;
-				this.pointerY = event.pageY - this.domElement.offsetTop - this.viewHalfY;
+		this.object.lookAt( _target );
 
-			}
+		this._setOrientation();
 
-		};
+		return this;
 
-		this.onKeyDown = function ( event ) {
+	}
 
-			switch ( event.code ) {
+	update( delta ) {
 
-				case 'ArrowUp':
-				case 'KeyW': this.moveForward = true; break;
+		if ( this.enabled === false ) return;
 
-				case 'ArrowLeft':
-				case 'KeyA': this.moveLeft = true; break;
+		if ( this.heightSpeed ) {
 
-				case 'ArrowDown':
-				case 'KeyS': this.moveBackward = true; break;
+			const y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
+			const heightDelta = y - this.heightMin;
 
-				case 'ArrowRight':
-				case 'KeyD': this.moveRight = true; break;
+			this._autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
 
-				case 'KeyR': this.moveUp = true; break;
-				case 'KeyF': this.moveDown = true; break;
+		} else {
 
-			}
+			this._autoSpeedFactor = 0.0;
 
-		};
+		}
 
-		this.onKeyUp = function ( event ) {
+		const actualMoveSpeed = delta * this.movementSpeed;
 
-			switch ( event.code ) {
+		if ( this._moveForward || ( this.autoForward && ! this._moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this._autoSpeedFactor ) );
+		if ( this._moveBackward ) this.object.translateZ( actualMoveSpeed );
 
-				case 'ArrowUp':
-				case 'KeyW': this.moveForward = false; break;
+		if ( this._moveLeft ) this.object.translateX( - actualMoveSpeed );
+		if ( this._moveRight ) this.object.translateX( actualMoveSpeed );
 
-				case 'ArrowLeft':
-				case 'KeyA': this.moveLeft = false; break;
+		if ( this._moveUp ) this.object.translateY( actualMoveSpeed );
+		if ( this._moveDown ) this.object.translateY( - actualMoveSpeed );
 
-				case 'ArrowDown':
-				case 'KeyS': this.moveBackward = false; break;
+		let actualLookSpeed = delta * this.lookSpeed;
 
-				case 'ArrowRight':
-				case 'KeyD': this.moveRight = false; break;
+		if ( ! this.activeLook ) {
 
-				case 'KeyR': this.moveUp = false; break;
-				case 'KeyF': this.moveDown = false; break;
+			actualLookSpeed = 0;
 
-			}
+		}
 
-		};
+		let verticalLookRatio = 1;
 
-		this.lookAt = function ( x, y, z ) {
+		if ( this.constrainVertical ) {
 
-			if ( x.isVector3 ) {
+			verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
 
-				_target.copy( x );
+		}
 
-			} else {
+		this._lon -= this._pointerX * actualLookSpeed;
+		if ( this.lookVertical ) this._lat -= this._pointerY * actualLookSpeed * verticalLookRatio;
 
-				_target.set( x, y, z );
+		this._lat = Math.max( - 85, Math.min( 85, this._lat ) );
 
-			}
+		let phi = MathUtils.degToRad( 90 - this._lat );
+		const theta = MathUtils.degToRad( this._lon );
 
-			this.object.lookAt( _target );
+		if ( this.constrainVertical ) {
 
-			setOrientation( this );
+			phi = MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
 
-			return this;
+		}
 
-		};
+		const position = this.object.position;
 
-		this.update = function () {
+		_targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
 
-			const targetPosition = new Vector3();
+		this.object.lookAt( _targetPosition );
 
-			return function update( delta ) {
+	}
 
-				if ( this.enabled === false ) return;
+	_setOrientation() {
 
-				if ( this.heightSpeed ) {
+		const quaternion = this.object.quaternion;
 
-					const y = MathUtils.clamp( this.object.position.y, this.heightMin, this.heightMax );
-					const heightDelta = y - this.heightMin;
+		_lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
+		_spherical.setFromVector3( _lookDirection );
 
-					this.autoSpeedFactor = delta * ( heightDelta * this.heightCoef );
+		this._lat = 90 - MathUtils.radToDeg( _spherical.phi );
+		this._lon = MathUtils.radToDeg( _spherical.theta );
 
-				} else {
+	}
 
-					this.autoSpeedFactor = 0.0;
+}
 
-				}
+function onPointerDown( event ) {
 
-				const actualMoveSpeed = delta * this.movementSpeed;
+	if ( this.domElement !== document ) {
 
-				if ( this.moveForward || ( this.autoForward && ! this.moveBackward ) ) this.object.translateZ( - ( actualMoveSpeed + this.autoSpeedFactor ) );
-				if ( this.moveBackward ) this.object.translateZ( actualMoveSpeed );
+		this.domElement.focus();
 
-				if ( this.moveLeft ) this.object.translateX( - actualMoveSpeed );
-				if ( this.moveRight ) this.object.translateX( actualMoveSpeed );
+	}
 
-				if ( this.moveUp ) this.object.translateY( actualMoveSpeed );
-				if ( this.moveDown ) this.object.translateY( - actualMoveSpeed );
+	if ( this.activeLook ) {
 
-				let actualLookSpeed = delta * this.lookSpeed;
+		switch ( event.button ) {
 
-				if ( ! this.activeLook ) {
+			case 0: this._moveForward = true; break;
+			case 2: this._moveBackward = true; break;
 
-					actualLookSpeed = 0;
+		}
 
-				}
+	}
 
-				let verticalLookRatio = 1;
+	this.mouseDragOn = true;
 
-				if ( this.constrainVertical ) {
+}
 
-					verticalLookRatio = Math.PI / ( this.verticalMax - this.verticalMin );
+function onPointerUp( event ) {
 
-				}
+	if ( this.activeLook ) {
 
-				lon -= this.pointerX * actualLookSpeed;
-				if ( this.lookVertical ) lat -= this.pointerY * actualLookSpeed * verticalLookRatio;
+		switch ( event.button ) {
 
-				lat = Math.max( - 85, Math.min( 85, lat ) );
+			case 0: this._moveForward = false; break;
+			case 2: this._moveBackward = false; break;
 
-				let phi = MathUtils.degToRad( 90 - lat );
-				const theta = MathUtils.degToRad( lon );
+		}
 
-				if ( this.constrainVertical ) {
+	}
 
-					phi = MathUtils.mapLinear( phi, 0, Math.PI, this.verticalMin, this.verticalMax );
+	this.mouseDragOn = false;
 
-				}
+}
 
-				const position = this.object.position;
+function onPointerMove( event ) {
 
-				targetPosition.setFromSphericalCoords( 1, phi, theta ).add( position );
+	if ( this.domElement === document ) {
 
-				this.object.lookAt( targetPosition );
+		this._pointerX = event.pageX - this._viewHalfX;
+		this._pointerY = event.pageY - this._viewHalfY;
 
-			};
+	} else {
 
-		}();
+		this._pointerX = event.pageX - this.domElement.offsetLeft - this._viewHalfX;
+		this._pointerY = event.pageY - this.domElement.offsetTop - this._viewHalfY;
 
-		this.dispose = function () {
+	}
 
-			this.domElement.removeEventListener( 'contextmenu', contextmenu );
-			this.domElement.removeEventListener( 'pointerdown', _onPointerDown );
-			this.domElement.removeEventListener( 'pointermove', _onPointerMove );
-			this.domElement.removeEventListener( 'pointerup', _onPointerUp );
+}
 
-			window.removeEventListener( 'keydown', _onKeyDown );
-			window.removeEventListener( 'keyup', _onKeyUp );
+function onKeyDown( event ) {
 
-		};
+	switch ( event.code ) {
 
-		const _onPointerMove = this.onPointerMove.bind( this );
-		const _onPointerDown = this.onPointerDown.bind( this );
-		const _onPointerUp = this.onPointerUp.bind( this );
-		const _onKeyDown = this.onKeyDown.bind( this );
-		const _onKeyUp = this.onKeyUp.bind( this );
+		case 'ArrowUp':
+		case 'KeyW': this._moveForward = true; break;
 
-		this.domElement.addEventListener( 'contextmenu', contextmenu );
-		this.domElement.addEventListener( 'pointerdown', _onPointerDown );
-		this.domElement.addEventListener( 'pointermove', _onPointerMove );
-		this.domElement.addEventListener( 'pointerup', _onPointerUp );
+		case 'ArrowLeft':
+		case 'KeyA': this._moveLeft = true; break;
 
-		window.addEventListener( 'keydown', _onKeyDown );
-		window.addEventListener( 'keyup', _onKeyUp );
+		case 'ArrowDown':
+		case 'KeyS': this._moveBackward = true; break;
 
-		function setOrientation( controls ) {
+		case 'ArrowRight':
+		case 'KeyD': this._moveRight = true; break;
 
-			const quaternion = controls.object.quaternion;
+		case 'KeyR': this._moveUp = true; break;
+		case 'KeyF': this._moveDown = true; break;
 
-			_lookDirection.set( 0, 0, - 1 ).applyQuaternion( quaternion );
-			_spherical.setFromVector3( _lookDirection );
+	}
+
+}
 
-			lat = 90 - MathUtils.radToDeg( _spherical.phi );
-			lon = MathUtils.radToDeg( _spherical.theta );
+function onKeyUp( event ) {
 
-		}
+	switch ( event.code ) {
 
-		this.handleResize();
+		case 'ArrowUp':
+		case 'KeyW': this._moveForward = false; break;
 
-		setOrientation( this );
+		case 'ArrowLeft':
+		case 'KeyA': this._moveLeft = false; break;
+
+		case 'ArrowDown':
+		case 'KeyS': this._moveBackward = false; break;
+
+		case 'ArrowRight':
+		case 'KeyD': this._moveRight = false; break;
+
+		case 'KeyR': this._moveUp = false; break;
+		case 'KeyF': this._moveDown = false; break;
 
 	}
 
 }
 
-function contextmenu( event ) {
+function onContextMenu( event ) {
+
+	if ( this.enabled === false ) return;
 
 	event.preventDefault();
 

粤ICP备19079148号