For quite a few projects I have worked on, I've found that I always need a way to detect if a player is clicking on a button or part from behind another part (Usually transparent and uncollidable.) I've never found a way to do this and have had to use work arounds. So my question is, what is the best way to see when a player clicks on a part even when there is another part beetwen the player and the part? If you need me to explain more I'm more then happy too.
You could achieve this by shooting a Ray from the camera to the mouse position. You would need to supply Ray.new() with a starting/origin position and direction.
We can get the direction by using a function called :ScreenPointToRay()
from the Camera object along with the mouse using :GetMouse()
.
local Players = game:GetService("Players") local player = Players.LocalPlayer local mouse = player:GetMouse() local pointRay = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y, 1)
pointRay
will be a unit vector of length 1 that is positioned to the camera's location in the world and faces the mouse's own position.
We can then create a new Ray and provide the Origin and Direction properties of pointRay
to this Ray.
local distance = 500 --How far we want to shoot the ray in studs local ray = Ray.new(pointRay.Origin, pointRay.Direction * distance) local hit, pos = workspace:FindPartOnRay(ray)
We now have a ray firing from the camera to the mouse with a magnitude of 500 studs. We could connect all this to create some kind of click detection for certain parts. I use UserInputService to do this.
local Players = game:GetService("Players") local uis = game:GetService("UserInputService") local player = Players.LocalPlayer local mouse = player:GetMouse() local partToIgnore = workspace:WaitForChild("IgnorePart") local targetedPart = workspace:WaitForChild("TargetPart") local distance = 500 local function onInput(inputObject, gameProccessed) if inputObject.UserInputType == Enum.UserInputType.MouseButton1 then local pointRay = workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y, 1) local ray = Ray.new(pointRay.Origin, pointRay.Direction * distance) local hit, pos = workspace:FindPartOnRay(ray, partToIgnore) if hit == targetedPart then hit.BrickColor = BrickColor.Random() end end end uis.InputBegan:Connect(onInput)
Here's a example gif linked to imgur.
For testing, I included a part that :FindPartOnRay
will ignore. I then compare what it returns through the hit variable to my target part and change it's BrickColor to something random. You can see more on UserInputService for InputBegan here.
Note that this is done in a local script so nothing is replicated to the server.
In this case, you would use Mouse.TargetFilter, which gets an instance to ignore (Only one, but it will ignore descendants).
Here’s how you would use it:
local player = game:GetService(“Players”).LocalPlayer local mouse = player:GetMouse() mouse.TargetFilter = workspace.PartInFront game:GetService(“RunService”).RenderStepped:Connect(function() print(mouse.Target) — PartBehind end)
(I’M ACTUALLY NOT SURE IF THIS WOULD WORK WITH CLICKDETECTORS PLEASE TRY)