diff --git a/src/lib/CharacterControls.js b/src/lib/CharacterControls.js deleted file mode 100644 index 3204726..0000000 --- a/src/lib/CharacterControls.js +++ /dev/null @@ -1,178 +0,0 @@ -import * as THREE from 'three' -import { QueryFilterFlags } from '@dimforge/rapier3d'; - -export class CharacterControls { - walkDirection = new THREE.Vector3() - rotateAngle = new THREE.Vector3(0, 1, 0) - cameraY = 3 - #cameraZ = 5 - - get cameraZ(){ - return this.#cameraZ; - } - - set cameraZ(v){ - this.#cameraZ = Math.min(Math.max(v, 2), 10); - } - - // constants - fadeDuration = 0.2 - runVelocity = 11 - walkVelocity = 7 - lerp = (x, y, a) => x * (1 - a) + y * a; - - constructor(model, animationsMap, engine, currentAction, po, pointerControls) { - this.model = model - this.animationsMap = animationsMap - this.currentAction = currentAction - this.animationsMap[currentAction].play() - - this.characterController = engine.physics.world.createCharacterController(0.1); - this.characterController.setUp({x:0, y:1, z:0}); - po.rigidBody.setTranslation(this.model.position) - po.characterController = this.characterController; - //this.characterController.enableSnapToGround(0.5); - // // Don’t allow climbing slopes larger than 45 degrees. - // this.characterController.setMaxSlopeClimbAngle(45 * Math.PI / 180); - // Automatically slide down on slopes smaller than 30 degrees. - // this.characterController.setMinSlopeSlideAngle(30 * Math.PI / 180); - // this.characterController.enableAutostep(0.5, 0.2, true); - this.characterController.setApplyImpulsesToDynamicBodies(true); - // this.characterController.setCharacterMass(50); - this.po = po; - this.pointerControls = pointerControls - - this.orbitControl = engine.orbitControls - this.camera = engine.camera - - this.direction = this.model.rotation.y; - this.directionVelocity = 0; - this.actionStart = 0; - this.cameraDelta = 0; - this.cameraIdleDelta = 0; - } - - update(world, delta, pointerControls) { - let input = this.getInput(pointerControls) - - let play = this.currentAction || 'idle', velocity = this.walkVelocity; - this.fadeDuration = 0.2; - if (input[1] && pointerControls.running) { - play = 'run'; - velocity = this.runVelocity - } else if (input[1] > 0) { - play = 'walk' - } else if (input[1] < 0) { - play = 'backward' - velocity = this.walkVelocity / 3 - } else if (input[0] < 0) { - play = 'right' - } else if (input[0] > 0) { - play = 'left' - } else if (!this.currentAction.startsWith('idle')){ - play = 'idle' - } else if (this.actionStart == -1){ - this.actionStart = 0; - play = 'idle'; - } - - if (this.currentAction.startsWith('idle') && play.startsWith('idle') && this.actionStart > 10){ - let idx = 1 + parseInt(this.currentAction.split('.')[1] || 0); - if (this.animationsMap[`idle.${idx}`]) { - play = `idle.${idx}` - }else{ - play = 'idle' - } - this.fadeDuration = 1; - } - - if (this.currentAction.startsWith('idle') && play != 'idle'){ - this.cameraIdleDelta += delta * 0.33; - }else { - this.cameraIdleDelta = 0; - } - - if (this.currentAction != play) { - const toPlay = this.animationsMap[play] - const current = this.animationsMap[this.currentAction] - - current.fadeOut(this.fadeDuration) - //toPlay.timeScale = 1; - toPlay.reset().fadeIn(this.fadeDuration).play(); - - this.currentAction = play - this.actionStart = 0; - } - - this.actionStart += delta; - this.cameraDelta += delta * ( pointerControls.cameraLeft * -1 + pointerControls.cameraRight * 1) - this.walkDirection.x = this.walkDirection.y = this.walkDirection.z = 0 - - if (pointerControls.kb.KeyR && this.cameraY < 5){ - this.cameraY+=delta; - } - if (pointerControls.kb.KeyF && this.cameraY > 1){ - this.cameraY-=delta; - } - - if (pointerControls.motion) { - this.directionVelocity = this.directionVelocity * 2.5 * Math.abs(input[0]) - this.direction += input[0] * delta * 2.5 //this.directionVelocity; - this.model.rotation.y = this.direction; - this.walkDirection.set(0, 0, input[1]) - this.walkDirection.applyAxisAngle(this.rotateAngle, this.direction) - this.walkDirection.normalize(); - } - - this.walkDirection.x = this.walkDirection.x * velocity * delta// + this.model.position.x - this.walkDirection.z = this.walkDirection.z * velocity * delta// + this.model.position.z - - //const translation = this.rigidBody.translation(); - - this.characterController.computeColliderMovement( - this.po.collider, // The collider we would like to move. - this.walkDirection, // The movement we would like to apply if there wasn’t any obstacle. - QueryFilterFlags['EXCLUDE_SENSORS'] - ); - - // for (let i = 0; i < this.characterController.numComputedCollisions(); i++) { - // let collision = this.characterController.computedCollision(i); - // //console.log(collision) - // // Do something with that collision information. - // } - - let correctedMovement = this.characterController.computedMovement(); - - let v = new THREE.Vector3(); - v.copy(this.po.rigidBody.translation()); - v.add(correctedMovement) - - this.po.rigidBody.setNextKinematicTranslation(v); - - this.model.position.copy(this.po.rigidBody.nextTranslation()) - - let cameraPosition = new THREE.Vector3().copy(this.camera.position) - let cameraDesiredPosition = new THREE.Vector3( - this.model.position.x + this.#cameraZ* Math.sin(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta), - this.cameraY, - this.model.position.z + this.#cameraZ* Math.cos(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta) - ) - - cameraPosition.lerp(cameraDesiredPosition, delta*2) - this.camera.position.copy(cameraPosition) - this.orbitControl.target.set(this.model.position.x, this.cameraY - 1, this.model.position.z) - this.camera.lookAt(this.orbitControl.target) - } - - getInput(pointerControls) { - return [ - pointerControls.moveLeft * 1 + pointerControls.moveRight * -1, - pointerControls.moveForward * 1 + pointerControls.moveBackward * -1 - ] - } - - idleReset(){ - this.actionStart = -1; - //this.currentAction = 'idle' - } -} \ No newline at end of file diff --git a/src/lib/Hero.js b/src/lib/Hero.js index d923909..6f3a698 100644 --- a/src/lib/Hero.js +++ b/src/lib/Hero.js @@ -1,10 +1,27 @@ -import { AnimationMixer } from 'three'; +import { AnimationMixer, Vector3, Clock } from 'three'; import { PointerControls } from './PointerControls'; -import { CharacterControls } from './CharacterControls'; -import * as THREE from 'three'; import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxSize } from './MeshUtils'; +import { QueryFilterFlags } from '@dimforge/rapier3d'; class Hero{ + walkDirection = new Vector3() + rotateAngle = new Vector3(0, 1, 0) + cameraY = 3 + #cameraZ = 5 + + get cameraZ(){ + return this.#cameraZ; + } + + set cameraZ(v){ + this.#cameraZ = Math.min(Math.max(v, 2), 10); + } + + // constants + fadeDuration = 0.2 + runVelocity = 11 + walkVelocity = 7 + lerp = (x, y, a) => x * (1 - a) + y * a; constructor(object, data){ this.object = object; @@ -14,8 +31,6 @@ class Hero{ init(gameEngine){ this.gameEngine = gameEngine; - gameEngine.camera.position.set(0,17,-30) - gameEngine.camera.lookAt(new THREE.Vector3(this.model.position.x, 5, this.model.position.z)) this.mixer = new AnimationMixer(this.model); gameEngine.mixers.push( this.mixer ); @@ -32,19 +47,42 @@ class Hero{ let size = getBoundingBoxSize(bb); let center = getBoundingBoxCenterPoint(bb, this.model.position) - let po = gameEngine.physics.add(this.model, 'kinematicPositionBased', false, undefined, 'capsule', { radius: size.x/2, halfHeight: size.y/2}) - po.collider.setTranslationWrtParent({x: center.x, y: center.y + size.y/2, z: center.z}); - //po.collider.setActiveEvents(RAPIER.ActiveEvents.COLLISION_EVENTS); - - this.characterControls = new CharacterControls(this.model, - this.animationsMap, gameEngine, 'idle', po, this.pointerControls) - - this.clock = new THREE.Clock() - this.delta = 0 + this.po = gameEngine.physics.add(this.model, 'kinematicPositionBased', false, undefined, 'capsule', { radius: size.x/2, halfHeight: size.y/2}) + this.po.collider.setTranslationWrtParent({x: center.x, y: center.y + size.y/2, z: center.z}); + + this.initCharacterControls(); + this.clock = new Clock() this.ready = true; } + initCharacterControls(){ + this.currentAction = 'idle'; + this.animationsMap[this.currentAction].play() + + this.characterController = this.gameEngine.physics.world.createCharacterController(0.1); + this.characterController.setUp({x:0, y:1, z:0}); + this.po.rigidBody.setTranslation(this.model.position) + this.po.characterController = this.characterController; + //this.characterController.enableSnapToGround(0.5); + // // Don’t allow climbing slopes larger than 45 degrees. + // this.characterController.setMaxSlopeClimbAngle(45 * Math.PI / 180); + // Automatically slide down on slopes smaller than 30 degrees. + // this.characterController.setMinSlopeSlideAngle(30 * Math.PI / 180); + // this.characterController.enableAutostep(0.5, 0.2, true); + this.characterController.setApplyImpulsesToDynamicBodies(true); + // this.characterController.setCharacterMass(50); + + this.orbitControl = this.gameEngine.orbitControls + this.camera = this.gameEngine.camera + + this.direction = this.model.rotation.y; + this.directionVelocity = 0; + this.actionStart = 0; + this.cameraDelta = 0; + this.cameraIdleDelta = 0; + } + lockControls(){ this.pointerControls.controls.lock(); } @@ -55,13 +93,7 @@ class Hero{ if (this.ready && !this.disableInput) { let pc = this.pointerControls; pc.update(); - let dlt = this.clock.getDelta(); - this.delta += dlt; - //if (this.delta > 0.00001){ - this.characterControls.update(this.gameEngine.physics.world, this.delta, pc) - this.gameEngine.physics.step() - this.delta = 0; - //} + this.updateCharacterControls(this.clock.getDelta(), pc) } } @@ -70,6 +102,137 @@ class Hero{ this.gameEngine.mixers.splice(this.gameEngine.mixers.indexOf(this.mixer), 1); } + updateCharacterControls(delta) { + if (!this.gameEngine.physics.started) return; + let input = this.getInput() + + let play = this.currentAction || 'idle', velocity = this.walkVelocity; + this.fadeDuration = 0.2; + if (input[1] && this.pointerControls.running) { + play = 'run'; + velocity = this.runVelocity + } else if (input[1] > 0) { + play = 'walk' + } else if (input[1] < 0) { + play = 'backward' + velocity = this.walkVelocity / 3 + } else if (input[0] < 0) { + play = 'right' + } else if (input[0] > 0) { + play = 'left' + } else if (!this.currentAction.startsWith('idle')){ + play = 'idle' + } else if (this.actionStart == -1){ + this.actionStart = 0; + play = 'idle'; + } + + if (this.currentAction.startsWith('idle') && play.startsWith('idle') && this.actionStart > 10){ + let idx = 1 + parseInt(this.currentAction.split('.')[1] || 0); + if (this.animationsMap[`idle.${idx}`]) { + play = `idle.${idx}` + }else{ + play = 'idle' + } + this.fadeDuration = 1; + } + + if (this.currentAction.startsWith('idle') && play != 'idle'){ + this.cameraIdleDelta += delta * 0.33; + }else { + this.cameraIdleDelta = 0; + } + + if (this.currentAction != play) { + const toPlay = this.animationsMap[play] + const current = this.animationsMap[this.currentAction] + + current.fadeOut(this.fadeDuration) + //toPlay.timeScale = 1; + toPlay.reset().fadeIn(this.fadeDuration).play(); + + this.currentAction = play + this.actionStart = 0; + } + + this.actionStart += delta; + this.cameraDelta += delta * ( this.pointerControls.cameraLeft * -1 + this.pointerControls.cameraRight * 1) + this.walkDirection.x = this.walkDirection.y = this.walkDirection.z = 0 + + if (this.pointerControls.kb.KeyR && this.cameraY < 5){ + this.cameraY+=delta; + } + if (this.pointerControls.kb.KeyF && this.cameraY > 1){ + this.cameraY-=delta; + } + + if (this.pointerControls.motion) { + this.directionVelocity = this.directionVelocity * 2.5 * Math.abs(input[0]) + this.direction += input[0] * delta * 2.5 //this.directionVelocity; + this.model.rotation.y = this.direction; + this.walkDirection.set(0, 0, input[1]) + this.walkDirection.applyAxisAngle(this.rotateAngle, this.direction) + this.walkDirection.normalize(); + } + + this.walkDirection.x = this.walkDirection.x * velocity * delta// + this.model.position.x + this.walkDirection.y = -delta * 0.5 * 9.8; //gravity!!! + this.walkDirection.z = this.walkDirection.z * velocity * delta// + this.model.position.z + + //const translation = this.rigidBody.translation(); + + this.characterController.computeColliderMovement( + this.po.collider, // The collider we would like to move. + this.walkDirection, // The movement we would like to apply if there wasn’t any obstacle. + QueryFilterFlags['EXCLUDE_SENSORS'] + ); + + // for (let i = 0; i < this.characterController.numComputedCollisions(); i++) { + // let collision = this.characterController.computedCollision(i); + // //console.log(collision) + // // Do something with that collision information. + // } + + let correctedMovement = this.characterController.computedMovement(); + //console.log(correctedMovement); + + let v = new Vector3(); + v.copy(this.po.rigidBody.translation()); + v.add(correctedMovement) + + this.po.rigidBody.setNextKinematicTranslation(v); + + 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.cameraY + this.model.position.y, + this.model.position.z + this.#cameraZ* Math.cos(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta) + ) + + cameraPosition.lerp(cameraDesiredPosition, delta*2) + this.camera.position.copy(cameraPosition) + this.orbitControl.target.set( + this.model.position.x, + this.cameraY - 1 + this.model.position.y, + this.model.position.z + ) + this.camera.lookAt(this.orbitControl.target) + } + + getInput() { + return [ + this.pointerControls.moveLeft * 1 + this.pointerControls.moveRight * -1, + this.pointerControls.moveForward * 1 + this.pointerControls.moveBackward * -1 + ] + } + + idleReset(){ + this.actionStart = -1; + //this.currentAction = 'idle' + } + } export { Hero } \ No newline at end of file