import * as THREE from 'three' export const CONTROLLER_BODY_RADIUS = 0.28; export class CharacterControls { // temporary data walkDirection = new THREE.Vector3() rotateAngle = new THREE.Vector3(0, 1, 0) rotateQuarternion = new THREE.Quaternion() cameraTarget = new THREE.Vector3() storedFall = 0 // constants fadeDuration = 0.2 runVelocity = 7 walkVelocity = 3 lerp = (x, y, a) => x * (1 - a) + y * a; constructor(model, mixer, animationsMap, orbitControl, camera, currentAction, ray, rigidBody, pointerControls) { this.model = model this.mixer = mixer this.animationsMap = animationsMap this.currentAction = currentAction this.animationsMap[currentAction].play() this.ray = ray this.rigidBody = rigidBody this.pointerControls = pointerControls this.orbitControl = orbitControl this.camera = camera this.updateCameraTarget(new THREE.Vector3(0,1,5)) } switchRunToggle() { this.toggleRun = !this.toggleRun } update(world, delta, pointerControls) { const directionPressed = pointerControls.moving() var play = ''; if (directionPressed && this.toggleRun) { play = 'run' } else if (directionPressed) { play = 'walk' } else { play = 'idle' } if (this.currentAction != play) { const toPlay = this.animationsMap[play] const current = this.animationsMap[this.currentAction] current.fadeOut(this.fadeDuration) toPlay.reset().fadeIn(this.fadeDuration).play(); this.currentAction = play } this.mixer.update(delta) this.walkDirection.x = this.walkDirection.y = this.walkDirection.z = 0 let velocity = 0 if (this.currentAction == 'run' || this.currentAction == 'walk') { // calculate towards camera direction var angleYCameraDirection = Math.atan2( (this.camera.position.x - this.model.position.x), (this.camera.position.z - this.model.position.z)) // diagonal movement angle offset var directionOffset = this.directionOffset(pointerControls) // rotate model this.rotateQuarternion.setFromAxisAngle(this.rotateAngle, Math.PI + angleYCameraDirection + directionOffset) this.model.quaternion.rotateTowards(this.rotateQuarternion, 0.2) // calculate direction this.camera.getWorldDirection(this.walkDirection) this.walkDirection.y = 0 this.walkDirection.normalize() this.walkDirection.applyAxisAngle(this.rotateAngle, directionOffset) // run/walk velocity velocity = this.currentAction == 'run' ? this.runVelocity : this.walkVelocity } const translation = this.rigidBody.translation(); if (translation.y < -1) { // don't fall below ground this.rigidBody.setNextKinematicTranslation( { x: 0, y: 10, z: 0 }); } else { const cameraPositionOffset = this.camera.position.sub(this.model.position); // update model and camera this.model.position.x = translation.x this.model.position.y = translation.y this.model.position.z = translation.z this.updateCameraTarget(cameraPositionOffset) this.walkDirection.y += this.lerp(this.storedFall, -9.81 * delta, 0.10) this.storedFall = this.walkDirection.y this.ray.origin.x = translation.x this.ray.origin.y = translation.y this.ray.origin.z = translation.z let hit = world.castRay(this.ray, 0.5, false, 0xfffffffff); if (hit) { const point = this.ray.pointAt(hit.timeOfImpact); let diff = translation.y - ( point.y + CONTROLLER_BODY_RADIUS); if (diff < 0.0) { this.storedFall = 0 this.walkDirection.y = this.lerp(0, -diff, 0.5) } } this.walkDirection.x = this.walkDirection.x * velocity * delta this.walkDirection.z = this.walkDirection.z * velocity * delta this.rigidBody.setNextKinematicTranslation( { x: translation.x + this.walkDirection.x, y: translation.y + this.walkDirection.y, z: translation.z + this.walkDirection.z }); } } updateCameraTarget(offset) { // move camera const rigidTranslation = this.rigidBody.translation(); this.camera.position.x = rigidTranslation.x + offset.x this.camera.position.y = rigidTranslation.y + offset.y this.camera.position.z = rigidTranslation.z + offset.z // update camera target this.cameraTarget.x = rigidTranslation.x this.cameraTarget.y = rigidTranslation.y + 1 this.cameraTarget.z = rigidTranslation.z this.orbitControl.target = this.cameraTarget } directionOffset(pointerControls) { var directionOffset = 0 // w if (pointerControls.moveForward) { if (pointerControls.moveLeft) { directionOffset = Math.PI / 4 // w+a } else if (pointerControls.moveRight) { directionOffset = - Math.PI / 4 // w+d } } else if (pointerControls.moveBackward) { if (pointerControls.moveLeft) { directionOffset = Math.PI / 4 + Math.PI / 2 // s+a } else if (pointerControls.moveRight) { directionOffset = -Math.PI / 4 - Math.PI / 2 // s+d } else { directionOffset = Math.PI // s } } else if (pointerControls.moveLeft) { directionOffset = Math.PI / 2 // a } else if (pointerControls.moveRight) { directionOffset = - Math.PI / 2 // d } return directionOffset } }