â—€ Overview
Camera Video Background

Put it anywhere in your scene to render a camera video behind your 3D scene Live demoopen in new window

import { Behaviour, ClearFlags, RGBAColor } from "@needle-tools/engine";

export class VideoBackground extends Behaviour {

    async awake() {
        // create video element and put it inside the <needle-engine> component
        const video = document.createElement("video");
        video.style.cssText = `
            position: fixed;
            min-width: 100%;
            min-height: 100%;
            z-index: -1;
        `
        this.context.domElement.shadowRoot!.appendChild(video);

        // get webcam input
        const input = await navigator.mediaDevices.getUserMedia({ video: true })
        if (!input) return;
        video.srcObject = input;
        video.play();

        // make sure the camera background is transparent
        const camera = this.context.mainCameraComponent;
        if (camera) {
            camera.clearFlags = ClearFlags.SolidColor;
            camera.backgroundColor = new RGBAColor(125, 125, 125, 0);
        }
    }
}
USDZ: Hide Object on Start

This is an example from our Everywhere Actions. The following script hides an object on start on Android and on iOS AR

export class HideOnStart extends Behaviour implements UsdzBehaviour {

    start() {
        this.gameObject.visible = false;
    }

    createBehaviours(ext, model, _context) {
        if (model.uuid === this.gameObject.uuid)
            ext.addBehavior(new BehaviorModel("HideOnStart_" + this.gameObject.name,
                TriggerBuilder.sceneStartTrigger(),
                ActionBuilder.fadeAction(model, 0, false)
            ));
    }

    beforeCreateDocument() {
        this.gameObject.visible = true;
    }

    afterCreateDocument() {
        this.gameObject.visible = false;
    }
}
Everywhere Action: Emphasize on Click

Example for adding custom USDZ behaviours for iOS AR

This is an USDZ / iOS AR only example

export class EmphasizeOnClick extends Behaviour implements UsdzBehaviour {

    @serializable()
    target?: Object3D;

    @serializable()
    duration: number = 0.5;

    @serializable()
    motionType: MotionType = MotionType.bounce;

    beforeCreateDocument() { }

    createBehaviours(ext, model, _context) {
        if (!this.target) return;

        if (model.uuid === this.gameObject.uuid) {
            const emphasize = new BehaviorModel("emphasize " + this.name,
                TriggerBuilder.tapTrigger(this.gameObject),
                ActionBuilder.emphasize(this.target, this.duration, this.motionType, undefined, "basic"),
            );
            ext.addBehavior(emphasize);
        }
    }

    afterCreateDocument(_ext, _context) { }
}
Control a Timeline by scroll

Use the mouse wheel or touch delta to update a timeline's time.

import { Behaviour, PlayableDirector, serializeable } from "@needle-tools/engine";
import { Mathf } from "@needle-tools/engine";

// Example of setting a timeline's time 
// without relying on any HTML elements.
// Here we directly use the mousewheel scroll and the touch delta

export class ScrollTimeline_2 extends Behaviour {

    @serializeable(PlayableDirector)
    timeline?: PlayableDirector;

    @serializeable()
    scrollSpeed: number = 0.5;

    @serializeable()
    lerpSpeed: number = 2.5;

    private targetTime: number = 0;

    start() {

        this.timeline?.pause();

        // Grab the mousewheel event
        window.addEventListener("wheel", (evt: WheelEvent) => this.updateTime(evt.deltaY));

        // Touch events are a bit more complicated
        // We need to keep track of the last touch position
        // and calculate the delta between the current and the last position
        let lastTouchPosition = -1;
        window.addEventListener("touchmove", (evt: TouchEvent) => {
            const delta = evt.touches[0].clientY - lastTouchPosition;
            // We only want to apply the delta if it's not TOO big
            // e.g. when the user is scrolling the page
            if (delta < 10) this.updateTime(-delta);
            // Update the last touch position
            lastTouchPosition = evt.touches[0].clientY;
        });
    }

    private updateTime(delta) {
        if (!this.timeline) return;
        this.targetTime += delta * 0.01 * this.scrollSpeed;
        this.targetTime = Mathf.clamp(this.targetTime, 0, this.timeline.duration);
    }

    onBeforeRender(): void {
        if (!this.timeline) return;
        this.timeline.pause();
        this.timeline.time = Mathf.lerp(this.timeline.time, this.targetTime, this.lerpSpeed * this.context.time.deltaTime);
        this.timeline.evaluate();
    }
}

Code Contribution Example

This is mostly a basic example on how to contribute. It will then be added on our documentation contributions page: https://engine.needle.tools/docs/community/contributions

Please include at least one code snippet, for example like this:

import { Behaviour, serializable } from "@needle-tools/engine"
import { Object3D } from "three"

export class MyComponent extends Behaviour {

    @serializable(Object3D)
    myObjectReference?: Object3D;

    start() {
        console.log("Hello world", this);
    }

    update() {
        // called every frame
    }
}