Dumb question, but given any CFrame, how could you replace any CFrame’s orientation with the rotation implied by a unit vector?
Assuming I understand what you’re asking, you need two vectors to form an unambiguous rotation. In most cases you can just use the unit Y vector as the rotation, but note that you will have a “singularity” (point where things explode) when the primary vector is facing directly up or directly down. If this isn’t a problem, you can use the constructor of CFrame.new
which takes two vectors. One vector is a position (for a rotation-only CFrame, use zero) and the other is a look vector (pass in your unit vector here). It will assume an up vector equal to Vector3.new(0, 1, 0)
.
If you can’t use the unit Y as an up vector (such as in a space game, or some other case where gimbal lock matters), you’ll need to find a second vector somehow. Once you have these two vectors, you can take their cross product to form a third vector, and these three vectors form a set of basis vectors which you can use to construct a matrix.
Each column of a CFrame corresponds to a basis vector. There’s an API in the process of shipping which will make this easier, but for now you have to use the component-wise constructor to convert your three basis vectors into a rotation CFrame:
local forwardVector = ...
local upVector = ... -- can default to Vector3.new(0, 1, 0) usually
local rightVector = forwardVector:Cross(upVector) -- order of arguments is important, look up the right hand rule for cross products
local matrix = CFrame.new(
0, 0, 0, -- position component
-- these values are specified row wise (which for this purpose is basically sideways)
rightVector.x, upVector.x, forwardVector.x,
rightVector.y, upVector.y, forwardVector.y,
rightVector.z, upVector.z, forwardVector.z)
Update (March 2023)
There is now a CFrame.fromMatrix(position, right, up, forwards)
method that helps with this.
The code sample from above is much simpler now:
local function faceTowardsDirection(position: Vector3, direction: Vector3) -> CFrame
local upVector = Vector3.yAxis
-- Order of arguments determines the sign. Swap them around and you get a leftVector instead. Look up "Right hand rule" if you're curious.
local rightVector = forwardVector:Cross(upVector)
return CFrame.fromMatrix(origin, rightVector, upVector)
end