How preload AssetReference before instantiate?

How preload AssetReference before instantiate?

Hi oot4chi, you can preload an AssetReference by first calling its preload() method to load the asset binary without creating an instance, then later instantiate it when needed. This is useful if you want to avoid delays when instantiating the asset later on.

For example, you might write a component like this:

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

export class PreloadAssetComponent extends Behaviour {
  @serializable(AssetReference)
  asset?: AssetReference;

  private preloadedAsset: any;

  async awake() {
    if (this.asset) {
      // Preload the asset binary ahead of time
      await this.asset.preload();
      // Optionally, you can also load the asset to store it in a variable:
      this.preloadedAsset = this.asset.asset;
    }
  }

  async createInstance() {
    if (this.asset) {
      // Instantiate a new instance from the preloaded asset.
      // Note: instantiate() will create a copy from the cached asset.
      const instance = await this.asset.instantiate();
      this.context.scene.add(instance);
    }
  }
}

In this example, the asset is preloaded during the awake phase. Later, when you call createInstance(), a new instance is created from the cached asset. You can also use loadAssetAsync() which loads and instantiates the asset in one go, but if you want to split preloading and instantiation, preload() plus instantiate() is the way to go. This is based on the Needle Engine API details found in our documentation at engine_addressables.d.ts.md and the prefab example .

Let me know if you have any other questions!

1 replies left - Become PRO for more replies.
thread_JsplaBms21gISQnYjoL1r1nZ
Reply to this post directly if you need further help (bottom right button)

how preload and instantiate later without await?

Hi oot4chi!

To preload an AssetReference and instantiate it later without using await, you can initiate the preload and use a callback or event to verify when it is fully loaded. Here’s an example:

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

export class PreloadAndInstantiateComponent extends Behaviour {
  @serializable(AssetReference)
  myPrefab?: AssetReference;

  start() {
    // Preload the asset without await
    this.myPrefab?.preload().then((result) => {
      console.log('Asset preloaded', result);
      // Optionally instantiate later when needed
      this.instantiateAsset();
    }).catch(err => {
      console.error('Error preloading asset:', err);
    });
  }

  instantiateAsset() {
    if (this.myPrefab) {
      // Instantiate the preloaded asset
      this.myPrefab.instantiate().then(instance => {
        this.gameObject.add(instance);
      }).catch(err => {
        console.error('Error instantiating asset:', err);
      });
    }
  }
}

In this example, preload() returns a promise that resolves when the asset is preloaded. You can then instantiate it at any later point when you require it. This allows for non-blocking operations while preloading assets efficiently.

If you have more specific scenarios or requirements, feel free to share!

1 replies left - Become PRO for more replies.
thread_JsplaBms21gISQnYjoL1r1nZ
Reply to this post directly if you need further help (bottom right button)

without callback too. need return GameObject function.

Hi oot4chi!

If you want to preload an AssetReference and instantiate it later without using await or callbacks, you can directly call the preload method and check if it’s loaded later on using the isLoaded() method. Here’s an example:

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

export class PreloadAndInstantiateComponent extends Behaviour {
  @serializable(AssetReference)
  myPrefab?: AssetReference;

  start() {
    // Preload the asset without waiting for it to complete
    this.myPrefab?.preload();
  }

  update() {
    // Check if the asset is loaded before instantiating
    if (this.myPrefab && this.myPrefab.isLoaded()) {
      const instance = this.myPrefab.instantiate(); 
      // Here you can add the instance to the scene
      this.gameObject.add(instance); 
    }
  }
}

In this example:

  • The asset is preloaded during the start() method.
  • In the update() method, you can check if the asset is loaded using isLoaded().
  • If it is loaded, you can instantiate it without blocking or waiting; just remember that instantiate() will still return a promise, so if you truly want to instantiate immediately then it requires handling based on your flow.

Let me know if you need further help!

0 replies left - Become PRO for more replies.
thread_JsplaBms21gISQnYjoL1r1nZ
Reply to this post directly if you need further help (bottom right button)

I encountered an issue where setting the position right after calling GameObject.instantiate() doesn’t seem to work as expected:

const newObj = await GameObject.instantiate(prefab);
newObj.position.copy(currentPos);

At first glance, this should place the new object at currentPos, but the position doesn’t update immediately. After some testing, I realized that the instantiated GameObject may not be fully initialized until the next frame. So setting .position right after instantiate() might get overridden or ignored internally.

Is this expected behavior in Needle Engine? If so, is there a proper lifecycle hook or callback after instantiation that ensures the object is fully ready for transformation updates?

There are quite a few of those pitfalls to look out for.

For now you can use InstantiateOptions e.g. like this:

await GameObject.instantiate(prefab, { position: currentPos });

Hello @oot4chi the object is fully initialized after instantiate has happened. Do you perhaps have a component on the prefab instance that also modifies the position? Or is any of the materials using instancing?

It doesnt reproduce here: Needle-engine - Vite Template (duplicated) - StackBlitz

If you can provide a reproduction it would be helpful

I’m encountering a collision bug in Spawn1:

async Spawn1(): Promise<void> { 
    const carInScene = await this.carPrefab?.instantiate();
    carInScene?.position.set(0, 0, 5);
}

The problem is that the instantiated object briefly exists at the prefab’s default position (usually at (0, 0, 0)) before it gets moved to (0, 0, 5). If there’s any collider at (0, 0, 0), the object will immediately collide with it upon spawning.

I fixed the issue by temporarily disabling the object right after instantiation, repositioning it, and then re-enabling it:

async Spawn2(): Promise<void> { 
    const carInScene = await this.carPrefab?.instantiate();
    GameObject.setActive(carInScene, false);
    carInScene?.position.set(0, 0, 5);
    GameObject.setActive(carInScene, true);
}

This way, the object doesn’t interact with any colliders until it’s in the correct position.

Is this the recommended way to avoid collider issues on spawn, or is there a better practice?

this bug first frame position start at (0,0,0)

import {
  AssetReference,
  GameObject,
  onUpdate,
} from '@needle-tools/engine';
import { Vector3 } from 'three';

onUpdate((context) => {
  if (context.input.getPointerDown(0)) {
    test();
  }
});

async function test() {
  const ar = AssetReference.getOrCreateFromUrl(
    'https://cloud.needle.tools/-/assets/Z23hmXBZ1Mqr5s-latest-product/file'
  );

  const newObj = await GameObject.instantiate(ar);
  if (newObj) {
    newObj?.position.set(0, 3, 0);
  }

  setTimeout(() => {
    GameObject.destroy(newObj!);
  }, 100);
}

Ah i see, I think in this particular case the issue is caused by the async await - the code in the async function runs and adds the object to the scene but when it returns it’s already rendered for one frame…

I need to investigate it further if there’s something we can do to prevent this but I’m not sure right now.

In the meantime can you change your code to either pass in the position etc to the instantiate method (like krizzlor suggested) or use a differnet approach? (see link below)


  // load instante
  await ar.loadAssetAsync();
  // instantiate
  const newObj = instantiate(ar.asset!);
  if (newObj) {
    scene.add(newObj);
    newObj?.position.set(0, 3, 0);
  }
1 Like

BTW In Unity, we usually avoid calling Instantiate() repeatedly in runtime because it leads to frequent memory allocation and garbage collection, which causes frame rate drops — especially on mobile or WebGL.

In Needle, when we do:

const go = instantiate(this.prefab.asset);
this.context.scene.add(go);

does this instantiate logic also create a new object every time like Unity? Or is there some built-in pooling optimization?

Just want to confirm if it’s still recommended to build a custom pooling system for performance reasons.

Instantiate behaves the same way as in Unity and creates a new object every time. If you have to create a ton of objects then yes you’d like to pool them (and you want to create a pooling system)

1 Like

This topic was automatically closed 13 days after the last reply. New replies are no longer allowed.