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.
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.
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.
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.
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)
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.
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.