Scripting Helpers is winding down operations and is now read-only. More info→
Ad
Log in to vote
2

Rotating along a single axis to point at another position?

Asked by
Sublimus 992 Moderation Voter
9 years ago

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.

1 answer

Log in to vote
0
Answered by
BlueTaslem 18071 Moderation Voter Administrator Community Moderator Super Administrator
9 years ago

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!

Relative Spinning

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.

  1. Calculate the angle difference and multiply by that
  2. Calculate 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!

Ad

Answer this question