A CFrame is composed of four vectors. A position, and then three other directional vectors (RotX, RotY, RotZ), which together describe rotation. LookVector, which you seem to be familiar with, is just that third rotational vector, only pointing in the opposite direction (‘forwards’ as opposed to ‘backwards’).
So, if I have a LookVector that’s [-0.5, 0.3, 0.2], what that’s essentially saying, is that for every stud I want to move in the direction that the object is facing, I have to move -0.5 studs on the global X axis, +0.3 studs on the global Y axis, and +0.2 studs on the global Z axis.
And this is fundamentally what traveling in ObjectSpace is. If I want to move an object forwards by 5 studs, I multiply its CFrame’s RotZ vector by -5, and then add the resulting vector to its position.
Offsetting an object by a vector is much the same. An offset of [2, -4, 6], means multiplying RotX by 2, multiplying RotY by -4, multiplying RotZ by 6, adding each of these resulting displacements together, and then finally adding the object’s position. And this is exactly what happens when you multiply a CFrame by a Vector3. CFrame * Offset
is analogous to CFrame:PointToWorldSpace(Offset)
. They are the same operation.
Thus, as I wrote in my original reply,
And if we want to convert this into a CFrame quantity, such that the bullet faces the same direction as RootPart, then we can just grab those rotational vectors from RootCFrame.
local StartingCF = CFrame.fromMatrix(RootCF * Offset, RootCF.XVector, RootCF.YVector, RootCF.ZVector)
-- Or more briefly,
local StartingCF = CFrame.fromMatrix(RootCF * Offset, RootCF.XVector, RootCF.YVector)
-- As specifying RotZ is unnecessary, since it can already be inferred from which way RotX and RotY are facing.