From 0649d0708cc8630116865b5b500e82d60bd7cc72 Mon Sep 17 00:00:00 2001 From: goynov Date: Fri, 27 Feb 2026 13:22:41 +0200 Subject: [PATCH] rewrite pointerlock controls --- src/lib/GameEngine.js | 2 +- src/lib/Hero.js | 34 ++++++++++-------- src/lib/PointerControls.js | 74 +++++++++++++++++++++++++++++++++----- 3 files changed, 86 insertions(+), 24 deletions(-) diff --git a/src/lib/GameEngine.js b/src/lib/GameEngine.js index 0ec1477..f46cb46 100644 --- a/src/lib/GameEngine.js +++ b/src/lib/GameEngine.js @@ -196,7 +196,7 @@ class GameEngine extends EventManager{ renderer.domElement.addEventListener('wheel', (event) => { event.preventDefault(); if (gameEngine.hero){ - if (!gameEngine.pointerControls.controls.isLocked){ + if (!gameEngine.pointerControls.isLocked){ gameEngine.hero.cameraZ += event.deltaY / 100; }else{ gameEngine.camera.fov += event.deltaY / 100; diff --git a/src/lib/Hero.js b/src/lib/Hero.js index db3279c..1aed274 100644 --- a/src/lib/Hero.js +++ b/src/lib/Hero.js @@ -110,11 +110,11 @@ class Hero{ updateCharacterControls(delta) { let pc = this.engine.pointerControls; - if (pc.controls.isLocked && this.model.visible){ + if (pc.isLocked && this.model.visible){ this.model.visible = false; this.camera.rotation.reorder('YZX'); } - if (!pc.controls.isLocked && !this.model.visible){ + if (!pc.isLocked && !this.model.visible){ this.model.visible = true; this.#cameraZ = 6 this.engine.camera.fov = 45; @@ -153,7 +153,7 @@ class Hero{ this.fadeDuration = 1; } - if (this.currentAction.startsWith('idle') && play != 'idle' && !pc.controls.isLocked){ + if (this.currentAction.startsWith('idle') && play != 'idle' && !pc.isLocked){ this.cameraIdleDelta += delta * 0.33; }else { this.cameraIdleDelta = 0; @@ -183,13 +183,13 @@ class Hero{ } if (pc.motion){ - if (!pc.controls.isLocked){ + if (!pc.isLocked){ //this.directionVelocity = this.directionVelocity * 2.5 * Math.abs(input[0]) this.direction += input[0] * delta * 2.5 //this.directionVelocity; this.walkDirection.set(0, 0, input[1]) this.walkDirection.applyAxisAngle(this.rotateAngle, this.direction) }else{ - this.direction = this.camera.rotation.y + Math.PI; + this.direction = this.camera.rotation.y; this.walkDirection.set(input[0], 0, input[1]) this.walkDirection.applyAxisAngle(this.rotateAngle, this.direction) //console.log(this.camera.rotation.y * 180/Math.PI); @@ -231,18 +231,24 @@ class Hero{ this.model.position.copy(this.po.rigidBody.nextTranslation()) let cameraPosition = new Vector3().copy(this.camera.position) - let cameraDesiredPosition = new Vector3( - this.model.position.x + this.#cameraZ* Math.sin(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta), - this.model.position.y + (pc.controls.isLocked? this.size.y*0.9 : this.cameraY), - this.model.position.z + this.#cameraZ* Math.cos(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta) - ) + let cameraDesiredPosition; + if (true){ + cameraDesiredPosition = new Vector3( + this.model.position.x + this.#cameraZ* Math.sin(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta), + this.model.position.y + (pc.isLocked? this.size.y*0.9 : this.cameraY), + this.model.position.z + this.#cameraZ* Math.cos(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta) + ) + } else { + cameraDesiredPosition = new Vector3( + this.model.position.x, + this.model.position.y + (pc.isLocked? this.size.y*0.9 : this.cameraY), + this.model.position.z - this.#cameraZ + ) + } cameraPosition.lerp(cameraDesiredPosition, delta*2) this.camera.position.copy(cameraPosition) - if (!pc.controls.isLocked){ - // this.orbitControl.target.set( - - // ) + if (!pc.isLocked){ this.camera.lookAt( this.model.position.x, this.cameraY -this.size.y * 0.5 + this.model.position.y, diff --git a/src/lib/PointerControls.js b/src/lib/PointerControls.js index 4df1eb7..a549c80 100644 --- a/src/lib/PointerControls.js +++ b/src/lib/PointerControls.js @@ -1,8 +1,12 @@ -import { Vector3 } from 'three'; -import { PointerLockControls } from 'three/examples/jsm/Addons.js'; +import { Vector3, Controls, Euler } from 'three'; -class PointerControls { +const _MOUSE_SENSITIVITY = 0.002; + +const _euler = new Euler( 0, 0, 0, 'YXZ' ); + +class PointerControls extends Controls { constructor(engine) { + super( engine.cameraWorld, engine.renderer.domElement ); this.kb = {}; this.dom = engine.renderer.domElement; @@ -16,7 +20,14 @@ class PointerControls { this.engine = engine; this.click = false; - this.controls = new PointerLockControls(engine.camera, this.dom); + this.isLocked = false; + this.minPolarAngle = 0; + this.maxPolarAngle = Math.PI; + this.pointerSpeed = 1.0; + + this._onMouseMove = onMouseMove.bind( this ); + this._onPointerlockChange = onPointerlockChange.bind( this ); + this._onPointerlockError = onPointerlockError.bind( this ); const onKeyDown = (event) => { this.kb[event.code] = true; @@ -36,24 +47,43 @@ class PointerControls { document.addEventListener('keyup', onKeyUp); this.dom.addEventListener('click', () => { - this.controls.isLocked && this.clicked && this.clicked(); + this.isLocked && this.clicked && this.clicked(); }); this.dom.addEventListener('mousedown', () => { - this.controls.isLocked && this.onpointer && this.onpointer('start'); + this.isLocked && this.onpointer && this.onpointer('start'); }); this.dom.addEventListener('mousemove', () => { - this.controls.isLocked && this.onpointer && this.onpointer('drag'); + this.isLocked && this.onpointer && this.onpointer('drag'); }); this.dom.addEventListener('mouseup', () => { - this.controls.isLocked && this.onpointer && this.onpointer('end'); + this.isLocked && this.onpointer && this.onpointer('end'); }); this.update = () => { }; + + this.connect(this.dom); } + connect( element ) { + super.connect( element ); + this.dom.ownerDocument.addEventListener( 'mousemove', this._onMouseMove ); + this.dom.ownerDocument.addEventListener( 'pointerlockchange', this._onPointerlockChange ); + this.dom.ownerDocument.addEventListener( 'pointerlockerror', this._onPointerlockError ); + } + + disconnect() { + this.dom.ownerDocument.removeEventListener( 'mousemove', this._onMouseMove ); + this.dom.ownerDocument.removeEventListener( 'pointerlockchange', this._onPointerlockChange ); + this.dom.ownerDocument.removeEventListener( 'pointerlockerror', this._onPointerlockError ); + } + + dispose() { + this.disconnect(); + } + get moveForward(){ return this.kb['ArrowUp'] || this.kb['KeyW'] || this.engine.xrController1?.gamepad?.axes[3] < -0.5 || false } @@ -128,4 +158,30 @@ class PointerControls { } } -export { PointerControls }; \ No newline at end of file +export { PointerControls }; + +function onMouseMove( event ) { + if ( this.enabled === false || this.isLocked === false ) return; + const camera = this.object; + _euler.setFromQuaternion( camera.quaternion ); + _euler.y -= event.movementX * _MOUSE_SENSITIVITY * this.pointerSpeed; + _euler.x -= -event.movementY * _MOUSE_SENSITIVITY * this.pointerSpeed; + _euler.x = Math.max( Math.PI / 2 - this.maxPolarAngle, Math.min( Math.PI / 2 - this.minPolarAngle, _euler.x ) ); + camera.quaternion.setFromEuler( _euler ) + this.dispatchEvent( { type: 'change' } ); +} + +function onPointerlockChange() { + if ( this.dom.ownerDocument.pointerLockElement === this.dom ) { + this.dispatchEvent( { type: 'lock' } ); + this.isLocked = true; + } else { + this.dispatchEvent( { type: 'unlock' } ); + this.isLocked = false; + } + //this.engine.cameraRig.rotation.y = this.isLocked ? 0 : Math.PI; +} + +function onPointerlockError() { + console.error( 'Unable to use Pointer Lock API' ); +} \ No newline at end of file