How would I do this, or at least where should I start? I'm trying to rotate the players arms to follow a point only along one axis.
First, solving a simpler question:
You have a brick
that you want to point at a point target
, but only turn around the (world's) Y axis -- the part will stay level, parallel to the ground, but face towards the target laterally.
I'm going to restrict us from using CFrame.new(from, target)
because that won't generalize well when we use an arbitrary axis.
That means in order to turn the part, we'll need to be using CFrame.Angles
, probably CFrame.new(0, spin, 0)
for some spin
. So the question is how to determine spin
?
We can determine a direction to the target like so:
local direction = target - brick.Position
This has an x
and a z
(we don't care about the y
since that's what we're rotating around) that we need to turn into an angle. When you need to convert normal coordinates into angles, you'll almost always use math.atan2
:
local spin = math.atan2(direction.x, direction.z) + math.pi
I cannot explain exactly why that is the right formula, it's just what works. I would have expected it to just be
(z, x)
but that doesn't work for some reason -- ROBLOX must have a weird way they've made the matrices work out in CFrames.
Then, the part just needs to be turned using that:
brick.CFrame = CFrame.new(brick.Position) * CFrame.Angles(0, spin, 0)
And we get what we wanted!
brick.CFrame * CFrame.Angles(0, spin, 0)
won't work; since * CFrame.Angles
spins by an amount, it's only correct when brick
starts facing the "0" direction -- thus CFrame.new(brick.Position)
.
This is a bit of a problem for making it more general, so we should fix that.
There are two ways to go about this.
x
and z
in object space.While both work for the simple case, 2 will work much better when we generalize.
It's pretty simple to accomplish that. Just compute direction
in object space:
local direction = brick.CFrame:pointToObjectSpace( target )
Since this is a relative angle, we do in fact want to use this now:
local spin = math.atan2(direction.x, direction.z) + math.pi -- (same as before) brick.CFrame = brick.CFrame * CFrame.Angles(0, spin, 0) -- (similar to before)
Done!
And in fact, now this works no matter how brick
is rotated -- we are no longer restricted to just rotating around the (world's) y axis! It's now around the top surface of brick
!