UNPKG

@croquet/microverse-library

Version:

An npm package version of Microverse

263 lines (204 loc) 8.95 kB
// the following import statement is solely for the type checking and // autocompletion features in IDE. A Behavior cannot inherit from // another behavior or a base class but can use the methods and // properties of the card to which it is installed. // The prototype classes ActorBehavior and PawnBehavior provide // the features defined at the card object. import {ActorBehavior, PawnBehavior} from "../PrototypeBehavior"; class AvatarActor extends ActorBehavior { nicknameOffset() { console.log("nicknameOffset"); return [0, 0.5, 0]; } } class AvatarPawn extends PawnBehavior { setup() { this.speedManager = {snapshots: [], speed: 0, sign: 1, lastTime: Date.now()}; this.teardown(); this.animationsPromise = this.animationsPromise || this.loadAnimations(); this.subscribe(this.id, "3dModelLoaded", "modelLoaded"); if (this.avatarModel) { this.modelLoaded(); } if (!this.isMyPlayerPawn) {return;} this.addFirstResponder("pointerTap", {ctrlKey: true, altKey: true}, this); this.addEventListener("pointerTap", this.pointerTap); this.addFirstResponder("pointerDown", {ctrlKey: true, altKey: true}, this); this.addLastResponder("pointerDown", {}, this); this.addEventListener("pointerDown", this.pointerDown); this.addFirstResponder("pointerMove", {ctrlKey: true, altKey: true}, this); this.addLastResponder("pointerMove", {}, this); this.addEventListener("pointerMove", this.pointerMove); this.addLastResponder("pointerUp", {ctrlKey: true, altKey: true}, this); this.addEventListener("pointerUp", this.pointerUp); this.addLastResponder("pointerWheel", {ctrlKey: true, altKey: true}, this); this.addEventListener("pointerWheel", this.pointerWheel); this.removeEventListener("pointerDoubleDown", "onPointerDoubleDown"); this.addFirstResponder("pointerDoubleDown", {shiftKey: true}, this); this.addEventListener("pointerDoubleDown", this.addSticky); this.addLastResponder("keyDown", {ctrlKey: true}, this); this.addEventListener("keyDown", this.keyDown); this.addLastResponder("keyUp", {ctrlKey: true}, this); this.addEventListener("keyUp", this.keyUp); } loadAnimations() { const assetManager = this.service('AssetManager').assetManager; const paths = ["idle", "walking", "running"].map(n => `./assets/avatar-animations/${n}.glb`); let promises = paths.map((path) => { return assetManager.fillCacheIfAbsent(path, () => { return this.getBuffer(path).then((buffer) => { return assetManager.load(buffer, 'glb', Microverse.THREE); }); }); }); return Promise.all(promises).then((animatedObjects) => { return animatedObjects.reduce((animations, obj) => { return [...animations, ...obj._croquetAnimation.animations]; }, []); }); } handlingEvent() { } modelLoaded() { this.avatarModel = this.shape.children[0]; const group = new Microverse.THREE.Group(); group.add( this.shape.children[0] ); // group.rotateY(Math.PI); group.translateY(-1.7); this.shape.add(group); if (this.myInterval) { clearInterval(this.myInterval); delete this.myInterval; } this.animationsPromise.then((animations) => this.animate(animations)); } animate(animations) { const mixer = new Microverse.THREE.AnimationMixer(this.avatarModel); this.avatarModel.animations = animations; const [ idle, walking, running, ] = this.avatarModel.animations; this.animatedActions = { idle: mixer.clipAction(idle), walking: mixer.clipAction(walking), running: mixer.clipAction(running), } Object.values(this.animatedActions).forEach((action) => action.play()); const run = () => { let now = Date.now(); let lastTime = this.speedManager.lastTime; this.speedManager = this.calcSpeed(this.speedManager, now); let speed = this.speedManager.speed; let sign = this.speedManager.sign; const weight = speed / 2; if (this.avatarModel.visible) { this.animatedActions.idle.setEffectiveTimeScale(sign); this.animatedActions.walking.setEffectiveTimeScale(sign); this.animatedActions.running.setEffectiveTimeScale(sign); this.animatedActions.idle.setEffectiveWeight(1 - weight); this.animatedActions.walking.setEffectiveWeight(weight < 1 ? weight : 2 - weight); this.animatedActions.running.setEffectiveWeight(weight - 1); let delta = (now - lastTime) / 1000; mixer.update(delta); this.speedManager.lastTime = now; } } if (!this.myInterval) { this.myInterval = setInterval(() => run(), 16); } } calcSpeed(config, time) { if (config.snapshots.length >= 16) { config.snapshots.shift(); } config.snapshots.push({ position: [...this.translation], time, }); const from = config.snapshots.at(0); const to = config.snapshots.at(-1); const preTo = config.snapshots.at(-2) || from; // const tick = to.time - preTo.time; if (from.position[0] === to.position[0] && from.position[1] === to.position[1] && from.position[2] === to.position[2] ) { return { snapshots: config.snapshots, lastTime: time, speed: 0, sign: 1, }; } const distance = Math.sqrt( (from.position[0] - to.position[0]) ** 2 + (from.position[1] - to.position[1]) ** 2 + (from.position[2] - to.position[2]) ** 2 ); const speed = distance / (to.time - from.time) * 1000; // Sign should represent if avatar moves forward or backward const [,a,,b] = this.rotation; const xSign = Math.sign(a) !== Math.sign(b) ? 1 : -1; const sign = 0 <= to.position[0] - preTo.position[0] ? xSign : -xSign; return { snapshots: config.snapshots, lastTime: time, speed, sign, }; } /* up(p3d) { this._plane = null; let avatar = Microverse.GetPawn(p3d.avatarId); avatar.removeFirstResponder("pointerMove", {}, this); } */ mapOpacity(avatar, opacity) { if (this._target === avatar && Microverse.v3_magnitude(this.lookOffset) < 0.8) {return 0;} if (opacity === 0 || opacity === 1) {return opacity;} return 1; } teardown() { delete this.bones; if (this.myInterval) { clearInterval(this.myInterval); delete this.myInterval; } if (this.animationId) { cancelAnimationFrame(this.animationId); } if (!this.isMyPlayerPawn) {return;} this.removeFirstResponder("pointerTap", {ctrlKey: true, altKey: true}, this); this.removeEventListener("pointerTap", this.pointerTap); this.removeFirstResponder("pointerDown", {ctrlKey: true, altKey: true}, this); this.removeLastResponder("pointerDown", {}, this); this.removeEventListener("pointerDown", this.pointerDown); this.removeFirstResponder("pointerMove", {ctrlKey: true, altKey: true}, this); this.removeLastResponder("pointerMove", {}, this); this.removeEventListener("pointerMove", this.pointerMove); this.removeLastResponder("pointerUp", {ctrlKey: true, altKey: true}, this); this.removeEventListener("pointerUp", this.pointerUp); this.removeLastResponder("pointerWheel", {ctrlKey: true, altKey: true}, this); this.removeEventListener("pointerWheel", this.pointerWheel); this.removeEventListener("pointerDoubleDown", "onPointerDoubleDown"); this.removeFirstResponder("pointerDoubleDown", {shiftKey: true}, this); this.removeEventListener("pointerDoubleDown", this.addSticky); this.removeLastResponder("keyDown", {ctrlKey: true}, this); this.removeEventListener("keyDown", this.keyDown); this.removeLastResponder("keyUp", {ctrlKey: true}, this); this.removeEventListener("keyUp", this.keyUp); } } export default { modules: [ { name: "FullBodyAvatarEventHandler", actorBehaviors: [AvatarActor], pawnBehaviors: [AvatarPawn], } ] } /* globals Microverse */