Rather than trying to convert this up vector into an angle which is also complicated it’s better to leave it I would rather use @EgoMoose advanced CFrame technique to leave it in terms of vectors.
We can obtain a CFrame rotation to align the current persons “UpVector” to the new surface upvector. Here is an example script and video.
The advantage of this method is that it will allow you to rotate along the new upVector without having to specify a right vector as a CFrame.fromMatrix solution.
Even unity games use this method, They have the built in method Quaternion.FromToRotation which is @EgoMoose advanced CFrame trick
Also notice the similarities with my code piece:
local rotateToFloorCFrame = getRotationBetween(wedge.CFrame.UpVector,rayResult.Normal,randomAxis)
local goalCF = rotateToFloorCFrame*wedge.CFrame
--TL;DR
local fromToQuaternion = getRotationBetween(wedge.CFrame.UpVector,rayResult.Normal,randomAxis)
wedge.CFrame = fromToQuaternion * wedge.CFrame
Edit: Yeah this is the correct and smoother version
local wedge = script.Parent
local randomAxis = Vector3.new(1,0,0) -- Read this in the EgoMoose article
local DOWN = -Vector3.new(0,500,0)
local function getRotationBetween(u, v, axis)
local dot, uxv = u:Dot(v), u:Cross(v)
if (dot < -0.99999) then return CFrame.fromAxisAngle(axis, math.pi) end
return CFrame.new(0, 0, 0, uxv.x, uxv.y, uxv.z, 1 + dot)
end
while true do
local dt = wait()
local rayResult = workspace:Raycast(wedge.Position,-1000*wedge.CFrame.UpVector)
if rayResult then
local rotateToFloorCFrame = getRotationBetween(wedge.CFrame.UpVector,rayResult.Normal,randomAxis)
local goalCF = rotateToFloorCFrame*wedge.CFrame
wedge.CFrame = wedge.CFrame:Lerp(goalCF,5*dt).Rotation +wedge.CFrame.Position
end
end