@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.
196 lines • 7.1 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { Object3D, Vector3 } from "three";
import { Mathf } from "../../engine/engine_math.js";
import { serializeable } from "../../engine/engine_serialization_decorator.js";
import { Behaviour } from "../Component.js";
import { SplineContainer } from "./Spline.js";
/**
* [SplineWalker](https://engine.needle.tools/docs/api/SplineWalker) Moves an object along a {@link SplineContainer}.
* Use this with a SplineContainer component.
*
* 
*
* - Example http://samples.needle.tools/splines
*
* @summary Moves an object along a spline
* @category Splines
* @group Components
*/
export class SplineWalker extends Behaviour {
/**
* The spline to use/walk along. Add a SplineContainer component to an object and assign it here.
*/
spline = null;
/**
* The object to move along the spline.
* If object is undefined then the spline walker will use it's own object (gameObject).
* If object is null the spline walker will not move any object.
* @default undefined
*/
object = undefined;
/**
* If true the object will rotate to look in the direction of the spline while moving along it.
* @default true
*/
useLookAt = true;
/**
* The object to look at while moving along the spline.
* If null the object will look in the direction of the spline.
* This can be disabled by setting useLookAt to false.
* @default null
*/
lookAt = null;
/**
* When clamp is set to true, the position01 value will be clamped between 0 and 1 and the object will not loop the spline.
* @default false
*/
clamp = false;
/**
* The current position on the spline. The value ranges from 0 (start of the spline curve) to 1 (end of the spline curve)
*
* When setting this value, the position will be updated in the next frame.
* @default 0
*/
// @type float
get position01() {
return this._position01;
}
set position01(v) {
this._position01 = v;
this._needsUpdate = true;
}
/** Resets the position to 0 */
reset() {
this._position01 = 0;
}
/**
* If true the SplineWalker will automatically move along the spline
* @default true
*/
autoRun = true;
/**
* The duration in seconds it takes to complete the whole spline when autoWalk is enabled.
* @default 10
*/
duration = 10;
/**
* The strength with which the object is pulled to the spline.
* This can be used to create a "rubber band" effect when the object is moved away from the spline by other forces.
* A value of 0 means no pull, a value of 1 means the object is always on the spline.
* @default 1
*/
pullStrength = 1;
// #region internal
_position01 = 0;
_needsUpdate = false;
/** @internal */
start() {
if (this.object === undefined)
this.object = this.gameObject;
this.updateFromPosition();
}
/** @internal */
onEnable() {
window.addEventListener("pointerdown", this.onUserInput, { passive: true });
// TODO: wheel event is also triggered for touch and it interrupts spline pull if it's an actual site scroll
this.context.domElement.addEventListener("wheel", this.onUserInput, { passive: true });
}
/** @internal */
onDisable() {
window.removeEventListener("pointerdown", this.onUserInput);
this.context.domElement.removeEventListener("wheel", this.onUserInput);
}
onUserInput = () => {
if (this.object?.contains(this.context.mainCamera)) {
this._needsUpdate = false;
this._performedUpdates += 999;
}
};
/** @internal */
update() {
if (this.autoRun) {
this._needsUpdate = true;
this._position01 += this.context.time.deltaTime / this.duration;
}
if (this._needsUpdate) {
this._needsUpdate = false;
this.updateFromPosition();
}
}
/**
* Updates the position of the object based on the current position01 value.
* @internal
*/
updateFromPosition() {
if (!this.spline || !this.spline.curve)
return;
if (!this.object)
return;
if (this.clamp)
this._position01 = Mathf.clamp01(this._position01);
else
this._position01 = this._position01 % 1;
const t = this._position01 >= 1 ? 1 : this._position01 % 1;
const pt = this.spline.getPointAt(t);
if (this.pullStrength >= 1) {
this.object.worldPosition = pt;
}
else {
if (this._position01 !== this._lastPosition01) {
this._performedUpdates = 0;
}
this._requiredUpdates = Math.round(100 / this.pullStrength);
if (this._performedUpdates < this._requiredUpdates) {
const wp = this.object.worldPosition;
this._performedUpdates++;
const pull = Mathf.clamp01(this.pullStrength);
const newPosition = this.object.worldPosition = wp.lerp(pt, pull * (this.context.time.deltaTime / .3));
this._lastPositionVector.copy(newPosition);
this._needsUpdate = true;
}
}
if (this.useLookAt) {
if (!this.lookAt) {
const tan = this.spline.getTangentAt(t);
this.object.lookAt(pt.add(tan));
}
else
this.object.lookAt(this.lookAt.worldPosition);
}
this._lastPosition01 = this._position01;
}
_lastPosition01 = 0;
_requiredUpdates = 0;
_performedUpdates = 0;
_lastPositionVector = new Vector3();
}
__decorate([
serializeable(SplineContainer)
], SplineWalker.prototype, "spline", void 0);
__decorate([
serializeable(Object3D)
], SplineWalker.prototype, "object", void 0);
__decorate([
serializeable()
], SplineWalker.prototype, "useLookAt", void 0);
__decorate([
serializeable(Object3D)
], SplineWalker.prototype, "lookAt", void 0);
__decorate([
serializeable()
], SplineWalker.prototype, "clamp", void 0);
__decorate([
serializeable()
], SplineWalker.prototype, "position01", null);
__decorate([
serializeable()
], SplineWalker.prototype, "autoRun", void 0);
__decorate([
serializeable()
], SplineWalker.prototype, "duration", void 0);
//# sourceMappingURL=SplineWalker.js.map