Create a CFrame facing towards mouse position (without Mouse.Hit)

Example of my problem:

As you can see, Mouse.Hit wouldn’t work for the job as it will take any part it hits into account. I’m trying to get the arrow to be facing exactly from where the mouse position is in relation to its current position.

I’m currently just using:

local l = Mouse.Hit.p; l = Vector3.new(l.X,asPart.CFrame.p.Y,l.Z)
local cf = CFrame.new(asPart.CFrame.p,l)
4 Likes

Try using the lookat constructor on the mouse origin. That should work.

Edit: Actually, I doubt it will. But you can try it.

1 Like

I don’t know how I’d use it. If you mean replacing Mouse.Hit.P with Mouse.Origin.P then no it doesn’t do anything, it just stays in one position.

Yeah, that’s what I meant. I doubt either of those would work. You could also try getting the difference between mouse.Hit.p and the part by using the lookVector to multiply it by the difference.

Here’s what I usually use for getting the mouse position:

local coll = game:GetService('CollectionService')
local function getMousePosition(mouse)
	local ray = Ray.new(mouse.Origin.p,mouse.UnitRay.Direction*2048)
	local ignore = coll:GetTagged('TargetIgnore')
	ignore[#ignore+1] = plr.Character
	local _,pos = workspace:FindPartOnRayWithIgnoreList(ray,ignore)
	return pos
end

I add the CollectionService tag “TargetIgnore” to any parts I want the mouse position to ignore.

With this, you could turn the part to face the mouse with asPart.CFrame = CFrame.new(asPart.Position,getMousePosition(mouse))

@RVVZ Try checking what parts might collide with the mouse by printing the mouse’s target

print(mouse.Target:GetFullName())

When I make the Raycast an empty whitelist, and reduce the length of the ray it works. However it feels like it should be simpler than this, and I’m unsure if this would affect performance or not running in a renderstepped, I don’t think it would though.

It is still slightly glitchy though, and it seems that I have to move the mouse around the arrow to set its direction accurately.

Hmm, so this is a tricky one.

We can’t use mouse.Hit for the reasons you have discussed and we can’t use the 2D space either b/c then we have issues with our camera’s pitch causing issues.

The way I’d approach this is with a plane intersection. E.g. finding where a ray intersects with an infinite surface.

We can define a plane with a point on the plane and the surface normal of the plane. In your case the point will be arrow.Position and the normal will be Vector3.new(0, 1, 0).

As for the ray we need the same information we’d need for a raycast (a starting position and a direction). Luckily the camera has a method that will provide that for us via :ScreenPointToRay(x, y, depth).

Now onto the math:

Pardon my poor writing/drawing

Ray Point = rp
Ray Direction = rd
Plane Point = pp
Plane Normal = pn

We have two equations we will work with

Intersection = rp + rd * some_scalar
(Intersection - pp):Dot(pn) = 0

Now we plug the first into the second and solve for some_scalar

(rp + rd * some_scalar - pp):Dot(pn) = 0
rp:Dot(pn) + rd:Dot(pn) * some_scalar - pp:Dot(pn) = 0
some_scalar = (pp - rp):Dot(pn) / rd:Dot(pn)

So then our function would be:

local function planeRay(rp, rd, pp, pn)
	local scalar = (pp - rp):Dot(pn) / rd:Dot(pn)
	return rp + rd * scalar
end

-- your code might then be:
local unitRay = game.Workspace.CurrentCamera:ScreenPointToRay(mouse.X, mouse.Y)
local intersection = planeRay(unitRay.Origin, unitRay.Direction, arrow.Position, Vector3.new(0, 1, 0))

Now some things to take note of, when rd and pn are orthogonal this function will give a nan vector because the ray direction runs across the surface of the plane which either means infinite intersections or none at all. Just something to keep in mind…

Hope that helps!

Edit: Here’s a quick place file in action.

planeray.rbxl (17.9 KB)

Edit 2: I suppose I should mention from a math standpoint we are making use of the fact that the dot product is equivalent to a:Dot(b) = a.Magnitude * b.Magnitude * cos(shortest_angle_between_a_and_b). As such since cos(90 degrees) = 0 we can use the dot product to make one of our equations.

16 Likes

This works great! I never thought of it like this, very well explained. Thanks! :slight_smile:

1 Like