ViewportFrame; Finding mouse position

How to find mouse position in ViewportFrame?
How to raycast using WorldModel in ViewportFrame?


I want to detect, if player mouse is entering one of this bricks, for example, but how?

1 Like

Use the read only Mouse.x and Mouse.y

How to transform it to 3d World in ViewportFrame?

Actually you can use raycast.

Belive it or not if you put a WorldModel inside a viewport frame you can do some physics related operations.

https://developer.roblox.com/en-us/api-reference/class/WorldModel

Much of the documentation is on there. I haven’t really got any reason to use it but have fun with it ig.

but how? I need to get direction for using raycast, how?

Its basically the same way as how you would raycast with a normal pistol except instead of using the normal CurrentCamera use the Camera for the viewport frame. Also, instead of doing workspace:Raycast(args) you will be doing ViewportFrame.WorldModel:Raycast(args) instead.

If you haven’t worked with guns before you have to use the Camera:ScreenPointToRay() function and insert the mouse position as the arguments and then use the output ray to raycast.

local MouseLocation = UserInputService:GetMouseLocation()
local ViewportRay = Camera:ScreenPointToRay(MouseLocation.X, MouseLocation.Y) -- Camera is the ViewportFrame's camera.
local Result = ViewportFrame.WorldModel:Raycast(ViewportRay.Origin, ViewportRay.Direction * Length)

https://developer.roblox.com/en-us/api-reference/function/Camera/ScreenPointToRay
https://developer.roblox.com/en-us/api-reference/class/UserInputService
https://developer.roblox.com/en-us/api-reference/function/WorldRoot/Raycast

It’s possible that it might not work because I haven’t tested it and I don’t exactly know is ScreenPointToRay accounts for the offset that ViewportFrames have but it’s worth a shot.

Yeah, I know about this, and tested so, but… it doesn’t seem to work

Probably raycasts are not firing correctly. Could I see your code if possible.


-- Debug
local raycastView = Instance.new("Part")
raycastView.Anchored = true
raycastView.CanCollide = false
raycastView.CanQuery = false
raycastView.CanTouch = false
raycastView.CastShadow = false
local function visRay(startPosition,direction,colour)
	local endPosition = startPosition + direction
	local midpoint = (startPosition + endPosition) /2
	local ray = raycastView:Clone()
	ray.Parent = workspace
	ray.Size = Vector3.new(0.2,0.2,direction.Magnitude)
	ray.CFrame = CFrame.lookAt(midpoint,startPosition)
	Debris:AddItem(ray,0.1)
	--Ignore this if you do not care about the colour :)
	if colour == "red" then
		ray.BrickColor =BrickColor.new("Really red")
	elseif colour == "green" then
		ray.BrickColor =BrickColor.new("Bright green")
	elseif colour == "blue" then
		ray.BrickColor = BrickColor.new("Bright blue")
	elseif colour == "violet" then
		ray.BrickColor = BrickColor.new("Bright violet")
	end
	ray.Material = "Neon"
end

^This piece of code is a raycast visualizer. Make sure to change the parent to the worldmodel and then you should in theory be able to test and see what is happening.

I’m trying to figure out the exact same thing. I already made a post about my issue but it didn’t get any replies. My code is almost identical to your code but for some reason the ray’s direction is completely off. I’ve been stuck on this issue for days and have came up with no solution at all.

Hmm. I actually came up with a reason that might be the reason why the frames are off.

We might have to get the MousePosition relative to the frame and not the screen. It could be that Camera:ScreenPointToRay() only works for screens and we have to offset it ourselves for it to line up.

I am not entirely sure if it is the problem and I am not even sure if this will even work. But you could try simply negating the position of the frame.

local MouseLocation = UserInputService:GetMouseLocation()
local ViewportMouseLocation = MouseLocation - ViewportFrame.AbsolutePosition -- Idrk if this accounts for anchor points.

I will see if I can find a solution for you. Also, I didn’t see your post.

(Maybe you can experiment by setting the size of the Viewport frame to match the screen size.)

(Maybe you can experiment by setting the size of the Viewport frame to match the screen size.) <

I thought of the exact same thing and I don’t think that’s the issue. I scaled the ViewportFrame to be the size of the screen and I get the exact same result. What I find really weird about this is the fact that the ray is pointing in the complete wrong direction as I showed on my post:

The ray is shooting in a completely different direction than what would be expected.

CurrentCamera’s lookvector: -0, -0, -1
DrawUnitRay.Direction: 0.90532231330872, -0.42472475767136, -0.00064873765222728

1 Like

@koziahss @Master_Aaron

I have also had no success with trying to use Camera:ScreenPointToRay/ViewportPointToRay on Cameras in ViewportFrames; it probably warrants a bug report/feature request at some point since you’d expect it to work normally, but…

In the meantime, the two functions below should work for raycasting from a ViewportFrame’s CurrentCamera (although I haven’t tested them extensively):

local function RaycastInViewportFrame(viewportFrame, x, y, raycastDistance, raycastParams)
	local camera = viewportFrame.CurrentCamera
	local worldModel = viewportFrame:FindFirstChildWhichIsA("WorldModel")
	local mousePosition = Vector2.new(x,y) - viewportFrame.AbsolutePosition -- account for viewportframe offset
	local relativePosition = ((mousePosition - (viewportFrame.AbsoluteSize/2)) * Vector2.new(1, -1))/(viewportFrame.AbsoluteSize/2) -- get the relative position of the click with center of viewportFrame as origin: -1 is left/bottom and 1 is right/top for X and Y respectively
	local projectedY = math.tan(math.rad(camera.FieldOfView)/2)*raycastDistance -- the projected height of a 2D frame raycastDistance studs from the camera with same aspect ratio
	local projectedX = projectedY * (viewportFrame.AbsoluteSize.X/viewportFrame.AbsoluteSize.Y) -- projected width from aspect ratio
	local projectedPosition = Vector2.new(projectedX, projectedY) * relativePosition -- the projected position of the input on the similar frame
	local worldPosition = (camera.CFrame * CFrame.new(projectedPosition.X, projectedPosition.Y, -raycastDistance)).Position -- the 3d position of said projected position
	return worldModel:Raycast(camera.CFrame.Position, (worldPosition - camera.CFrame.Position).Unit * raycastDistance, raycastParams)
end

local function ViewportFramePointToRay(viewportFrame, x, y, depth) -- similar to Camera:ScreenPointToRay
	depth = depth or 0
	local camera = viewportFrame.CurrentCamera
	local mousePosition = Vector2.new(x,y) - viewportFrame.AbsolutePosition -- account for viewportframe offset
	local relativePosition = ((mousePosition - (viewportFrame.AbsoluteSize/2)) * Vector2.new(1, -1))/(viewportFrame.AbsoluteSize/2) -- get the relative position of the click with center of viewportFrame as origin: -1 is left/bottom and 1 is right/top for X and Y respectively
	local projectedY = math.tan(math.rad(camera.FieldOfView)/2) * (1 + depth) -- the projected height of a 2D frame 1 + depth studs from the camera with same aspect ratio
	local projectedX = projectedY * (viewportFrame.AbsoluteSize.X/viewportFrame.AbsoluteSize.Y) -- projected width from aspect ratio
	local projectedPosition = Vector2.new(projectedX, projectedY) * relativePosition -- the projected position of the input on the similar frame
	local worldPosition = (camera.CFrame * CFrame.new(projectedPosition.X, projectedPosition.Y, -(1 + depth))).Position -- the 3d position of said projected position
	local rayOrigin = (CFrame.new(camera.CFrame.Position, worldPosition) * CFrame.new(0, 0, -depth)).Position
	local rayDirection = worldPosition - rayOrigin
	return Ray.new(rayOrigin, rayDirection)
end

yourViewportFrame.InputBegan:Connect(function(input)
	if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
	print(RaycastInViewportFrame(yourViewportFrame, input.Position.X, input.Position.Y, yourRaycastDistance, yourRaycastParams))
	local ray = ViewportFramePointToRay(yourViewportFrame, input.Position.X, input.Position.Y)
	print(yourWorldModel:FindPartOnRay(Ray.new(ray.Origin, ray.Direction*500)))
end)

A sample place is provided below:
viewport_raycast.rbxl (37.1 KB)

An alternative method for converting the 2D screen position to a 3D world position without :ScreenPointToRay can also be found here.

8 Likes

The ViewportPointToRay of Roblox Studio still does not work

Sorry for the bump