Need help with SurfaceGUI moving to mouse

Hey! I’m currently making a spray can feature for my game. Its where you have a tool and when you click on an object (such as a wall), it puts a SurfaceGUI on the part and the side the mouse is on. Its supposed to put the GUI where the mouse is, but I don’t know how

I’m using a SurfaceGUI instead of creating a new part for one main reason. With SurfaceGUI, I can make it so the decal cuts off on corners, and actually stick to the part. I tried creating a new part instead, but I like SurfaceGUI more.

Once again, the issue with SurfaceGUI is that I have no idea how to actually position the GUI to where the mouse is. I’ve tried just using the X and Y of the mouse but this happens:

https://gyazo.com/6144fd6fdba6e9110b598b55e3fd2c13

Code:

local mouse = player:GetMouse()
local sprayPos = UDim2.new(0, mouse.X, 0, mouse.Y)
spray.Position = sprayPos

I’ve also tried setting the scale position instead of offset, but it just doesn’t show up
I’m pretty sure I need to find the mouse position relative to the GUI but I’m not sure.

Are there any solutions to this? Any help is appreciated!

(Also, sorry if anything is messed up! I just got access to the devforum yesterday and wanted to post this question as soon as I could! :sweat_smile: )

1 Like

local sprayPos = UDim2.new(0, mouse.Position.X, 0, mouse.Position.Y)

1 Like

Sadly this gives an error!
“Position is not a valid member of PlayerMouse Instance”

just set the mouse icon of the mouse to the gui wanted. Here is a post covering this: Mouse | Roblox Creator Documentation

also might want to have a closer look at the mouses properties on the wiki.

1 Like

I don’t think setting the image of the mouse will help much with surface guis, but I’ll look at the wiki again!

I’ve been looking at the wiki for a while and still dont seem to have a good solution to fix this. Any other tips?

It took me awhile to wrap my head around the solution, but here is what I came up with.

Full code
--belongs in starter character scripts
local tool = Instance.new("Tool")
tool.RequiresHandle = false
tool.Name = "Spraycan"
tool.Parent = game.Players.LocalPlayer.Backpack

local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()

local sprayTemplate = Instance.new("ImageLabel")
sprayTemplate.Name = "spray"
sprayTemplate.BackgroundTransparency = 1
sprayTemplate.Image = "http://www.roblox.com/asset/?id=77515058"
sprayTemplate.Size = UDim2.fromOffset(100,100)

--Enum.NormalId."Surface".Name
--direction, vectorDirection
local surfaceNormals = {
	Top = {1, "UpVector"},
	Bottom = {-1, "UpVector"},
	Right = {1, "RightVector"},
	Left = {-1, "RightVector"},
	Front = {1, "LookVector"},
	Back = {-1, "LookVector"}
}

function onActivation()
	if mouse.Hit then
		local mouseWorldPosition = mouse.Hit.Position
		local target = mouse.Target
		local surface = mouse.TargetSurface
		
		local surfaceGui: SurfaceGui
		for _, child in ipairs(target:GetChildren()) do
			if child:IsA("SurfaceGui") then
				if child.Face == surface then
					surfaceGui = child
					break
				end
			end
		end
		if not surfaceGui then
			surfaceGui = Instance.new("SurfaceGui")
			surfaceGui.Face = surface
			surfaceGui.Adornee = target
			surfaceGui.ClipsDescendants = true
			surfaceGui.SizingMode = Enum.SurfaceGuiSizingMode.PixelsPerStud
			surfaceGui.Parent = target
		end
		
		local surfaceCFrame = target.CFrame * CFrame.new(target.CFrame[surfaceNormals[surface.Name][2]] * surfaceNormals[surface.Name][1] * target.Size)
		local surfaceHit = surfaceCFrame:PointToObjectSpace(mouseWorldPosition)
		
		local spray = sprayTemplate:Clone()
		local sprayPosition = UDim2.fromScale(0.5,0.5) - UDim2.fromOffset(spray.AbsoluteSize.X/2, spray.AbsoluteSize.Y/2)
		
		--converting the surfaceHit vector3 to a UDim2, this bit is a pain
		local surfaceName = surface.Name
		local pixelsPerStud = surfaceGui.PixelsPerStud
		if surfaceName == "Top" then
			sprayPosition += UDim2.fromOffset(-surfaceHit.Z * pixelsPerStud, surfaceHit.X * pixelsPerStud)
		elseif surfaceName == "Bottom" then
			sprayPosition += UDim2.fromOffset(-surfaceHit.Z * pixelsPerStud, -surfaceHit.X * pixelsPerStud)
		elseif surfaceName == "Front" then
			sprayPosition += UDim2.fromOffset(-surfaceHit.X * pixelsPerStud, -surfaceHit.Y * pixelsPerStud)
		elseif surfaceName == "Back" then
			sprayPosition += UDim2.fromOffset(surfaceHit.X * pixelsPerStud, -surfaceHit.Y * pixelsPerStud)
		elseif surfaceName == "Right" then
			sprayPosition += UDim2.fromOffset(-surfaceHit.Z * pixelsPerStud, -surfaceHit.Y * pixelsPerStud)
		elseif surfaceName == "Left" then
			sprayPosition += UDim2.fromOffset(surfaceHit.Z * pixelsPerStud, -surfaceHit.Y * pixelsPerStud)
		end
		
		--rotate the image
		if surfaceName == "Top" or surfaceName == "Bottom" then -- face player
			local playerPos = player.Character.PrimaryPart.Position
			local direction = (Vector3.new(playerPos.X, mouseWorldPosition.Y, playerPos.Z) - mouseWorldPosition).Unit
			
			local rotation = math.deg(math.atan2(direction.Z, direction.X))
			if surfaceName == "Bottom" then
				rotation += 180
			end
			spray.Rotation = rotation
		else --upward rotation
			local up = Vector3.new(0,1,0)
			local targetUpVector = target.CFrame.UpVector
			
			local rotation = math.deg(math.acos(up:Dot(targetUpVector)))
			spray.Rotation = rotation
		end
		
		spray.Position = sprayPosition
		spray.Parent = surfaceGui
	end
end

tool.Activated:Connect(onActivation)

Spraycan.rbxl (30.8 KB)
Here is the game, find the code in the starter character scripts or above. Hope this helps. I am happy to answer any questions you may have aswell.

2 Likes

Wow thanks! I’ll check it out later today!

I just checked it out and it works amazingly!! I’m gonna edit the code a bit then use it! Thanks a ton! :smiley: