Accurate positioning of items using getWorldPosition

So I’m trying to generate red spheres on top of food items that are dragged onto a plate, but I’m having trouble generating the “correct” position of the food item, and I am not sure whether I’m approaching this correctly. I have some scripts that are meant to manage this spawning of red spheres, and they’re working, but it’s creating some odd results.

  1. The positions are correct, in a technical sense but not in a practical sense. to demonstrate my point I’ll share a screencap

Original Post on Discord

by user 962902844049096704

The Yellow circle is where the plate is, the blue circle is where the food item is, and the tiny red dot far off is the red sphere that is supposed to be on top of the food item.

by user 962902844049096704

The code that gets the position of the food item and sets the target for the red sphere is as such: ```ts
import * as THREE from ‘three’;
import { GameObject } from “@needle-tools/engine”;
import { myEmitterInstance } from ‘…/…/include/controlscripts/MyEmitter.js’;
import { getWorldPosition } from “@needle-tools/engine”; // Import the getWorldPosition function

class FoodCoordinateManager {
private targets: THREE.Object3D = ;

constructor() {
    // Listen for the 'itemAddedToPlate' event
    myEmitterInstance.on('itemAddedToPlate', this.handleItemAddedToPlate.bind(this));
}

private handleItemAddedToPlate(name: string, position: THREE.Vector3, gameObject: GameObject) {
    // Get the accurate world position of the food item
    const accuratePosition = getWorldPosition(gameObject);

    // Create a new target position directly above the added food item
    const newTargetPosition = accuratePosition.clone();
    newTargetPosition.y += 0.05; // Adjust the Y value to set the height of the new target

    // Create a new GameObject to represent the target
    const targetObject = new GameObject();
    targetObject.position.copy(newTargetPosition);

    // Log to the console that a new target has been generated and emit its coordinates
    console.log(`A new target has been generated at coordinates: x: ${newTargetPosition.x}, y: ${newTargetPosition.y}, z: ${newTargetPosition.z}`);

    // Add the new target to the targets array
    this.targets.push(targetObject);
}

// Method to get the next target for a food item
getNextTarget(): THREE.Object3D | null {
    // Return and remove the first target from the targets array, or null if there are no targets
    return this.targets.shift() || null;
}

}

// Export a single instance of FoodCoordinateManager
export const foodCoordinateManagerInstance = new FoodCoordinateManager(); ```

by user 962902844049096704

that coordinate manager communicates with the redspheregenerator component like so ```ts
import * as THREE from “three”;
import { Behaviour, GameObject } from “@needle-tools/engine”;
import { getWorldPosition } from “@needle-tools/engine”;
import { myEmitterInstance } from ‘…/…/include/controlscripts/MyEmitter.js’;

import { foodCoordinateManagerInstance } from “./FoodCoordinateManager”; // Adjust the import path accordingly

export class RedSphereGenerator extends Behaviour {
private redMaterial = new THREE.MeshStandardMaterial({ color: 0xff0000 });
private sphereGeometry = new THREE.SphereGeometry(0.05);

awake() {
    // Listen for the 'itemAddedToPlate' event emitted by myEmitterInstance
    myEmitterInstance.on('itemAddedToPlate', this.generateRedSphere.bind(this));
}

private generateRedSphere(itemName: string, itemPosition: THREE.Vector3, itemGameObject: GameObject): void {
    // Get the next target position from the FoodCoordinateManager
    const targetObject = foodCoordinateManagerInstance.getNextTarget();
    if (!targetObject) {
        console.warn('No target available for red sphere generation.');
        return;
    }

    const redSphere = new GameObject();
    const sphereMesh = new THREE.Mesh(this.sphereGeometry, this.redMaterial);
    redSphere.add(sphereMesh);
    redSphere.position.copy(targetObject.position); // Set the position of the red sphere to the target position
    redSphere.scale.set(0.25, 0.25, 0.25);
    this.gameObject.add(redSphere);
    console.log(`Red sphere generated at target position due to ${itemName} being added!`);
}

}


*by user 962902844049096704*

and technically this is all working correctly as reported by the console.

by user 962902844049096704

but, practically this is not correct, because the system is not getting the actual in scene position of the food item, so it leads to the red sphere being spawned in a position that isn’t practically useful . If anyone can help point me in the right direction, so that I can solve this issue I’d be extremely appreciative.

by user 962902844049096704

I’m sure I’m just missing something really obvious here.

by user 962902844049096704

Hi, the position here is local space but above that you get the world space of an item. I would guess this is the error and why your object is displayed in the wrong place. You can use setWorldPosition instead of doing position.copy(...)

If you’re familiar with Unity this might be unexpected (since position in Unity is worldspace and localPosition is local space - but in three.js there’s only a position property which is in localspace of each object (the same is true for rotation/quaternion/scale)

Knowing that you don’t get paid nearly enough to help me with my stupid little problems, would you be able to give me a little example of an implementation ? :pray:

by user 962902844049096704

I guess what I don’t understand is how can the gameobject that has been moved onto the plate have a position different than the position of the object that is cloning said gameobjects position

by user 962902844049096704

Can you try changing the code above to this:

        const accuratePosition = getWorldPosition(gameObject);

        // Create a new target position directly above the added food item
        const newTargetPosition = accuratePosition.clone();
        newTargetPosition.y += 0.05; // Adjust the Y value to set the height of the new target

        // Create a new GameObject to represent the target
        const targetObject = new GameObject();
        setWorldPosition(targetObject, newTargetPosition);

(you need to import setWorldPosition from needle-engine just as you do with getWorldPosition)

Yep, I pushed those changed and nothing happened, which I assumed because I was implementing incorrectly, but no… no luck yet

by user 962902844049096704