Detect if mouse is in a certain area of a viewportframe

G’day, I’m trynna work out how one would interact with certain parts within a viewportframe, or if there is a better way of handling this.

So the concept I’m attempting to achieve is detecting if a player’s mouse is hovered/clicked on a certain area of a viewport, as I’m using a mesh for a radio and I’m attempting to use each individual buttons, an example will be posted below.

https://gyazo.com/b0bcff99bcd6b218471f25360de40720

Each of the transparent parts roughly represent the area the mouse has to be in before a tooltip or information tip is displayed and then if the user clicks on the same area, they are then able to open a separate UI (which I can handle on my own)

The UI also must remain draggable so absoluteposition would be out of the question, taking any requests at this point.

Cheers,
xg

1 Like

You need to get all the corners of the Mouse Gui, then check if all the corners are inside the Gui you want and it should work.

local MouseTopLeftCorner
local MouseTopRightCorner
local MouseDownLeftCorner
local MouseDownRightCorner

local FrameTopLeftCorner
local FrameTopRightCorner
local FrameDownLeftCorner
local FrameDownRightCorner
local Mouse = game.Players.LocalPlayer:GetMouse()

Mouse.ButtonDown:Connect(function()
if MouseTopLeftCorner > FrameTopLeftCorner and MouseTopRightCorner < FrameTopRightCorner and MouseDownLeftCorner > FrameDownLeftCorner and MouseDownRightCorner < FrameDownRightCorner then
    print("Is Inside")
end
end)

I’d have a transparent gui come over and just locate buttons over each one. Then you can just have the hover thing trigger what ever code you want. Like maybe a wait 1 before showing a tooltip.

MouseEnter and MouseLeave. They can be used for text buttons or image buttons.

2 Likes

it would be so easy then I would have him out the same way, but it’s not. He wants it to work, and an event within an event is never good. So with my method it fires the event first, then controls if it’s inside and if yes, it does what he wants.

Yeah I thought it was all Gui stuff. Seems simple to me in that sense. For any Guis that need to stay stationary relative to a moving object, like and NPC or a moving sign ect, I use billboard Guis or Surface Guis with buttons. If it’s always in the same place on screen, I use Screen Guis.

If the buttons are actual parts, you can always put surface guis and make then actual buttons. Then the MouseEnter can trigger a Bilboard Gui with an Image and text tooltip.

But again I don’t know the whole story so I’m probably way off.

Just thought it was a simple tooltip come up over a button is all. A button that could be pressed and do something. I’m still learning about how to control who sees the guis though so still a bit of a newb here.

1 Like

I would suggest using raycasting for this, (there are diffentally other methods), there is a WorldModel object releasing a some point that would hopefully allow for an easier “built in” ability to raycast in veiwports, but for now @Onogork created a Module to do this.

@Jaycbee05
Raycasting isn’t a cheap method and should be avoided if possible, which is entirely the case here.

local serviceInput = game:GetService("UserInputService")
local guiViewport = script.Parent

--[[The table ocontaining the buttons and their positions.
	Note that these vectors represent their centers, not their top left corners.
	]]
local listButtonPos = {
	Button1 = Vector2.new(0.2,0.2),
	Button2 = Vector2.new(0.5,0.2),
	Button3 = Vector2.new(0.8,0.2)
}

--Reveals button positions for debugging purpose
for i,v in next, listButtonPos do
	local frame = Instance.new("Frame")
	frame.Size = UDim2.new(0,10,0,10)
	frame.Position = UDim2.new(v.X,-5,v.Y,-5)
	frame.Parent = guiViewport
end

--This part handles the scaling and dragging part you mentioned
local posCorner0
local posCorner1

local function UpdateScale()
	local pos,size = guiViewport.AbsolutePosition,guiViewport.AbsoluteSize
	posCorner0 = pos
	posCorner1 = pos+size
end
guiViewport:GetPropertyChangedSignal("Size"):Connect(UpdateScale)
guiViewport:GetPropertyChangedSignal("Position"):Connect(UpdateScale)
UpdateScale()

--The actual input
serviceInput.InputBegan:Connect(function(inp,gpe)
	local timeDown = tick()
	if not gpe and inp.UserInputType==Enum.UserInputType.MouseButton1 then
		
		local con; con=serviceInput.InputEnded:Connect(function(inp)
			if inp.UserInputType==Enum.UserInputType.MouseButton1 then
				--[[
					A click is registered when a mouse button goes up and down within a set interval,
					which is 0.2 of a second here.
					This is also why there is an InputEnded connection inside the InputBegan function.
					]]
				if tick()-timeDown<0.2 then
					local posMouse = inp.Position
					--Check if mouse is above the frame
					if posMouse.X>=posCorner0.X and posMouse.X<=posCorner1.X and posMouse.Y>=posCorner0.Y and posMouse.Y<=posCorner1.Y then
						local closest,closestdist = nil,math.huge
						for name,pos in next, listButtonPos do
							pos = posCorner0+pos*guiViewport.AbsoluteSize
							local dist = math.abs(pos.X-posMouse.X)+math.abs(pos.Y-posMouse.Y)
							--[[
								Find the closest button which is also within a set range(10).
								I used a taxi cab distance instead of a magnitude because
								it doesn't affect the result much.
								]]
							if dist<closestdist and dist<10 then
								closest,closestdist = name,dist
							end
						end
						if closest then
							print(closest)
							print(closestdist)
						else
							print("No buttons within range")
						end
					end
				end
				con:Disconnect()
				
			end
		end)
		
	end
end)
1 Like

Depends on how you’re using raycasts. Short raycasts are fairly cheap and you can fire off hundreds of them per frame.

I agree, but casting rays with ViewportFrames adds much more complexity to it rather than just having to create a new ray and call a method.

EDIT: Even though you could probably use an anchored transparent and non-collidable model offset under the map for this, but that would still be more complex than just checking click positions.

1 Like

I believe simply using this for finding the position of those buttons on the “screen” and then applying them to the buttons should work. Of course, I don’t know how to convert Vector2 to UDim2.

1 Like

Why you not try by using a ClickDetector?
A ClickDetector maybe work.

Not in ViewportFrames, ClickDetectors only work in Workspace.

1 Like