UserInputService equivalent of Mouse.Hit

How do you get the mouse’s CFrame using UserInputService? Is there some sort of property I can access or do I have to create a variable that changes each time the mouse CFrame changes with the InputBegan event?

edit: yes I’m aware the mouse isn’t an actual 3D object and therefore doesn’t have a CFrame lol

5 Likes

A misconception I’d like to clear up first: the mouse doesn’t have a CFrame. Mouse.Hit describes a point in the 3D world where the mouse effectively “hits”. The Mouse is a 2D object. That being said, GetMouseLocation returns the location of the mouse in 2D space.

If you’re looking for an equivalent of getting where the mouse is pointing to in the world, you will need to use ViewportPointToRay as well as Mouse.UnitRay. This unit ray will need to be stretched as it only has a length magnitude of 1. You can then combine that with any ray function you need to accomplish a specific task, such as FindPartOnRay.

Here’s a thread regarding essentially the same question:

(Please search first!)

4 Likes

Mouse.Hit is technically a CFrame:


It stores the position of the mouse at wherever it hit and the orientation of the camera as its angle. It’s really an implementation detail though.

2 Likes

Mouse.Hit is a CFrame that allows you to get a point in the 3D world where the mouse is “hitting”. The Mouse itself is a 2D object and doesn’t have a third dimension. You could essentially think of it like ViewportPointToRay and Mouse.UnitRay but not done on the Lua side (whether that’s what’s actually happening or not).

2 Likes

If you want the script for the variable that changes each time the mouse moves here:

game:GetService("UserInputService.InputChanged:connect(function(object)
	local type=object.UserInputType.Name
	local pos=object.Position
	if type == "MouseMovement" then
		--stuff here
	elseif type == "MouseWheel" then
		--suff here
	end
end)

Does anyone know if the Mouse.Hit property works for mobile? That’s all I’m really worried about. I know the wiki page says “for the computer mouse” but I feel like I’ve tested it before on mobile and it worked.

edit: It does but it’s very inaccurate at times.

It’s a mouse tell me how it makes any sense on why it would work for mobiles besides a mobile that has their own mouse??

Sorry, this response is quite wordy. Looking at tons of words probably isn’t fun.

I mean, it might, but why would you want to use it on mobile? Mouse object should be kept strictly for computer uses. Mobile input, unlike a computer’s mouse, is not constant. Mobile input is dependent on when the user taps on the screen and even then, branches a bit.

In the case that you need a mobile input location, UserInputService.TouchTapInWorld. This is fired when the user taps in the world and not on a Gui, so it’s essentially equivalent to GetMouseLocation as in when the user taps their screen. This is not equivalent to Mouse.Hit though, there is no mobile equivalent for that.

Touch taps, much like the mouse, are done in 2D space. You then need to find a way to turn this 2D input into 3D space. Fortunately, ViewportPointToRay is there again. Between computer and mobile, the only difference you will experience is how you get the 2D position of the user’s input. After that, it’s relatively the same process for both. Get the unit ray, extend it, use a ray method.

Since TouchTapInWorld is an event, you’ll need to connect a function to it. It’ll pass two arguments; the Vector2 space where the user tapped and whether they tapped on a Gui. You’ll need to take the first argument, the Vector2 and feed it into ViewportPointToRay. Notice how it’s the same with GetMouseLocation, which also returns a Vector2 that you can put into that function.

local UserInputService = game:GetService("UserInputService")

UserInputService.TouchTapInWorld:Connect(function (position, processedByUI)
    -- Code
end)

ViewportPointToRay generates a unit ray with the properties you pass to it. You can then feed this back into another ray or extend the direction of it. There’s a code sample on the Wiki page itself under TouchTapInWorld, so you can look there and salvage the code as you need.

13 Likes

yayyyy it worked!

So basically I just created my own little mouse object, then every time the InputBegan event fired, used the SetCompMouseCFrame function to change the mouse object’s properties. Then I did pretty much the same for mobile but with a separate function.

Code
local Mouse = {
	Hit = CFrame.new(),
	Position = Vector2.new(),
	Target = nil
}


function SetCompMouseCFrame(Input, ProcessedByUI)
	if ProcessedByUI then return end
	if (Input.UserInputType ~= Enum.UserInputType.MouseButton1) then return end
	
	local Position = Input.Position
	local unitRay = Camera:ViewportPointToRay(Position.X, Position.Y+36)
	local ray = Ray.new(unitRay.Origin, unitRay.Direction * Length)
	local hitPart, hitPosition = game.Workspace:FindPartOnRay(ray)
	
	Mouse.Hit = CFrame.new(hitPosition)
	Mouse.Target = hitPart
	Mouse.Position = Vector2.new(Position.X, Position.Y)
end


UIService.InputBegan:Connect(SetCompMouseCFrame)
10 Likes

Oh, that’s actually a smart catch-all function. I admittedly didn’t think of constructing a function that way to essentially handle both inputs at one time. I completely forgot that you can use InputObject.Position which is a Vector2 in itself. :flushed:

One thing - unless InputObject.Position accounts for the Gui inset (36 studs), you don’t need to add anything to the Y. If it ever came to needing to account for the Gui inset, you’d have to take away 36 from the Y, not add. The Gui inset takes up 36 pixels down, so naturally you can compensate for that by going 36 pixels up.

3 Likes

Yeah I tested it without the +36 and all of the parts I created at the position were all off. :laughing:

Always feel free to play around with the values. I don’t think you need to change the Y at all since ViewportPointToRay doesn’t take the Gui inset into account. You can try running it with a raw Y and see if that achieves your desired effect. If not, subtract 36 from the Y and try again.

1 Like

Is there a way to do this without using userinputservice? Like on a loop basically, just firing the ray straight down the center of the camera.

Yes, you can use the same principle but just via RunService instead of UserInputService - you can pretty much just hotswap the event, the only thing you wouldn’t be able to take advantage of via RunService is the InputObject that UserInputService events pass, though GetMouseLocation stands in for that if any coordinates from the mouse are required.