CFrames have a LookVector property, which is a unit vector ((lookAt-pos).Unit, a vector with the same direction but a magnitude of 1). If you’re trying to find the lookAt Vector3 used to create the CFrame with that constructor, you need its distance from pos (not stored in CFrame).
local a = CFrame.new(pos, lookAt)
local v = a.Position + a.LookVector * distance -- assuming distance is known
If the distance is unknown, raycasting might be another option.
Another option is to just create your own method for setting the CFrame of something with lookAt and store that value in a table.
Simple example:
local list = {};
function lookAt(a, b)
list[a] = b.Position;
a.CFrame = CFrame.new(a.Position, b.Position);
end
function getLookAt(a)
return list[a];
end
local selection = game.Selection:Get();
local a,b = selection[1], selection[2];
lookAt(a, b);
print(getLookAt(a));
This is command line code (hence getting the selection) that assumes you’re inputting two parts and would have to be modified to suit your needs better. It’d also be best in the form of a module so that it can be accessed by multiple scripts.
That… is one hell of a hack. I’m out of words to describe what my brain cells are reading.
…except it falls apart as soon as a’s CFrame stops looking at b or getLookAt is called with something not in list and the caller doesn’t handle nil correctly.
please do explain why anyone would use this in practice
It’s an example command line code that is based on assumption and has no type checking. If you want to handle cases of something being nil or just flat out not being a part, then you’d obviously have to modify the code to suit your needs better. These are simple issues and can be addressed by asserting the types of a and b.
If the object is not purely being set to look at something 100% of the time then it obviously falls apart because you’re setting the cframe outside of using this method, but I suppose you could just listen for the Orientation to change with :GetPropertyChangedSignal(“Orientation”) and check if the position matches the one stored in the list. If not, then remove it from the list.
Edit: Added an example.
LookAt Module
local lookAtModule = {};
lookAtModule.partList = {};
function lookAtModule:LookAt(a, b)
assert(typeof(a)=="Instance" and a:IsA("Part"), "Object a must be a part.");
assert(typeof(b)=="Instance" and b:IsA("Part"), "Object b must be a part.");
a.CFrame = CFrame.new(a.Position, b.Position);
if not self.partList[a] then
local orientationSignal;
orientationSignal = a:GetPropertyChangedSignal("Orientation"):Connect(function()
self.partList[a] = nil;
orientationSignal:Disconnect();
end)
end
self.partList[a] = b.Position;
print(a, "is looking at", b);
end
function lookAtModule:getLookAt(a)
return self.partList[a]; -- doesn't really matter if this is nil here, won't break the code.
end
function lookAtModule:isLookingAt(a, b)
assert(typeof(a)=="Instance" and a:IsA("Part"), "Object a must be a part.");
assert(typeof(b)=="Instance" and b:IsA("Part"), "Object b must be a part.");
return self.partList[a]==b.Position;
end
return lookAtModule;
Example Use
local lookAtModule = require(script.lookAtModule);
local a, b = workspace:WaitForChild("a"), workspace:WaitForChild("b");
lookAtModule:LookAt(a, b);
print(lookAtModule:isLookingAt(a, b)); -- true
wait(1);
a.CFrame = CFrame.new(0, 0, 0);
print(lookAtModule:isLookingAt(a, b)); -- false
This is all assuming that the OP wants the exact position rather than just the LookVector, which it appears they do not.