ScreenGui to world position (bullet holes)

I’m creating an FPS game, so I need bullet holes. In the beginning I used parts but had a problem with clipping outside the part area. Now I’m using ScreenGui and the question is how do I offset gui to the ray.Position?

To sum up, its as if the part was a screen. You can then position the decals accordingly.

Yea I get that, but how would I find exact value of UDim2 that places gui where the ray hits the part?

You would cast ray from mouse and check if it hits a part (maybe wall or basic cube)
If it hits part then get the Viewport Position by doing:
local worldposition = WorldToViewportPoint(somerayposition)
and set position of gui element by UDim2 with x, y axis from worldposition.

Here’s example of my function (it may be incorrect as it’s my first time learning about raycast):

local function CastRay()
	local mouse = UserInputService:GetMouseLocation() -- GetMouse() is deprecated
	local ray = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y) -- Ray from screen point (cursor)
	local ignoreList = {} -- Ignore list if it hit specific part

	local hitPart, hitPosition = workspace:FindPartOnRayWithIgnoreList(ray, ignoreList)
	if hitPart then
		-- Offset the GUI element to the hit position
		local viewportPosition = workspace.CurrentCamera:WorldToViewportPoint(hitPosition)
		guiElement.Position = UDim2.new(0, viewportPosition.X, 0, viewportPosition.Y)
	else
		-- Hide the GUI element if there's no hit
		guiElement.Position = UDim2.new(2, 0, 2, 0) -- Move the GUI element outside the screen
	end
end

It does not work :frowning: , here is my code:

Local Script

if ray then
	local viewportPosition = camera:WorldToViewportPoint(ray.Position)
	local offset = UDim2.new(0, viewportPosition.X, 0, viewportPosition.Y)

	fireEvent:FireServer(weapon.Name, firePart.CFrame, cf, mouse.Hit, module.BulletSpeed, module.Damage, offset)
end

Server Script (This code is in fireEvent.OnServerEvent)

local bulletHole = Instance.new("SurfaceGui")
local frame = Instance.new("Frame")
local image = Instance.new("ImageLabel")
image.Image = "rbxassetid://13688262309"
bulletHole.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
bulletHole.ClipsDescendants = true
frame.BackgroundTransparency = 1
image.BackgroundTransparency = 1
image.ImageColor3 = ray.Instance.Color
frame.Size = UDim2.new(0, 25, 0, 25)
image.Size = UDim2.new(1, 0, 1, 0)

---------------------------- Settings

bulletHole.Face = normalToFace(ray.Normal, ray.Instance)
frame.Position = offset -- the line you are looking for

bulletHole.Parent = ray.Instance
frame.Parent = bulletHole
image.Parent = frame

I don’t understand this very well, please help me I really need it