refactoring, resolves #16
This commit is contained in:
@@ -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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+183
-20
@@ -1,10 +1,27 @@
|
|||||||
import { AnimationMixer } from 'three';
|
import { AnimationMixer, Vector3, Clock } from 'three';
|
||||||
import { PointerControls } from './PointerControls';
|
import { PointerControls } from './PointerControls';
|
||||||
import { CharacterControls } from './CharacterControls';
|
|
||||||
import * as THREE from 'three';
|
|
||||||
import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxSize } from './MeshUtils';
|
import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxSize } from './MeshUtils';
|
||||||
|
import { QueryFilterFlags } from '@dimforge/rapier3d';
|
||||||
|
|
||||||
class Hero{
|
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){
|
constructor(object, data){
|
||||||
this.object = object;
|
this.object = object;
|
||||||
@@ -14,8 +31,6 @@ class Hero{
|
|||||||
|
|
||||||
init(gameEngine){
|
init(gameEngine){
|
||||||
this.gameEngine = 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);
|
this.mixer = new AnimationMixer(this.model);
|
||||||
gameEngine.mixers.push( this.mixer );
|
gameEngine.mixers.push( this.mixer );
|
||||||
@@ -32,19 +47,42 @@ class Hero{
|
|||||||
let size = getBoundingBoxSize(bb);
|
let size = getBoundingBoxSize(bb);
|
||||||
let center = getBoundingBoxCenterPoint(bb, this.model.position)
|
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})
|
this.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});
|
this.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.initCharacterControls();
|
||||||
this.animationsMap, gameEngine, 'idle', po, this.pointerControls)
|
|
||||||
|
|
||||||
this.clock = new THREE.Clock()
|
|
||||||
this.delta = 0
|
|
||||||
|
|
||||||
|
this.clock = new Clock()
|
||||||
this.ready = true;
|
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(){
|
lockControls(){
|
||||||
this.pointerControls.controls.lock();
|
this.pointerControls.controls.lock();
|
||||||
}
|
}
|
||||||
@@ -55,13 +93,7 @@ class Hero{
|
|||||||
if (this.ready && !this.disableInput) {
|
if (this.ready && !this.disableInput) {
|
||||||
let pc = this.pointerControls;
|
let pc = this.pointerControls;
|
||||||
pc.update();
|
pc.update();
|
||||||
let dlt = this.clock.getDelta();
|
this.updateCharacterControls(this.clock.getDelta(), pc)
|
||||||
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;
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,6 +102,137 @@ class Hero{
|
|||||||
this.gameEngine.mixers.splice(this.gameEngine.mixers.indexOf(this.mixer), 1);
|
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 }
|
export { Hero }
|
||||||
Reference in New Issue
Block a user