import * as THREE from 'three' import { QueryFilterFlags } from '@dimforge/rapier3d'; export class CharacterControls { // temporary data walkDirection = new THREE.Vector3() rotateAngle = new THREE.Vector3(0, 1, 0) rotateQuarternion = new THREE.Quaternion() cameraTarget = new THREE.Vector3() cameraY = 3 // 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.updateCameraTarget(new THREE.Vector3(0,1,5)) 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 + 5* Math.sin(this.model.rotation.y + Math.PI + this.cameraDelta + this.cameraIdleDelta), this.cameraY, this.model.position.z + 5* 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' } }