Question about locked objects in the scene

I have a scene with a cube that can be dragged by a user.

After 10 seconds of dragging, we disable dragging by deactivating the DragControls component.
Till here it’s fine.

But when multiple users are in the scene and SyncTransform exists on the block, some information goes missing for the new users (i.e. original creation time of the block)
Hence, the block can still be moved, even though it was supposed to be immovable.

We want the creation time to persist and not be lost, for existing and even for new users to the room.

How can we achieve this ?

Hi Enthusiast!

Plan (Step-by-step):

  1. You want to persist a value (creationTime) for a shared object (the block) in a multiplayer Needle Engine scene.
  2. This value must:
    • Be available to all users, including those who join later.
    • Determine if the block can be dragged (i.e., lock after 10s based on creation time, not local client triggers).
  3. The value must be synced using Needle’s networking, not just local variables.

Approach:

  • Store creationTime in a shared, network-synced model property on the object.
  • On create, set it if not present.
  • On late join, new users read the networked property.
  • Deactivate drag for all if now - creationTime > 10s.

Component Pseudocode:

  1. Component with creationTime as @syncable property.
  2. On awake: if no value, set to current time (on owner/creator).
  3. On each update, check if DragControls should be enabled.
  4. On user join: synced property ensures correct state.

Code Example:

import { Behaviour, serializable, syncable } from "@needle-tools/engine";

export class LockAfterTime extends Behaviour {
    @syncable @serializable()
    creationTime: number = 0;

    @serializable()
    lockAfterSeconds: number = 10;

    awake() {
        // Only set on the network owner/creator
        if (this.isNetworkOwner && this.creationTime === 0) {
            this.creationTime = Date.now() / 1000; // seconds
        }
    }

    update() {
        if (this.creationTime > 0) {
            const now = Date.now() / 1000;
            const elapsed = now - this.creationTime;
            const shouldLock = elapsed > this.lockAfterSeconds;
            // Enable/disable DragControls; replace with your logic
            const dragCtrl = this.gameObject.getComponent("DragControls");
            if (dragCtrl) dragCtrl.enabled = !shouldLock;
        }
    }
}

Key Details:

  • Use @syncable to sync creationTime across network.
  • Only the network owner sets creationTime on creation.
  • New users will receive the synced value and lock state.

Tips:

  • Ask for more details (like precise Needle Engine version, or component structure) if needed.
  • Read more on syncable properties in the Needle Engine docs.

Next:
Do you have custom drag logic or are you using the built-in DragControls?
Would you like help integrating this with your block object’s existing components?

1 replies left - Get a PRO license for more AI replies.
thread_qxLk3cmBGVMU4LvOKm7qelNo
Reply to this post directly if you need further help (bottom right button)

Hello,

the bot’s answer is almost correct. Unfortunately it didn’t use the syncField() decorator for networking the time value (syncable does not exist).

I’ve created a live example for your on stackblitz: Needle Engine: Detect Input Start Time (Networking) - StackBlitz
Open the link in multiple tabs or windows for testing.

Here’s also a copy of the code that I’ve written. Put this component to your scripts and attach it to the same object as your DragControls.

import { Behaviour, DragControls, Gizmos, serializable, syncField, } from '@needle-tools/engine';

export class SyncedInputChecker extends Behaviour {
    /** The time after which the object is locked */
    @serializable()
    lockDelay: number = 10;

    /** The time after which the object is automatically freed. This is additive to the lockDelay. If this number is 0 or below the input will never be automatically freed */
    @serializable()
    autoResetDelay: number = 20;

    @serializable()
    showDebug: boolean = true;

    /**
     * This field is networked and updated by this script when the user starts to click the object this component is attached to. The value is then sent to all connected remote users
     */
    @syncField()
    private pointerDownTime: number = 0;

    get isLocked() {
        // after 10 seconds no input is allowed anymore
        return (
            this.pointerDownTime <= 0 ||
            Date.now() - this.pointerDownTime < this.lockDelay * 1000
        );
    }

    // We expose an API to reset the state
    resetState() {
        this.pointerDownTime = 0;
    }

    // We use onPointerDown which happens when the user pressed the mouse button or touches the object or uses a spatial controller (VR)
    onPointerDown() {
        if (this.pointerDownTime > 0) return;
        this.pointerDownTime = Date.now();
    }

    update() {
        // a reference to this componnt could be stored once in onEnable or start
        const dragcontrols = this.gameObject.getComponent(DragControls);
        if (dragcontrols) dragcontrols.enabled = this.isLocked;

        if (this.pointerDownTime > 0) {
            let time = (Date.now() - this.pointerDownTime) / 1000;

            // for testing we reset automatically
            if (
                this.autoResetDelay > 0 &&
                time > this.lockDelay + this.autoResetDelay
            ) {
                this.resetState();
            }

            // just visualizing the time here by drawing a label gizmo above our object - this can be removed for production or put behind a debug flag
            if (this.showDebug && time > 0) {
                const pos = this.gameObject.worldPosition;
                pos.y += 1;
                Gizmos.DrawLabel(
                    pos,
                    time.toFixed(0) + `${this.isLocked ? '' : ' (locked)'}`,
                    0.1,
                    undefined,
                    undefined,
                    this.isLocked ? 0x99ffdd : 0xff5500
                );
            }
        }
    }
}

Thanks for your answer marwie1.

Do we have to do something additional to run the project in stackblitz ?

For now it remains on: (even after npm start)

Nope, just click the link. On mobile it can take a little moment to get ready. If unclear check the stackblitz terminal or try it locally (you can download the whole project as well if you want) or just use it as a reference :slight_smile: