puppet face cam when idle
This commit is contained in:
@@ -1,12 +1,21 @@
|
|||||||
import * as THREE from "three";
|
import * as THREE from "three";
|
||||||
import type { PuppetRig } from ".";
|
import type { PuppetRig } from ".";
|
||||||
|
|
||||||
|
const MIN_IDLE_FACING_OFFSET_DEGREES = 3;
|
||||||
|
const MAX_IDLE_FACING_OFFSET_DEGREES = 5;
|
||||||
|
|
||||||
export class PuppetAnimation {
|
export class PuppetAnimation {
|
||||||
private readonly smoothedGroundPosition = new THREE.Vector3();
|
private readonly smoothedGroundPosition = new THREE.Vector3();
|
||||||
private readonly movementDelta = new THREE.Vector3();
|
private readonly movementDelta = new THREE.Vector3();
|
||||||
|
private readonly idleRotationY: number;
|
||||||
|
private idleTargetRotationY: number;
|
||||||
private hasSmoothedPosition = false;
|
private hasSmoothedPosition = false;
|
||||||
|
private wasMoving = false;
|
||||||
|
|
||||||
constructor(private readonly rig: PuppetRig) {}
|
constructor(private readonly rig: PuppetRig) {
|
||||||
|
this.idleRotationY = rig.root.rotation.y;
|
||||||
|
this.idleTargetRotationY = this.idleRotationY;
|
||||||
|
}
|
||||||
|
|
||||||
update(
|
update(
|
||||||
targetGroundPosition: THREE.Vector3,
|
targetGroundPosition: THREE.Vector3,
|
||||||
@@ -14,6 +23,8 @@ export class PuppetAnimation {
|
|||||||
deltaSeconds: number,
|
deltaSeconds: number,
|
||||||
elapsedSeconds: number,
|
elapsedSeconds: number,
|
||||||
) {
|
) {
|
||||||
|
this.updateIdleTarget(isMoving);
|
||||||
|
|
||||||
if (!this.hasSmoothedPosition) {
|
if (!this.hasSmoothedPosition) {
|
||||||
this.smoothedGroundPosition.copy(targetGroundPosition);
|
this.smoothedGroundPosition.copy(targetGroundPosition);
|
||||||
this.rig.root.position.copy(targetGroundPosition);
|
this.rig.root.position.copy(targetGroundPosition);
|
||||||
@@ -27,6 +38,9 @@ export class PuppetAnimation {
|
|||||||
if (this.movementDelta.lengthSq() <= 0.01) {
|
if (this.movementDelta.lengthSq() <= 0.01) {
|
||||||
this.pause();
|
this.pause();
|
||||||
this.rig.root.position.copy(targetGroundPosition);
|
this.rig.root.position.copy(targetGroundPosition);
|
||||||
|
if (!isMoving) {
|
||||||
|
this.turnTowards(this.idleTargetRotationY, deltaSeconds);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,20 +48,14 @@ export class PuppetAnimation {
|
|||||||
this.smoothedGroundPosition.lerp(targetGroundPosition, alpha);
|
this.smoothedGroundPosition.lerp(targetGroundPosition, alpha);
|
||||||
this.rig.root.position.copy(this.smoothedGroundPosition);
|
this.rig.root.position.copy(this.smoothedGroundPosition);
|
||||||
|
|
||||||
const targetRotationY = Math.atan2(
|
if (isMoving) {
|
||||||
this.movementDelta.x,
|
this.turnTowards(
|
||||||
this.movementDelta.z,
|
Math.atan2(this.movementDelta.x, this.movementDelta.z),
|
||||||
);
|
|
||||||
this.rig.root.rotation.y = this.dampAngle(
|
|
||||||
this.rig.root.rotation.y,
|
|
||||||
targetRotationY,
|
|
||||||
14,
|
|
||||||
deltaSeconds,
|
deltaSeconds,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isMoving) {
|
|
||||||
this.animateWalkCycle(elapsedSeconds);
|
this.animateWalkCycle(elapsedSeconds);
|
||||||
} else {
|
} else {
|
||||||
|
this.turnTowards(this.idleTargetRotationY, deltaSeconds);
|
||||||
this.pause();
|
this.pause();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,6 +67,34 @@ export class PuppetAnimation {
|
|||||||
this.rig.rightArm.rotation.x *= 0.8;
|
this.rig.rightArm.rotation.x *= 0.8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private updateIdleTarget(isMoving: boolean) {
|
||||||
|
if (isMoving) {
|
||||||
|
this.wasMoving = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!this.wasMoving) return;
|
||||||
|
|
||||||
|
const magnitudeDegrees = THREE.MathUtils.lerp(
|
||||||
|
MIN_IDLE_FACING_OFFSET_DEGREES,
|
||||||
|
MAX_IDLE_FACING_OFFSET_DEGREES,
|
||||||
|
Math.random(),
|
||||||
|
);
|
||||||
|
const direction = Math.random() < 0.5 ? -1 : 1;
|
||||||
|
this.idleTargetRotationY =
|
||||||
|
this.idleRotationY +
|
||||||
|
THREE.MathUtils.degToRad(magnitudeDegrees * direction);
|
||||||
|
this.wasMoving = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private turnTowards(targetRotationY: number, deltaSeconds: number) {
|
||||||
|
this.rig.root.rotation.y = this.dampAngle(
|
||||||
|
this.rig.root.rotation.y,
|
||||||
|
targetRotationY,
|
||||||
|
14,
|
||||||
|
deltaSeconds,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private dampAngle(
|
private dampAngle(
|
||||||
currentRadians: number,
|
currentRadians: number,
|
||||||
targetRadians: number,
|
targetRadians: number,
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import type { World } from "../world";
|
|||||||
import { PuppetAnimation } from "./animation";
|
import { PuppetAnimation } from "./animation";
|
||||||
import { PuppetSkin } from "./skin";
|
import { PuppetSkin } from "./skin";
|
||||||
|
|
||||||
|
const CAMERA_FACING_ROTATION_Y = Math.PI / 4;
|
||||||
|
|
||||||
export type PuppetRig = {
|
export type PuppetRig = {
|
||||||
root: THREE.Group;
|
root: THREE.Group;
|
||||||
body: THREE.Mesh<THREE.BoxGeometry, THREE.Material | THREE.Material[]>;
|
body: THREE.Mesh<THREE.BoxGeometry, THREE.Material | THREE.Material[]>;
|
||||||
@@ -89,6 +91,7 @@ export class Puppet {
|
|||||||
|
|
||||||
private createRig(): PuppetRig {
|
private createRig(): PuppetRig {
|
||||||
const root = new THREE.Group();
|
const root = new THREE.Group();
|
||||||
|
root.rotation.y = CAMERA_FACING_ROTATION_Y;
|
||||||
const material = new THREE.MeshStandardMaterial();
|
const material = new THREE.MeshStandardMaterial();
|
||||||
|
|
||||||
const body = new THREE.Mesh(
|
const body = new THREE.Mesh(
|
||||||
|
|||||||
Reference in New Issue
Block a user