I can’t seem to figure out how to make a part face toward another part using CFrame.Angles (or CFrame.fromOrientation).
I am aware of the way to do it using CFrame.new(PartA.Position,PartB.Position), but I would like to know how to use it with CFrame.Angles for future use. Any help is appreciated.
Why do you need to use angles? CFrame.new(pos, look) is as simple as it gets. Reinventing the wheel shouldn’t be waht you want to do.
local part0 = (whatever)
local part1 = (whatever)
part0.CFrame = CFrame.new(part0.Position, part1.Position)
EDIT: Not what OP was looking for.
“I am aware of the way to do it using CFrame.new(PartA.Position,PartB.Position), but I would like to know how to use it with CFrame.Angles for future use. Any help is appreciated.”
In order to use math.clamp to limit the rotation on a certain access (unless there’s a better way to do it)
My mistake, what future use would you need for that solution to not fit the bill?
@incapaxx the OP already understands how the CFrame.new(a,b) function works. They want to understand how to solve for this using trigonometry (for learning purposes probably.)
I’m not great at math, and this is a problem I’ve been trying to solve myself
here’s a good resource that gives you some steps to what your looking for, It uses cosines to get the angle.
I would do the above steps to find the angle between two vectors, then rotate the part by those offsets.
The other constructors are often much more helpful than Angles
will ever be, e.g. CFrame.fromAxisAngles
can use a cross product and dot product from a normal to figure out rotations, though EgoMoose knows more about this than I do, so here is his take on it I guess:
The simplest answer I can give is taking an already created CFrame and converting it to angles using CFrame:toEulerAnglesXYZ
, which you can plug into CFrame.Angles
. At that point though, you could just use CFrame.lookAt(a, b)
to get it to work just the same if you started from there.
Maybe a more complex answer could be using the 4 vector constructor, CFrame.new(pos, right, up, back)
, and forming each vector yourself, but that would be the exact same as CFrame.lookAt(a, b)
in the end, so it’s not all that useful.
On to the solution I came up with:
If we were to break this down, in theory, CFrame.Angles(0, y, 0) * CFrame.Angles(x, 0, 0)
can recreate any CFrame that CFrame.lookAt(Vector3.new(), b)
can, we would just have to figure out x
and y
from the vector b
. This can probably be done from the components alone and trigonometric functions as long as we’re careful.
Firstly, we can solve for y
by using math.atan2
between the X and Z components since they would be the legs of the right triangle made by X and Z:
-- Y / X
y = math.atan2(b.X, b.Z)
Don’t confuse math.atan2
with math.atan
, as they will not have the same behavior!
You will need to test this out because I can’t and probably won’t. You will probably want to tweak the math.atan2
formula by switching around or negating b.X
and b.Z
until the correct behavior is displayed. Make sure to do this in a fun testing environment I guess.
For x
, we can probably be sneaky and use just b.Y
along with math.asin
to create the angle, since the part will always be facing the right direction between -pi/2
and pi/2
degrees and Y would logically be the upright leg of the right trangle, with R being the magnitude of b
:
-- Y / R
x = math.asin(b.Y/b.Magnitude)
-- equivalent to:
x = math.asin(b.Unit.Y)
This will also need to be tweaked to do what you need, you will probably only need to negate b.Y
or math.asin
, they will both do the same thing anyway due to sine being an odd function.
After this, you can use 2 CFrame.Angles
constructors to achieve what you’re looking for, as briefly described near the top of this reply:
CFrame.Angles(0, y, 0) * CFrame.Angles(x, 0, 0)
In theory, this can also be applied in 1 fell swoop using CFrame.fromEulerAnglesXYZ(x, y, 0)
or CFrame.fromEulerAnglesYXZ(x, y, 0)
, most likely the latter, but I don’t really know, so more testing for you.
If you want to add in the position provided by CFrame.lookAt(a, b)
, you can just add a
to the end of the Angles
constructors, as long as you subtract a
from b
first:
CFrame.Angles(0, y, 0) * CFrame.Angles(x, 0, 0) + a
Finally, as a warning, if vector b
(from CFrame.new(Vector3.new(), b)
) goes straight up or down, e.g. (0, 1, 0)
, you will want to skip the first angle and just use the x
angle:
Edit: this is false, you don’t have to skip this at all.
I recommend using this as your testing environment, since spinning objects make it easy to tell when something goes wrong:
-- This tests both x and y calculations
local partA = ...
local partB = ...
local a = Vector3.new(...)
local function toAngles(b)
local x = ...
local y = ...
return x, y
end
local function TestY(i)
return Vector3.new(math.sin(i), 0, math.cos(i))
end
local function TestX(i)
return Vector3.new(0, math.sin(i), math.cos(i))
end
while true do
for i = 0, 0.05, 2 * math.pi do
local b = TestY(i) -- or TestX(i)
partA.CFrame = CFrame.new(a, b + a)
local x, y = toAngles(b)
partB.CFrame = CFrame.Angles(0, y, 0) * CFrame.Angles(x, 0, 0) + a + Vector3.new(6, 0, 0)
wait()
end
end
The reason why I’m making this so easy to test is because I want to know too…
Make sure to point out where the LookVector
Is clearly so no mistakes are made, obviously.
Thanks! I’m sure this will help me.