On the way to perfect look at fn

So thanks this post and some more research on the topic I can do pretty nice look-at stuff (like a surveillance camera orsmth.). Anyway all of solutions screw up one angle that should not rotate at all. Here is the code:

local UNIT_X = Vector3.new(1, 0, 0)
local UNIT_Z = Vector3.new(0, 0, 1)

local eyePart = workspace.Eye
local goalPart = workspace.Goal

-- Try 1
local function fromTo(v1, v2)
	local a = Vector3.new()
	
	local xAxis = Vector3.new(1,0,0)
	local yAxis = Vector3.new(0,1,0)
	
	local dot = v1:Dot(v2)	
	
	if (dot < -0.999999) then
		a = xAxis:Cross(v1)		
		if (a.Magnitude < 0.000001) then
			a = yAxis:Cross(v1)	
		end
		a = a.Unit
		return CFrame.fromAxisAngle(a, math.pi)
	elseif (dot > 0.999999) then
		return CFrame.new()
	else
		a = v1:Cross(v2)	
		return CFrame.new(0,0,0, a.X, a.Y, a.Z, 1 + dot)
	end
end

-- Example 1
local function getRotationBetween(u, v, axis)
    local dot = u:Dot(v)
    if (dot > 0.99999) then
        -- situation 1
        return CFrame.new()
    elseif (dot < -0.99999) then
        -- situation 2
        return CFrame.fromAxisAngle(axis, math.pi)
    end
    -- situation 3
    return CFrame.fromAxisAngle(u:Cross(v), math.acos(dot))
end

game:GetService("RunService").Heartbeat:Connect(function(dt)
	local eyeCF = eyePart.CFrame
	
	--local delta = getRotationBetween(-UNIT_Z, eyeCF:PointToObjectSpace(goalPart.Position).Unit, UNIT_X)	
	--eyePart.CFrame = eyeCF * delta
	
	eyePart.CFrame = eyeCF * fromTo(Vector3.new(1,0,0), eyeCF:PointToObjectSpace(goalPart.Position).Unit);
end)

And here is the problem:

Any ideas math geniuses? :slight_smile:

You’ve made multiple posts about this but I’m still confused as to what you actually want. Can you show an image of what you want the final product to look like?

From your post it sounds like you want it to be a surveillance camera, if so, it should be rotating on a point towards the goal - that should stop it from rotating undesirably.

Will slow reply as I have work to do, but I’ll be back to help at some point. Please provide as much info as possible, incl. preferably an image of how it should work

1 Like

Eye part should look at the Goal just like now (in provided code - mine or yours or whatever), but without doing rolling (just pitch and yaw) as: a) it makes no sense roll, b) roll just makes the Eye object look awkward (imagine a frog looking at a fly).

@Isocortex so, have u any ideas how to solve this Mystery? :))))

Do you know a lot about CFrames and the included math?

Keep in mind a CFrame is a 4x4 matrix.

Understanding CFrame further will help a lot more.

Its actually a 4x4 matrix. I recommend you read this

:joy: aw man I knew I got it wrong.
Thanks for that! I’ll edit my reply.

I don’t think this is about not understanding CFrame and matrix. But more lack on the correct technique to achieve what I want. So what you just suggested isn’t helpful and could come from anybody :frowning: why want’s to play Smart. Sorry.

What i’m thinking you need to do is eliminate movement from the roll axis; I suggest you start researching ways on doing this

Sure. I just can’t figure it out :frowning:

Without doing anything complicated, couldn’t you just achieve this by setting two arguments within the CFrame value?

local eyePart = workspace.Eye
local goalPart = workspace.Goal

game:GetService("RunService").Heartbeat:Connect(function()
	eyePart.CFrame = CFrame.new(eyePart.Position, goalPart.Position)
end)

Check this post Any way to prevent fliping with look at CFrame? . That what I tried at the beginning.

On that post, saw you had this code:

local UNIT_X = Vector3.new(1, 0, 0)
local UNIT_Z = Vector3.new(0, 0, 1)

local eyePart = workspace.EyePart
local goalPart = workspace.GoalPart

eyePart.CFrame = CFrame.new(eyePart.Position, goalPart.Position)

game:GetService("RunService").Heartbeat:Connect(function(dt)
	local eyeCF = eyePart.CFrame
	local delta = getRotationBetween(-UNIT_Z, eyeCF:PointToObjectSpace(goalPart.Position).Unit, UNIT_X)
	
	eyePart.CFrame = eyeCF * delta
end)

change it to:

local UNIT_X = Vector3.new(1, 0, 0)
local UNIT_Z = Vector3.new(0, 0, 1)

local eyePart = workspace.EyePart
local goalPart = workspace.GoalPart

eyePart.CFrame = CFrame.new(eyePart.Position, goalPart.Position)
local eyeCF = eyePart.CFrame

game:GetService("RunService").Heartbeat:Connect(function(dt)
	local delta = getRotationBetween(-UNIT_Z, eyeCF:PointToObjectSpace(goalPart.Position).Unit, UNIT_X)
	
	eyePart.CFrame = eyeCF * delta
end)

Not sure if that would help, it’s untested.

I’m kind of leaving this topic, as I realized that I do not really know what I want - how the look-at should behave in different scenarious. Will open new post when I have clear vision in my mind.

Im bumping this topic since it’s quite essential lost people know this, I’ve met loads with the issue and I’ve had it for years before I understood how CFrames worked, I also can’t find another post with the solution at all so I believe I’m justified?

Long waffle explanation

Sorry to bump, but this is in case anybody else was suffering from CFrame.lookAt’s tendency to smooth out rotation up to the TopVector (0,1,0), you can use either AxisAngles and ignoring theta (which are giving me some wild results and not even returning a value!??!) or EulerAngles by ignoring the Z axis.

To explain this:
EulerAngles can work in the order of Y, X, Z (they usually also X, Y, Z but that’s ew) (look this up for an image/explanation) where each axis is based off of the previous one so that rotating along Y (spinning around by Yaw) will mean that X will pull the eye up and down from that rotation (Pitch!), we ignore Z since that’s kind of something to do with your issue where the eye is rolling itself.

So we can convert the CFrame.lookAt() return value into angles with :ToEulerAngleYXZ (note YXZ not XYZ) and steal only the X and Y values, turning it back into a CFrame. There is a better way of doing this I’m sure but I forgot since that was about a year ago.

In conclusion the solution was actually really obvious, OP said: I don’t want roll… Solution: Don’t use roll with maths… Although OP was inexplicit in explaining this goal the video showed an unnatural result of CFrame.lookAt(arg1.Position,arg2.Position,arg1.UpVector)

Simple Solution:

local weirdLook = CFrame.lookAt(eyePart.Position,goalPart.Position,eyePart.UpVector)
local x,y,z = weirdLook:ToEulerAnglesYXZ()
local normalLook = CFrame.fromEulerAnglesYXZ(x,y,0)
eyePart.CFrame = normalLook + eyePart.CFrame.Position

I know you probably moved on now trshmanx but good luck with your endeavours :smiley:

1 Like