I need help making a part face another part only on a certain axis

I am trying to make a compass that constantly faces the nearest part in a folder ive designated. Ive got it working mostly but there is a problem i am unable to think on how to solve since my knowledge with CFrames and Vector math is not great. Here is a video that shows the problem:

https://gyazo.com/7a75ad8892ab8f5fed2c3fa46ed4c6a5

As you can see it faces the part but it sticks out the compass which is not what i want. Here is my code:

local tool = script.Parent.Parent
local arrow = script.Parent.Arrow
local center = tool.Handle.Loc --a part welded to the center of the compass where the arrow should be
local RS = game:GetService("RunService")
local connection

tool.Equipped:Connect(function()
	local keypack = game.Workspace:FindFirstChild("KeyPack",true) --a model containing all the objects the compass will lock onto
	if connection then connection:Disconnect() end
	
	connection = RS.Heartbeat:Connect(function()
		local nearestObj
		local dist 
		for _, v in pairs(keypack:GetChildren()) do
			local newdist = (v.Position - arrow.Position).Magnitude
			if not dist or newdist < dist then
				nearestObj = v
				dist = newdist
			end
		end
		arrow.WeldConstraint.Enabled = false
		arrow.CFrame = CFrame.new(center.Position, nearestObj.Position) * CFrame.Angles(math.rad(90),math.rad(90),math.rad(-90))
		arrow.WeldConstraint.Enabled = true
	end)
		
end)

tool.Unequipped:Connect(function()
	if connection then connection:Disconnect() end
end)
2 Likes

After some thought im assuming the best way to do this is to not make the part face the nearestObj.Position, but instead angle the part on the axis so that is faces the object. Question is, how would i find that angle beteween the nearest object and the arrow?

Here’s one way:

        local forwardTemp = (nearestObj.Position - center.Position).Unit -- edit: forgot .Unit
        local up = center.CFrame.UpVector
        local right = forwardTemp:Cross(up)
        arrow.CFrame = CFrame.fromMatrix(center.Position, right, up)

You may need to shuffle right/up around or rotate by 90 degrees depending on your setup.

EDIT: or if you just want the heading (angle), you can do:

local offset = nearestObj.Position - center.Position
local heading = math.atan2(-offset.Z, offset.X)

again, might be off by 90 degrees or whatever

8 Likes

Not sure if this is the best way. But it should work.

CFrame.new(Vector3.new(center.Position.X,0,center.Positon.Z),Vector3.new(nearestObj.Position.X,0,nearestObj.Position.Z))

Basically cutting out the Y axis, so that it doesn’t rotate vertically. Which seems to be the issue.

1 Like

Best and easiest way its using simple math.

arrow.CFrame = CFrame.new(center.Position, nearestObj.Position * Vector3.new(1,0,1))
We just setting to nil Y axis so it wont goes up or down

I tried you solution with the CFrame.fromMatrix and got it to work after some small adjustments. Thanks

1 Like

This and @Kitty4president’s solutions both work only if you can guarantee that the compass will never tilt.

Thanks nicemike40 30chararacters

Hello, I’m trying to accomplish basically the opposite of what this post is talking about. I have a lever that I only want to move up and down on the Y axis of the lever. I’ve tried adapting your solution myself but haven’t had much luck so far. Any way you could help with what I’m missing?