[SOLVED] Spawn screen element at entity position

Hey hey :slight_smile:

I’m trying to spawn entities with an element component in our gui based on the position of an entity in worldspace. But I can’t seem to figure out how to get the correct coordinates of where to place the element inside the screen component.

I tried using the camera.worldToScreen() to calculate the screenPosition of my entity and set the localPosition of my element to it, but this screenPosition isn’t the same as the position inside the screenComponent

// this.entity is the original entity inside the gui with an element component
var clone = this.entity.clone();
this.entity.element.screen.addChild(clone);
clone.setLocalPosition(0, 0, 0);

// entity is the world-space-entity, at which screen position I want to spawn it
var entityScreen = camera.worldToScreen(entity.getPosition());

clone.setLocalPosition(entityScreen);

I then tried calculating the screenPosition of both, taking the difference and then adding that to the elements position, but again, the coordinate system is different and therefore there is always an offset.

// this.entity is the original entity inside the gui with an element component
var clone = this.entity.clone();
this.entity.element.screen.addChild(clone);

// entity is the world-space-entity, at which screen position I want to spawn it
var entityScreen = camera.worldToScreen(entity.getPosition());
var cloneScreen = camera.worldToScreen(clone.getPosition());

var difference = new pc.Vec3().sub2(entityScreen, cloneScreen);
clone.setLocalPosition(difference.x, difference.y * -1, difference.z);

Has anyone had the same problem and found a solution for it? It would be much appreciated :slight_smile:
Thanks

Hi @AliMoe,

I haven’t really done something similar to help out. But if you are just trying to put a 2D graphic on top of a world model/entity maybe you can get away with using a screen component.

You can use a plane facing to the camera with a regular material and have it render on top of the scene. Take a look at this post:

Hey AliMoe!

Whenever I’ve done this, I’ve found to make sure your screen component is set to screenSpace = true and scaleMode = none.

The UI element you want to position in this space also needs to have its anchors set to [0, 0, 0, 0] to maintain a non-relative position in this space. Then to get the position of a world-object in screen space I use this function

function worldSpaceToScreenSpace(device: pc.GraphicsDevice, pos: pc.Vec3, camera: pc.Entity) {
    var worldPos = pos.clone();
    var screenPos = new pc.Vec3();
    // get screen space co-ord
    camera.camera!.worldToScreen(worldPos, screenPos);
    var scale = device.maxPixelRatio;
    return new pc.Vec3(screenPos.x * scale, device.height - (screenPos.y * scale), 0);
}

Hope this helps

1 Like

Thanks guys for the quick reply

@Leonidas
Unfortunately a world screen component won’t do. The context of our game is that you have to collect certain animals, and when you have found one, an emblem should appear at the animals place and then move to the top of the GUI, where all other emblems are shown as well.

@Tchom
Since these emblems need to move to the top of the UI, i need to set the anchor to the top as well (or it might get cut off on certain resolutions). Also the rest of our gui is built with scale mode blend in mind. But thanks for the function, maybe I can salvage some of it :wink:

I got it :grin: I fixed the offset by first placing the clone at the screens origin, and then adding the reference resolution and actual resolution to the calculation of the position. I’m leaving the fixed code here for future references :wink:

// this.entity is the original entity inside the gui with an element component
var clone = this.entity.clone();
this.entity.element.screen.addChild(clone);
clone.setLocalPosition(0, 0, 0);

// entity is the world-space-entity, where I want to spawn the clone
var entityScreen = camera.worldToScreen(entity.getPosition());
var cloneScreen = camera.worldToScreen(clone.getPosition());
var difference = new pc.Vec3().sub2(entityScreen, cloneScreen);

var resolution = this.entity.element.screen.screen.resolution;
var referenceResolution = this.entity.element.screen.screen.referenceResolution;

clone.setLocalPosition(
  difference.x * referenceResolution.x / resolution.x,
  difference.y * -1 * referenceResolution.x / resolution.x,
  difference.z
);
3 Likes

Thanks for sharing that @AliMoe!