Hello, I am trying to make a part’s position the same as the position of a surface GUI object., like this.
I really have no idea how to calculate this, and anchor points don’t make it any easier. If anyone has knowhow, please share.
Hello, I am trying to make a part’s position the same as the position of a surface GUI object., like this.
I really have no idea how to calculate this, and anchor points don’t make it any easier. If anyone has knowhow, please share.
I ‘might’ have a solution. Step by step
First we get the location of the box by it top left corner.
local gen = workspace.Generator.Display;
local pos = gen.Position + Vector3.new(-(gen.Size/2).X, (gen.Size/2).Y, -(gen.Size/2).Z);
print(pos)
local gen = workspace.Generator.Display;
local udimpos2 = (gen.SurfaceGui.Frame.AbsolutePosition+gen.SurfaceGui.Frame.AbsoluteSize/2)/gen.SurfaceGui.PixelsPerStud;
local pos = gen.Position + Vector3.new(-(gen.Size/2).X, (gen.Size/2).Y, -(gen.Size/2).Z) + Vector3.new(0, -udimpos2.Y, udimpos2.X);
print(pos)
If this doesn’t explain it I’ll try to come up with another method or find someone/post that could help with it. Since you asked how to find position this is my solution.
Edit: My method works if the part doesn’t have rotation to them, I only have sold the idea.
Sure let’s try figure this out Here is my test setup that I used:
Essentially we can calculate the positions and sizes by using the “Board part” as a reference as well as checking SurfaceGui’s PixelsPerStud ratio. Then its just a matter of offsetting
This is the code I used, to achieve this. I’ve only included info regarding a single face so you are free to edit it however you like. I also did not account for SurfaceGui being fixed size nor did I add rotations but both of those are fairly easy additions
local SurfaceGui = script.Parent.Board.SurfaceGui
local Part = script.Parent.Part
local SurfaceInfo = {
[Enum.NormalId.Front] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Right,
Right = Enum.NormalId.Left,
Down = Enum.NormalId.Bottom
},
[Enum.NormalId.Back] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Left,
Right = Enum.NormalId.Right,
Down = Enum.NormalId.Bottom
},
[Enum.NormalId.Top] = {
Up = Enum.NormalId.Left,
Left = Enum.NormalId.Back,
Right = Enum.NormalId.Front,
Down = Enum.NormalId.Right
},
[Enum.NormalId.Bottom] = {
Up = Enum.NormalId.Right,
Left = Enum.NormalId.Back,
Right = Enum.NormalId.Front,
Down = Enum.NormalId.Left
},
[Enum.NormalId.Left] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Front,
Right = Enum.NormalId.Back,
Down = Enum.NormalId.Bottom
},
[Enum.NormalId.Right] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Back,
Right = Enum.NormalId.Front,
Down = Enum.NormalId.Bottom
}
}
function CreatePart(element: GuiBase2d)
local ReferencePart: BasePart = SurfaceGui.Adornee or SurfaceGui.Parent
local info = SurfaceInfo[SurfaceGui.Face]
local Direction = Vector3.FromNormalId(SurfaceGui.Face)
local Up = Vector3.FromNormalId(info.Up)
local Left = Vector3.FromNormalId(info.Left)
local Right = Vector3.FromNormalId(info.Right)
local Down = Vector3.FromNormalId(info.Down)
local Size = ReferencePart.Size
local NewPart = Part:Clone()
local ElementCorner = element.AbsolutePosition / SurfaceGui.PixelsPerStud
local ElementSize = element.AbsoluteSize / SurfaceGui.PixelsPerStud
local NewSize = Left * ElementSize.X + Up * ElementSize.Y + Direction
local PositiveSize = Vector3.new(
math.sign(NewSize.X) * NewSize.X,
math.sign(NewSize.Y) * NewSize.Y,
math.sign(NewSize.Z) * NewSize.Z
)
NewPart.Size = PositiveSize
local CF = CFrame.new(
(Direction + Up + Left) * Size/2 -- Move to "top left"
-- Offset by element corner position
+ Right * ElementCorner.X
+ Down * ElementCorner.Y
-- Size offset
+ Right * ElementSize.X/2
+ Down * ElementSize.Y/2
+ Direction/2 -- I've set depth size to 1 stud
) * CFrame.fromAxisAngle(-Direction, math.rad(element.AbsoluteRotation))
NewPart.CFrame = ReferencePart.CFrame:ToWorldSpace(CF)
NewPart.Parent = script.Parent
end
for i,v in SurfaceGui:GetChildren() do
if v:IsA("GuiBase2d") then
CreatePart(v)
end
end
Hopefully this gets you started If you have any additional questions, feel free to ask!
EDIT: Cleaned up the code a little to make it more readable.
EDIT2: Added rest of the faces and rotation support
This works well, however, the part seems to move on the Z axis instead of the X axis.
Basically, when turning the ui object left and right, the part goes back and forth
This is because the direction depends on which face the surface gui is on Those need to be taken into account.
One way you could do this, is get the percentage of the frame’s absolute position within the surface gui using inverselerp. then have the part pointtoobjectspace of the surfacegui part and then lerp between the size of the part with the percentage you got before.
How would I take faces into account with this code?
local udimpos2 = (UI.Frame.AbsolutePosition+UI.Frame.AbsoluteSize/2)/UI.PixelsPerStud;
local pos = surfacePart.Position + Vector3.new(-(surfacePart.Size/2).X, (surfacePart.Size/2).Y, -(surfacePart.Size/2).Z) + Vector3.new(0, -udimpos2.Y, udimpos2.X);
Here, I wrote the code for you. This should work with static objects (not moving) however if you got the ui constantly moving the part may fall behind.
local board = Instance.new'Part'
board.Size = Vector3.new(10, 10, 0.1)
board.Anchored = true
board.Parent = workspace
local surfaceGui = Instance.new'SurfaceGui'
surfaceGui.Face = Enum.NormalId.Back
surfaceGui.Adornee = board
surfaceGui.Parent = board
local frame = Instance.new'Frame'
frame.Size = UDim2.new(0.3, 0, 0.3, 0)
frame.AnchorPoint = Vector2.new(0.5, 0.5)
frame.Position = UDim2.new(0.5, 0, 0.5, 0)
frame.BackgroundColor3 = Color3.new(0, 0, 0)
frame.Parent = surfaceGui
local part = Instance.new'Part'
part.Size = Vector3.new(1, 1, 1)
part.Anchored = true
part.Parent = script
local rs = game:GetService'RunService'
local function inverseLerp(a, b, t)
return (t - a) / (b - a)
end
local function lerp(a, b, t)
return a + (b - a) * t
end
local function get2Dto3D_CF(part)
local boardSizeX = board.Size.X
local boardSizeY = board.Size.Y
local framePosPercentageX = inverseLerp(0, surfaceGui.AbsoluteSize.X, frame.AbsolutePosition.X + frame.AbsoluteSize.X / 2)
local framePosPercentageY = inverseLerp(0, surfaceGui.AbsoluteSize.Y, frame.AbsolutePosition.Y + frame.AbsoluteSize.Y / 2)
local partPosX = lerp(-boardSizeX / 2, boardSizeX / 2, framePosPercentageX)
local partPosY = -lerp(-boardSizeY / 2, boardSizeY / 2, framePosPercentageY)
local zOffset = 1
return part.CFrame:ToObjectSpace(board.CFrame) * CFrame.new(partPosX, partPosY, zOffset)
end
frame.Position = UDim2.new(math.sin(tick() * 2) * 0.5 + 0.5, 0, math.cos(tick() * 2) * 0.5 + 0.5, 0)
part.CFrame = get2Dto3D_CF(part)
Have you looked at the example I posted for you?
I am going to be trying it! Thank you for the help
Worked like a charm! Thank you very much!
No problem! It was fun figuring it out Good luck on your project!
Hey, I just noticed it seems to not be orientated correctly on different faces
With front as the face
TLDR; I am wondering if you know how to make it so the part faces in the same direction as the GUI object, like in the first picture
The current version of the code makes it so if SurfaceGui is on Left face, then the created part will also be facing in a way where the outside face is Left. Are you hoping that the created parts are always facing in the Front face?
I would like the part to rotate so that the images always stays like in the first picture, without changing the face
Which face is the picture on? Front?
Right, well here is a modified version where the front face is always facing outwards
local SurfaceGui = script.Parent.Board.SurfaceGui
local Part = script.Parent.Part
local SurfaceInfo = {
[Enum.NormalId.Front] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Right,
Right = Enum.NormalId.Left,
Down = Enum.NormalId.Bottom
},
[Enum.NormalId.Back] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Left,
Right = Enum.NormalId.Right,
Down = Enum.NormalId.Bottom
},
[Enum.NormalId.Top] = {
Up = Enum.NormalId.Left,
Left = Enum.NormalId.Back,
Right = Enum.NormalId.Front,
Down = Enum.NormalId.Right
},
[Enum.NormalId.Bottom] = {
Up = Enum.NormalId.Right,
Left = Enum.NormalId.Back,
Right = Enum.NormalId.Front,
Down = Enum.NormalId.Left
},
[Enum.NormalId.Left] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Front,
Right = Enum.NormalId.Back,
Down = Enum.NormalId.Bottom
},
[Enum.NormalId.Right] = {
Up = Enum.NormalId.Top,
Left = Enum.NormalId.Back,
Right = Enum.NormalId.Front,
Down = Enum.NormalId.Bottom
}
}
function CreatePart(element: GuiBase2d)
local ReferencePart: BasePart = SurfaceGui.Adornee or SurfaceGui.Parent
local info = SurfaceInfo[SurfaceGui.Face]
local Direction = Vector3.FromNormalId(SurfaceGui.Face)
local Up = Vector3.FromNormalId(info.Up)
local Left = Vector3.FromNormalId(info.Left)
local Right = Vector3.FromNormalId(info.Right)
local Down = Vector3.FromNormalId(info.Down)
local Size = ReferencePart.Size
local NewPart = Part:Clone()
local ElementCorner = element.AbsolutePosition / SurfaceGui.PixelsPerStud
local ElementSize = element.AbsoluteSize / SurfaceGui.PixelsPerStud
local DepthSize = 1
local NewSize = Vector3.new(ElementSize.X, ElementSize.Y, DepthSize)
NewPart.Size = NewSize
local Position = (Direction + Up + Left) * Size/2 -- Move to "top left"
-- Offset by element corner position
+ Right * (ElementCorner.X + ElementSize.X/2)
+ Down * (ElementCorner.Y + ElementSize.Y/2)
-- Size offset
+ (Direction * DepthSize)/2
local CF = CFrame.fromMatrix(Position, Left, Up, -Direction) * CFrame.Angles(0,0,math.rad(element.AbsoluteRotation))
NewPart.CFrame = ReferencePart.CFrame:ToWorldSpace(CF)
NewPart.Parent = script.Parent
end
for _,face in Enum.NormalId:GetEnumItems() do
SurfaceGui.Face = face
for i,v in SurfaceGui:GetChildren() do
if v:IsA("GuiBase2d") then
CreatePart(v, Part)
task.wait(1)
end
end
end
Hopefully that works out for you
Worked like a charm! Thank you very much for all the help you provided me : D
This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.