Is there a way to create ScriptableObjects like in Unity using Needle?

Hello everyone, Iā€™m trying to create a game using Needles. Iā€™m attempting to make a game in which I need to create something similar to a Scriptable Object. Iā€™ve searched the Needles documentation, but I canā€™t seem to find the answer. If anyone could help, I would greatly appreciate it. Or if thereā€™s a way to achieve this, I would like to know.

The game is essentially like the mini-games where you receive a recipe and have to cook it. Iā€™ve already managed to make the player move and interact to some extent. The problem arises when I try to create an object. I need the player to be able to create the object, pick it up, place it on a table, and pick it up again. In this case, I believe I need to change the objectā€™s parent, and I think using a Scriptable Object could be a solution.

In the Needles documentation, Iā€™m using the following code to create the object: @serializable(AssetReference)
myScenes?: AssetReference;

async awake() {
    if (!this.myScenes) {
        return;
    }
    for (const scene of this.myScenes) {
        // check if it is assigned in unity
        if(!scene) continue;
        // load the scene once
        const myScene = await scene.loadAssetAsync();
        // add it to the threejs scene
        this.gameObject.add(myScene);
        
        // of course you can always just load one at a time
        // and remove it from the scene when you want
        // myScene.removeFromParent();
        // this is the same as scene.asset.removeFromParent()
    }
}

onDestroy(): void {
    if (!this.myScenes) return;
    for (const scene of this.myScenes) {
        scene?.unload();
    }
}

}

Original Post on Discord

by user 691445074055397447

Hey :wave: Iā€™m unsure if understand it correctly. What element in the above text would be a ScriptableObject?

Hm, if the spawning doesnā€™t work, try using instantiate instead of loadAssetAsync.

None of the elements are Scriptable Objects. I wanted to know if there is a way to create one using Needles.

by user 691445074055397447

public loadScene(ingredientsName: string) {
// Check if the list of scenes has been configured

console.log(ingredientsName);
if (this.ingredients === undefined) {
    console.error("The list of foods has not been configured.");
    return;
} else {



 }


 

// Get the AssetReference for the scene by its name
const scenePrefab = this.getSceneAsset(ingredientsName);

if (scenePrefab) {
    // Load the scene using the AssetReference
    this.load(scenePrefab);
} else {
    console.error(`No scene found with the name: ${ ingredientsName}`);
}

}

private async load(scenePrefab: AssetReference) {
const scene = await scenePrefab.loadAssetAsync();
if (scene) {
if (this.playerTopPoint) {
this.playerTopPoint.add(scene); // Add the loaded scene to the scene root
} else {
this.gameObject.add(scene); // Add the loaded scene to the GameObject itself
}
this.loadedingredients.push(scenePrefab); // Add the loaded scene to the array
} else {
if(!this.hasFood) {

this.loadSceneInTable(this.ingredientsFilter);
console.log("se est ejecutndo esta funcion ")
  
}

}
}

/**

  • Gets the AssetReference of a scene by its name.

  • @param value - The name of the scene.

  • @returns The AssetReference of the scene or undefined if not found.
    */
    private getSceneAsset(value: string): AssetReference | undefined {
    // Construct the full URI
    const sceneUri = assets/${value}.glb;

    // Find the AssetReference of the scene by name
    const sceneAssetReference = this.ingredients?.find(prefab => prefab.uri === sceneUri);

    // Return the scene asset reference (or undefined if not found)
    return sceneAssetReference;
    }

by user 691445074055397447

This is my code, and actually, the object is being instantiated. What I need is to control or figure out how to change the objectā€™s position. Initially, the player interacts and picks up the item, letā€™s say a tomato. Then, if they interact again, they should be able to place it on a table, and if they want to interact again, they can pick it up. I mention Scriptable Objects because I see it being done this way in a YouTube video Iā€™m watching. I apologize for the question; Iā€™m really new to this, but Iā€™m eager to learn.

by user 691445074055397447

I see, first things first.
You can format your code if you write short snippets

```ts
YOUR_CODE
```

will result in

YOUR_CODE

it will be correctly formatted and highlighted :+1:
Alternative way is to drop the whole .ts file here, which works great as well and makes the code readable right here in discord.

public loadScene(ingredientsName: string) {
    // Check if the list of scenes has been configured

    console.log(ingredientsName);
    if (this.ingredients === undefined) {
        console.error("The list of foods has not been configured.");
        return;
    } else {



     }




    // Get the AssetReference for the scene by its name
    const scenePrefab = this.getSceneAsset(ingredientsName);

    if (scenePrefab) {
        // Load the scene using the AssetReference
        this.load(scenePrefab);
    } else {
        console.error(No scene found with the name: ${ ingredientsName});
    }
}

private async load(scenePrefab: AssetReference) {
  const scene = await scenePrefab.loadAssetAsync();
  if (scene) {
      if (this.playerTopPoint) {
          this.playerTopPoint.add(scene); // Add the loaded scene to the scene root
      } else {
          this.gameObject.add(scene); // Add the loaded scene to the GameObject itself
      }
      this.loadedingredients.push(scenePrefab); // Add the loaded scene to the array
  } else {
    if(!this.hasFood) {



    this.loadSceneInTable(this.ingredientsFilter);
    console.log("se est ejecutndo esta funcion ")

    }
  }
}


/**
 
Gets the AssetReference of a scene by its name.
@param value - The name of the scene.
@returns The AssetReference of the scene or undefined if not found.
*/
private getSceneAsset(value: string): AssetReference | undefined {
     // Construct the full URI
    const sceneUri = assets/${value}.glb;

    // Find the AssetReference of the scene by name
    const sceneAssetReference = this.ingredients?.find(prefab => prefab.uri === sceneUri);

    // Return the scene asset reference (or undefined if not found)
    return sceneAssetReference;
}

by user 691445074055397447

ohh ok ok thank you

by user 691445074055397447

So the code above spawns the ingredients on the table.

Where exactly do you get into trouble?

  1. How to know when to pick it up?
  2. How to reparent it?
  3. Or how to return it back?

In what stage you need help.

Exactly, currently, the ingredient spawns on the table, and I need to be able to change that spawn to be on the player and be able to put it back on the table. So, I think Iā€™m at the point 2: How to reparent it?

by user 691445074055397447

I see :+1: So, you donā€™t want to spawn it again. You want to use the instance you have on the table already.

So, what is the input? How do you want to choose what to pick up? By distance/ presence? Or is it UI where you know what exactly your clicking on?

Okay, first of all, Iā€™m really new to this. Iā€™m trying to learn on my own. I know very little about Unity, and I have some knowledge of Three.js and TypeScript. Since there arenā€™t really any Needles tutorials on YouTube, Iā€™ve had to learn on my own. The input I have is that when the player collides with a counter and presses the E key, the interaction occurs. I think that would be my input.

by user 691445074055397447

 private handleInteractions() {
    const rayDirection = new Vector3(0, 0, 1); // Forward in negative Z
    const interactDistance: number = 2; // Adjust as needed

    const raycastResult = this.context.physics.engine?.raycast(
      this.gameObject.transform.position,
      rayDirection,
      interactDistance
    );

    if (raycastResult) {
      const hitPoint = raycastResult.point;
      const hitCollider = raycastResult.collider;
      const clearCounter = hitCollider.gameObject.getComponent(ClearCounter);

      if (clearCounter) {
        this.selectedCounter = clearCounter;
        clearCounter.showCounter();
      } else {
        if (this.selectedCounter) {
          this.selectedCounter.hideCounter();
        }
      }

      if (clearCounter && this.keysPressed['e']) {
        clearCounter.Interact();

        // recibir el evento onFood 
        this.addEventListener("onFood", () => {
          console.log("onFood");
        });
      }
    }
  }


by user 691445074055397447

the eventlistener its not working

by user 691445074055397447

Ah, so how do the show and hide counter methods look like?

  public showCounter() { 
    // console.log("showCounter");
    if (this.visualCounter) {
      GameObject.setActive(this.visualCounter, true);
    } 
  }

  public hideCounter() {

    // hide counter
    if (this.visualCounter) {
      GameObject.setActive(this.visualCounter, false);
    }
  }

by user 691445074055397447

Those methods are purely visual to make it possible to see when the player makes contact with the counter, allowing differentiation by simply changing the color.

by user 691445074055397447

Do you want to have 2 different instances of the ā€œTomatoā€? Or it can be the same? Some games have 2 models.

And do you really want to pick it up visually? Not just like ā€œdataā€ in an inventory.

Initially, I would like it to be just one. The issue is that later in the gameplay, the player can slice the tomato, which would be another visual like ā€œslice Tomato,ā€ for example. But what I really want to do now is visually allow the player to pick it up and place it on the counter.

by user 691445074055397447

Basically, to practice and improve and learn.

by user 691445074055397447