Slope Tilt not working

I’m making a script that tilts the player in the direction of a slope, however it is not really working and gives some really funny results. Here’s a screenshot (i would send a video but the recorder is not working:)

--Localscript, StarterCharacterScripts

local character = game.Players.LocalPlayer.Character
local rootPart = character:WaitForChild("HumanoidRootPart")
local rayParams = RaycastParams.new()
rayParams.FilterType = Enum.RaycastFilterType.Exclude

game:GetService("RunService").Heartbeat:Connect(function()
	local ray = workspace:Raycast(rootPart.Position, Vector3.new(0, 10, 0), rayParams)
	if ray then
		rootPart.CFrame = CFrame.new(rootPart.Position, ray.Normal)
	end
end)

Thats because roblox’s movement system constantly puts the character upright.
You’d have to modify the PlayerModule in order to be able to achieve that

I believe there are some resources on the forum regarding this

You can do this

Does this solution work for R6?

Requires some adjustment but will work, same concept and math formulas. However added an extra C1 inverse because R6 has some weird rotation to it’s C1 creating an offset.

I think the R15 one is the wrong actually this formula should work for both R15 and R6 (c1 offset not included)
image

local motor : Motor6D = script.Parent.HumanoidRootPart.RootJoint -- Assume this is a Motor6D
local part0 = motor.Part0 -- Parent part
local part1 = motor.Part1 -- Child part (e.g., head, torso)

local randomAxis = Vector3.new(0,0,1)

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


local originalC0 = motor.C0
local originalC1 = motor.C1

local function worldCFrameRotationToC0ObjectSpace(motor6DJoint, worldCFrame)
	local part0 = motor6DJoint.Part0
	local c1Store = motor6DJoint.C1
	local goalC0CFrame = part0.CFrame:Inverse() * worldCFrame * c1Store
	return goalC0CFrame
end
-- your previous code...

-- Create debug part for target rotation
local debugPart = Instance.new("Part")
debugPart.TopSurface = Enum.SurfaceType.Motor

debugPart.FrontSurface = Enum.SurfaceType.Motor
debugPart.Size = Vector3.new(3, 3, 0.5)
debugPart.Color = Color3.new(1, 0, 0) -- Red
debugPart.Material = Enum.Material.Neon
debugPart.Anchored = true
debugPart.CanCollide = false
debugPart.CanTouch = false
debugPart.CanQuery = false
debugPart.Name = "DebugTarget"
debugPart.Parent = workspace

-- Create another part for the floor normal gizmo
local floorNormalGizmo = Instance.new("Part")
floorNormalGizmo.Size = Vector3.new(0.2, 0.2, 5) -- Thin arrow
floorNormalGizmo.Color = Color3.new(0, 1, 0) -- Green
floorNormalGizmo.Material = Enum.Material.Neon
floorNormalGizmo.Anchored = true
floorNormalGizmo.CanCollide = false
floorNormalGizmo.CanTouch = false
floorNormalGizmo.CanQuery = false
floorNormalGizmo.Name = "FloorNormalGizmo"
floorNormalGizmo.Parent = workspace

--part0.Transparency = 0 -- make sure part0 is visible

--game["Run Service"].PreSimulation:Connect(function(dt)
--	motor.Transform = CFrame.new()
--end)

local params = RaycastParams.new()
params.FilterDescendantsInstances = {script.Parent, floorNormalGizmo, debugPart}

while true do
	local rayResult = workspace:Raycast(part0.Position, -1000 * part0.CFrame.UpVector, params)

	if rayResult then
		local floorNormal = rayResult.Normal
		local hitPosition = rayResult.Position
		
		local angle = part1.CFrame.UpVector:Dot(floorNormal)
		--print(math.deg(math.acos(angle))) IDK
		--if math.deg(math.acos(angle)) < 0.1 then
		--	task.wait()
		--	continue
		--end
		-- Rotate the part1 to align with floor normal
		local x,y,z = part1.CFrame:ToOrientation()
		local part1NewCF = CFrame.fromOrientation(x,y,0)
		local rotateToFloorCFrame = getRotationBetween(part0.CFrame.UpVector, floorNormal, randomAxis)

		-- Target worldspace CFrame
		local targetWorldCFrame = rotateToFloorCFrame * part0.CFrame*originalC1:Inverse()

		-- Move the debug part to the target
		--debugPart.CFrame = targetWorldCFrame.Rotation + part0.Position + Vector3.new(0,4,0)

		-- Update the Motor6D's C0
		motor.C0 = (part0.CFrame:Inverse() * targetWorldCFrame).Rotation + motor.C0.Position
		--motor.C1 = CFrame.new()
		-- Update the floor normal gizmo
		--local lookVector = floorNormal
		--local upVector = Vector3.new(0,1,0)
		--if math.abs(lookVector:Dot(upVector)) > 0.99 then
		--	upVector = Vector3.new(1,0,0) -- avoid gimbal lock
		--end
		--local rightVector = upVector:Cross(lookVector).Unit
		--local newUpVector = lookVector:Cross(rightVector).Unit

		--local orientationCFrame = CFrame.lookAlong(hitPosition, floorNormal * 2.5)
		--floorNormalGizmo.CFrame = orientationCFrame
	end
	
	task.wait(0)

end