Change only a CFrame upVector

You have a start and target upvector:

local start = part.CFrame.UpVector
local target = Vector3.new(1, 1, 1).Unit

You can do this in a single rotation about an axis. The axis is perpendicular to both:

local axis = start:Cross(target)

And figure out the angle between the two

local angle = math.acos(start:Dot(target))

Then just rotate the CFrame by that amount

part.CFrame = CFrame.fromAxisAngle(axis, angle) * part.CFrame.Rotation + part.CFrame.Position

I’ll do some testing to check, but I’m pretty sure that would work. Ignoring cases like full 180 degree rotations or 0 degree rotations, for which you’ll have to write special cases.

Edit: left out one part, gimme a sec :slight_smile: Fixed. Final function:

local function AlignUpVector(cframe: CFrame, up: Vector3)
	local start = cframe.UpVector
	local angle = math.acos(start:Dot(up))
	local axis = nil
	if angle < 0.00001 or angle > math.pi - 0.00001 then
		axis = Vector3.xAxis
	else
		axis = start:Cross(up)
	end
	return CFrame.fromAxisAngle(axis, angle) * cframe.Rotation + cframe.Position
end

Example use:

local target = Vector3.new(1, 1, 1).Unit
part.CFrame = AlignUpVector(part.CFrame, target)

Edit: Modified function to take into account special cases

7 Likes