Pivoting the Character along parts

So I’ve been trying to wrap my head around achieving accurate player movement along the faces of different parts (Spheres, Parts, Cylinders) for pretty much the entire day so far. My math is probably worst than a 5th grader’s, that much I know.

Want/Need
I want to add a mechanic in my game where the users would be able to scale up walls and hang upside with ease. Knowing that my math isn’t so hot, this would be impossible to achieve.
So, I have did some digging around the Forums and the internet in general for a snippet of code in which would help me understand and came across a few examples/ sources.

https://devforum.roblox.com/t/walking-on-walls-ceiling/51831/9
https://devforum.roblox.com/t/vector-math-help/32659/5
http://wiki.roblox.com/index.php?title=Cross_product

With a lead to go off of, I thank @Tinarg for the source code, it got me one step closer to my goal. After removing a few things and editing others so that it would be easier for me to implement, it produced good behavior, then no so good behavior.

Problem:
There are two problems that I’ve encountered actually.
1.) The alignment of the character is off centered when scaling along objects that aren’t on the global axis.

2.) Parts like Cylinders and Spheres return incorrect results.

Arragh! Math >_<
After tinkering and reading the output for some time, it is still hard for me to narrow down just what is going on (I’m blaming my lack of math intuition). I am totally stumped and would gladly appreciate any guidance. Here are some snippets from the local script.

Snippet #1

--From http://devforum.roblox.com/t/vector-math-help/32659/5
function Calculate_LookVector(MoveDirection,Normal)

local function clamp(value, min, max)
	return (value < min and min) or (value > max and max) or value
end

local function rotateBy(vec, axis, angle)
	local cosAngle = math.cos(angle)
	local sinAngle = math.sin(angle)
	local axisDotA = axis:Dot(vec)

	return Vector3.new(
		axis.X*axisDotA * (1-cosAngle) + vec.X*cosAngle + (-axis.Z*vec.Y + axis.Y*vec.Z)*sinAngle,
		axis.Y*axisDotA * (1-cosAngle) + vec.Y*cosAngle + (axis.Z*vec.X  - axis.X*vec.Z)*sinAngle,
		axis.Z*axisDotA * (1-cosAngle) + vec.Z*cosAngle + (-axis.Y*vec.X + axis.X*vec.Y)*sinAngle
		)
end

   local a = Vector3.new(0, 1, 0).unit --Original Vector a
   local b = MoveDirection.unit --Original Vector b
   local a2 = Normal.unit --New Vector a

   local axis = a:Cross(a2)
   axis = axis.magnitude == 0 and a:Cross(b).unit or axis.unit
   local angle = math.acos(clamp(a:Dot(a2), -1, 1))

   local b2 = rotateBy(b, axis, angle) --New Vector b
   return b2
end

Snippet #2

function Start()
Connection = RunService.Heartbeat:Connect(function(Tick)
	
	local LookVector = Calculate_LookVector(Humanoid.MoveDirection,Current_Angle)		
	
	local Backward_Ray_Pos = Root.Position-(Current_Angle*3.05)+(LookVector*1.1)		
	
	local Ray_Forward = Ray.new(Root.Position,LookVector*1.3)
	local Ray_Downward = Ray.new(Root.Position,-Current_Angle*3.1)
	local Ray_Backward = Ray.new(Backward_Ray_Pos,-LookVector*3)
		
	local Part_Test1, Point_Test1, Normal_Test1 = game.Workspace:FindPartOnRayWithWhitelist(Ray_Forward,game.Workspace.TestingStuff:GetChildren())
	local Part_Test2, Point_Test2, Normal_Test2 = game.Workspace:FindPartOnRayWithWhitelist(Ray_Downward,game.Workspace.TestingStuff:GetChildren())
	local Part_Test3, Point_Test3, Normal_Test3 = game.Workspace:FindPartOnRayWithWhitelist(Ray_Backward,game.Workspace.TestingStuff:GetChildren())

	if Part_Test1 and Check_Stickable(Part_Test1) then
		Normal = Normal_Test1
		Point = Point_Test1
		Part = Part_Test1
	else if Part_Test2 and Check_Stickable(Part_Test2) then
		Normal = Normal_Test2
		Point = Point_Test2
		Part = Part_Test2
	else if Part_Test3 and Check_Stickable(Part_Test3) then
		Normal = Normal_Test3
		Point = Point_Test3
		Part = Part_Test3
	else
		Normal = nil
		Point = nil
		Part = nil
	end
	end
	end		
	
	if Normal then
		Stick_Part = Part
		Current_Angle = Normal	
		Current_Point = Point
		if Normal.Magnitude > 0 then
			Root.CFrame = CFrame.new(Point)+(Current_Angle*3)
		end
	end

	if Humanoid.MoveDirection.Magnitude > 0 then
		Root.CFrame = CFrame.new(Root.CFrame.p+((LookVector*0.28)/(Tick / (1 / 60))))
		Move = Move:lerp((CFrame.Angles(0,math.pi*0.5, 0) * CFrame.new(Humanoid.MoveDirection)).p, .1 * (Tick / (1 / 60)))					
	end;			
	   Root.CFrame = CFrame.new(Root.CFrame.p, Root.CFrame.p + Current_Angle) * CFrame.Angles(-math.pi*0.5,math.pi,0) * CFrame.new(Vector3.new(), Move)		
   end)
end

Whoops!
Forgot to attach the placefile… My bad DeScript.rbxl (41.0 KB)

2 Likes