How do I rotate a character based on the ground normal?

Heyo! I’m attempting to create a sliding system in my game. Everything has been going quite smoothly so far, but I’ve run into a little ick that I can’t wrap my head around; I can’t seem to rotate the character based on the ground normal.

This is what I currently have:
image

versus what I actually want to achieve:

image

If you’re confused on the difference between the images; Within the first image, the character’s legs are high up and don’t touch the ground like the second character does. This looks even worse on grounds which have an even smaller slope angle.

I’ve also tried looking at quite a large amount of sources on the developer form, to no avail of course. I even tried AI to help me, but again, to no avail.

This is my current code for sliding:

self.characterSlideProperties.__bindedStepConnection = runService.Heartbeat:Connect(function(deltaTime: number)
	-- get the floor normal and the slope angle
	local floorNormal: Vector3 = self:GetFloorNormal(self.humanoidRootPart.Position + Vector3.new(0, 2.5, 0), Vector3.new(0, -10, 0))
	local slopeAngle = self:GetSlopeAngle(floorNormal)
		
	-- check if the slope anle exists, if not, it means the character went off the edge of a part
	if (not slopeAngle) then
		self:CancelSlide()
			
		return
	end
		
	-- compute the acceleration for the speed of the slide
	local slopeAcceleration = (1 + math.sin(math.rad(slopeAngle))) * 2
		
	-- get the orientation of the camera
	local rx: number, ry: number, rz: number = self.camera.CFrame:ToOrientation()
		
	-- tween the gyro to match the orientation of the camera
	tweenService:Create(bodyGyro, TweenInfo.new(self.slideGyroTweenSpeed, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, 0, false, 0), { CFrame = CFrame.fromOrientation(0, ry, 0) }):Play()
		
	-- compute the time elapsed, the duration elapsed and the force based on the elapses
	local timeElapsed: number = os.clock() - lastTime
	local durationElapsed: number = self.slideLength - timeElapsed
	local force = math.clamp(durationElapsed / slopeAcceleration, 0, 1)
		
	-- get the camera's right vector and cross it with the yAxis ( Vector3.new(0, 1, 0) )
	local cameraRightVector: Vector3 = self.camera.CFrame.RightVector
	local lookVector = cameraRightVector:Cross(Vector3.yAxis)
		
	-- set the humanoidRootPart's assemblyLinearVelocity to a combination of the calculated variables
	self.humanoidRootPart.AssemblyLinearVelocity = (-lookVector * self.slideMagnitude * force) + Vector3.new(0, self.humanoidRootPart.AssemblyLinearVelocity.Y, 0)
		
	-- TODO: orientate the character based on the ground normal...
	--self.rootJoint.C0 = CFrame.new()
end)

I’m trying to manipulate the C0 in the RootJoint of the HumanoidRootPart ( as seen at the very bottom )

If anyone could please guide me in the right direction, that would be greatly appreciated.

4 Likes

Here’s a post that may help you:

You can incorporate the getRotationBetween function to align the RootJoint C0 of the HumanoidRootPart with the returned CFrame, after multiplying it with the HumanoidRootPart’s CFrame.

1 Like

After some digging and trial and error with a few methods. I settled on setting the BodyGyro’s CFrame property using tweening:

tweenService:Create(bodyGyro, TweenInfo.new(self.slideGyroTweenSpeed, Enum.EasingStyle.Sine, Enum.EasingDirection.Out, 0, false, 0), { CFrame = getRotationBetween(self.humanoidRootPart.CFrame.UpVector, floorNormal, Vector3.new(1, 0, 0)) * CFrame.fromOrientation(0, ry, 0) }):Play()

The function from the post you linked was very helpful, thank you!

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.