@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
77 lines (64 loc) • 2.99 kB
text/typescript
import { Object3D, Quaternion, Vector3 } from "three";
import { Mathf } from "../engine/engine_math.js";
import { Axes } from "../engine/engine_physics.types.js";
import { serializable } from "../engine/engine_serialization_decorator.js";
import { getWorldPosition, getWorldQuaternion } from "../engine/engine_three_utils.js";
import { Behaviour } from "./Component.js";
/**
* SmoothFollow makes the {@link Object3D} (`GameObject`) smoothly follow another target {@link Object3D}.
* It can follow the target's position, rotation, or both.
* @category Interactivity
* @group Components
*/
export class SmoothFollow extends Behaviour {
/**
* The target to follow. If null, the GameObject will not move.
*/
target: Object3D | null = null;
/**
* The factor to smoothly follow the target's position.
* The value is clamped between 0 and 1. If 0, the GameObject will not follow the target's position.
*/
followFactor = .1;
/**
* The factor to smoothly follow the target's rotation.
* The value is clamped between 0 and 1. If 0, the GameObject will not follow the target's rotation.
*/
rotateFactor = .1;
positionAxes: Axes = Axes.All;
flipForward: boolean = false;
private static _invertForward: Quaternion = new Quaternion().setFromAxisAngle(new Vector3(0, 1, 0), Math.PI);
private _firstUpdate = true;
/**
* Update the position and rotation of the GameObject to follow the target.
*/
onBeforeRender(): void {
this.updateNow(false);
}
updateNow(hard: boolean) {
if (!this.target || this.target === this.gameObject) return;
if (this.followFactor > 0) {
const wp = getWorldPosition(this.target);
const fpos = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.followFactor);
const currentPosition = this.worldPosition;
if (this.positionAxes & Axes.X) currentPosition.x = Mathf.lerp(currentPosition.x, wp.x, fpos);
if (this.positionAxes & Axes.Y) currentPosition.y = Mathf.lerp(currentPosition.y, wp.y, fpos);
if (this.positionAxes & Axes.Z) currentPosition.z = Mathf.lerp(currentPosition.z, wp.z, fpos);
// TODO lerp distance from target as well
this.worldPosition = currentPosition;
}
if (this.rotateFactor > 0) {
const wr = getWorldQuaternion(this.target);
if (this.flipForward) {
wr.premultiply(SmoothFollow._invertForward);
}
const frot = this._firstUpdate || hard ? 1 : Mathf.clamp01(this.context.time.deltaTime * this.rotateFactor);
this.worldQuaternion = this.worldQuaternion.slerp(wr, frot);
}
this._firstUpdate = false;
}
}