Aligning 3D Positioned UI from a 2D perspective

Hi all,
I’m attempting to make a system similar to the Diablo style UI loot interface, where the ‘nametags’ for loot do not overlap in the event that there are several items in close proximity. Instead, they are aligned in a way that they touch edges. This makes every name of every item visible at once. Here is an image for reference (referring to the tags containing the item’s name): image

What I have currently is spawning an item and simply attaching a BillboardGui with properties related to the item, with a bit of Y offset so it appears above the item. There is no other positioning happening:

-- ModuleScript in ServerStorage
local nametag = nametagCopy:Clone() -- Copy nametag (BillboardGui with a TextLabel child) from ServerStorage
nametag.Label.TextColor3 = itemData.Color.Color -- BrickColor.Color the label using rarity constants' color from the item generated
nametag.Label.Text = string.format("%s %s", itemData.Rarity, item.Name) -- Format rarity + item name
local sizeBounds = game:GetService("TextService"):GetTextSize(nametag.Label.Text, 20, Enum.Font.Fantasy, Vector2.new(400, 30)) -- Fit the size to the bounds of the text
nametag.Size = UDim2.new(0, sizeBounds.X, 0, sizeBounds.Y) -- Size it using those bounds
nametag.Parent = item -- Parent it to the item that was spawned

Unfortunately I haven’t attempted anything to replicate the goal; I have no idea how to even approach this. It seems very complicated and I’m not certain the effort to create it would be worth the (likely) hacked-together product.

Wouldn’t this require converting the 3D position of the item’s nametag to a 2D screen-space and somehow (that’s a BIG somehow) arranging them vertically/horizontally to not overlap? It seems very overwhelming, and I’m not even sure if this is possible in Roblox!

Maybe I need to re-approach how I’m creating the nametags? I really have no ideas on how I would go about doing this :frowning:

Any ideas would be much appreciated, thank you!

3 Likes

One very simple way is sorting each label from highest to lowest Y position (i.e. from the bottom of the screen to the top), and then pushing each label up so that its bottom edge is at the same height as the top edge of the previous label. It looks like so:

You go from this

To this

Here's the code I threw together to get this effect
local gui = script.Parent
local itemLabel = gui.Box

itemLabel.Parent = nil

local ls = {}

for _ = 1, 10 do
	local l = itemLabel:Clone()
	l.Size = UDim2.new(
		0, math.random(50, 200),
		0, math.random(30, 50)
	)
	l.Position = UDim2.new(
		math.random(20, 80)/100, 0,
		math.random(20, 80)/100, 36
	)
	table.insert(ls, l)
	l.Parent = gui
end

table.sort(ls, function(a, b) return a.AbsolutePosition.Y > b.AbsolutePosition.Y end)

wait(4)

for i, l in pairs(ls) do
	local h = (i/#ls)*0.5
	l.BackgroundColor3 = Color3.fromHSV(h, 1, 1)
	
	if i > 1 then
		--Current label
		local l = ls[i]
		local lp = l.AbsolutePosition
		local lz = l.AbsoluteSize
		
		--Previous label
		local p = ls[i - 1]
		local pp = p.AbsolutePosition
		local pz = p.AbsoluteSize
		
		l.Position = UDim2.new(
			0, lp.X, 0, pp.Y - (lz.Y/2 + l.BorderSizePixel)
		)
	else
		l.BackgroundColor3 = Color3.new()
	end
end

image

Notice that the AnchorPoint is (0.5, 0.5)

It’s not perfect, because for some of the boxes it’s not necessary to move them at all. You could modify it to only move a box up if it’s overlapping any of the previous boxes. Look up AABB collision detection if you want to try that.

2 Likes

It looks as if his system is using BillboardGuis, so you would need to find how far to offset the tags from the part, and update it in RenderStepped, or you could just use the Camera method WorldToScreenPoint and have all the item tags as ScreenGui elements.

2 Likes