UNPKG

@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
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. * * ![](https://cloud.needle.tools/-/media/XIHaiNFsA1IbMZVJepp1aQ.gif) * * - 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