How would you tilt the character in the direction you walk in?

Hey! So recently in a game, I was shown this:

https://gyazo.com/409c245c4a46e48bc239d40d045ddfbd

now, I’ve done my research but I was unable to figure how to create this.

My first guess would be editing the Motor6D rigs’s orientation. Another person suggested using BodyGyro to tilt the character but that didn’t go well.

EDIT: I’m also going to assume that this will be done on the client since the client character replicate’s to the server.

It seems like my methods aren’t as smooth as this.

Any information regarding this is appreciated.

EDIT:
After much researching I’ve found a working script that you are free to copy and paste:

--|| SERVICES ||--
local RunService = game:GetService("RunService")
local Players = game:GetService("Players")

--|| VARIABLES ||--
local Player = Players.LocalPlayer
local Character = script.Parent
local Humanoid = Character:WaitForChild"Humanoid"

local HumanoidRootPart = Character.HumanoidRootPart
local Torso = Character.Torso

local RootJoint = HumanoidRootPart.RootJoint
local LeftHipJoint = Torso["Left Hip"]
local RightHipJoint = Torso["Right Hip"]

local function Lerp(a, b, c)
	return a + (b - a) * c
end

local Force = nil
local Direction = nil
local Value1 = 0
local Value2 = 0

local RootJointC0 = RootJoint.C0
local LeftHipJointC0 = LeftHipJoint.C0
local RightHipJointC0 = RightHipJoint.C0

RunService.RenderStepped:Connect(function()
	--> To get the force, we multiply the velocity by 1,0,1, we don't want the Y value so we set y to 0
	Force = HumanoidRootPart.Velocity * Vector3.new(1,0,1)
	if Force.Magnitude > 2 then
		--> This represents the direction
		Direction = Force.Unit	
		Value1 = HumanoidRootPart.CFrame.RightVector:Dot(Direction)
		Value2 = HumanoidRootPart.CFrame.LookVector:Dot(Direction)
	else
		Value1 = 0
		Value2 = 0
	end
	
	--> the values being multiplied are how much you want to rotate by
	
	RootJoint.C0 = RootJoint.C0:Lerp(RootJointC0 * CFrame.Angles(math.rad(Value2 * 10), math.rad(-Value1 * 10), 0), 0.2)
	LeftHipJoint.C0 = LeftHipJoint.C0:Lerp(LeftHipJointC0 * CFrame.Angles(math.rad(Value1 * 10), 0, 0), 0.2)
	RightHipJoint.C0 = RightHipJoint.C0:Lerp(RightHipJointC0 * CFrame.Angles(math.rad(-Value1 * 10), 0, 0), 0.2)
end)

This is for R6, to use it place a LocalScript into StarterCharacterScripts and paste the contents in there. Enjoy!

47 Likes

Could you contact who showed you and ask them?

2 Likes

The person who showed me this was not actually the owner / creator of this asset. He suggested that BodyGyro was being used.

I was in studio and tried to use BodyGyro but instead it gave me some odd behavior. Again, my best guess would be Motor6D edits.

1 Like

Ok, thanks. Have you got the script changed to use Motor6D yet?
Would you care to share the script you have so far?

2 Likes

The results I have right now are odd.

Editing the Desired Angle and Velocity for Motor6D seems to be pointless unless it’s the head (the head actually rotates).

Where editing the C0 and C1 properties seem to rotate the character by a 90-degree Interval

	Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
		Character.HumanoidRootPart["RootJoint"].C1 = CFrame.new(math.rad(-10),math.rad(180),0)
	end)
	

My current plan is to use MoveDirection as an update trigger which makes sense. Now I just have to figure out how to tilt the character.

EDIT: Multiplying the original C0 / C1 Property by an angle causes this:

https://gyazo.com/e54b4cd31edbf3fb40c4da14d3c747a0

The character moves away.

2 Likes

After messing around with it I gave up and went back to BodyGyro, it had better results.

So right now the character does indeed tilt:


	Humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
		if not Character.HumanoidRootPart:FindFirstChild("BodyGyro") then
			local newGyro = Instance.new("BodyGyro")
			newGyro.D = 250
			newGyro.P = 6000
			newGyro.Parent = Character.HumanoidRootPart
		else
			local Direction = Character.HumanoidRootPart.CFrame:VectorToObjectSpace(Humanoid.MoveDirection)
			
			Character.HumanoidRootPart.BodyGyro.CFrame = CFrame.new(Character.HumanoidRootPart.Position, Direction)
		end
	end)
	

The only issue is MoveDirection is in WorldSpace which is not relative to the character meaning…

https://gyazo.com/be75f8a220e85b8c6d1eafb9a6b38d15

So to fully finish this, I’d need to know how to get the MoveDirection relative to the player.

I’ve done some research and found 2 related links:

Both state that you must use VectorToObjectSpace but I do not understand how to use them. If anyone is experienced with VectorToObjectSpace it’d be amazing if you could reply. I’ve tried:

Character.HumanoidRootPart.CFrame:VectorToObjectSpace(Direction)

But was unable to get any new results.

2 Likes

I’ve came up with a very inefficient / bad solution to fill. It still does not provide the smoothness as the gif shown above.

https://gyazo.com/b7617226e3879e95803ce8dd88b4d2cc


local moveKeys = {
	["W"] = true,
	["A"] = true,
	["S"] = true,
	["D"] = true,
}


module.getDirectionFromKey = function(Key, Type)
	local Character = Player.Character
	local PrimaryPart = Character.HumanoidRootPart
	local Value = 10
	if Key == "W" then
		return (Character.HumanoidRootPart.CFrame * CFrame.new(0,-Value/2,-Value)).Position
	elseif Key == "S" then
		return (Character.HumanoidRootPart.CFrame * CFrame.new(0,-Value/2,Value)).Position
	elseif Key == "A" then
		return (Character.HumanoidRootPart.CFrame * CFrame.new(-Value,-Value/2,0)).Position 
	elseif Key == "D" then
		return (Character.HumanoidRootPart.CFrame * CFrame.new(Value,-Value/2,0)).Position 
	end
end


Humanoid.Running:Connect(function(Speed)
		if Speed == 0 then
			if Character.Torso:FindFirstChild("BodyGyro") then
				Character.Torso.BodyGyro.P = 0
			end
		else
			if Character.Torso:FindFirstChild("BodyGyro") then
				Character.Torso.BodyGyro.P = 5000
			end
			
			if not moveDirectionConnection then
				moveDirectionConnection = UserInputService.InputBegan:Connect(function(Input, IsTyping)
					if IsTyping or not Character then return end
					if moveKeys[Input.KeyCode.Name] then
						if not Character.Torso:FindFirstChild("BodyGyro") then
							local newGyro = Instance.new("BodyGyro")
							newGyro.D = 125
							newGyro.P = 5000
							newGyro.Parent = Character.Torso
						else
							local TotalCFrame = module.getDirectionFromKey(Input.KeyCode.Name)
							
							for Key, _ in pairs(moveKeys) do
								if Key ~= Input.KeyCode.Name then
									if UserInputService:IsKeyDown(Enum.KeyCode[Key]) then
										TotalCFrame = TotalCFrame * module.getDirectionFromKey(Key)
									end
								end
							end
							
							Character.Torso.BodyGyro.CFrame = CFrame.new(Character.Torso.Position, module.getDirectionFromKey(Input.KeyCode.Name))
						end
					end
				end)
			end
		end
	end)

As most of you guys can see, this is not very efficient. Sometimes the BodyGyro acts glitchy like so:

https://gyazo.com/90b2cb6306c26a1d80803f0166ead7b9

Again, if someone experienced with VectorToObjectSpace could help out that would be amazing.

For now this is the end result. Please note that editing the Motor6D invisible C0 and C1 properties do not work well as well as the DesiredAngles.

3 Likes

It’s as simple as sticking the code that begins with “local movementVector = …” in RenderStepped

local defaultC1 = lowerTorso.Root.C1
local tiltZ = 0
local tiltX = 0

function Lerp(a, b, t)
return a + (b - a) * t
end

local movementVector = rootPart.CFrame:vectorToObjectSpace(rootPart.Velocity/math.max(Humanoid.WalkSpeed,0.01))
tiltZ = math.clamp(Lerp(tiltZ, movementVector.X,0.1),-0.25,0.25)
tiltX = math.clamp(Lerp(tiltX, math.abs(math.clamp(movementVector.Z,-1,0)),0.1),0,0.25)
lowerTorso.Root.C1 = defaultC1*CFrame.Angles(tiltX,0,tiltZ)
43 Likes

There are tons of things I don’t understand in there! Could you explain each part in detail? It looks like a lot of math. Also thanks for replying.

EDIT: I’d like to learn why you did what you did rather than copy-pasting the solution, this way hopefully I learn how to do this without coming back.

11 Likes

math.abs(x)

Returns the distance of x from 0. If X is negative, it returns -(distance from 0).
math.abs(5) = 5
math.abs(25) = 25
math.abs(-25) = 25

math.clamp(val,min,max)

If val is over max, then it returns max
If val is under min, it returns min
If val > min and val < max then it returns val

CFrame:VectorToObjectSpace(Vector3)

Object space returns the position that is relative to the object.

Code
local p1 = Instance.new('Part')
p1.Parent = workspace
p1.Anchored = true
p1.Color = BrickColor.Random().Color
p1.CFrame = CFrame.new(0,6,0)

local p2 = Instance.new('Part')
p2.Parent = workspace
p2.Anchored = true
p2.Color = BrickColor.Random().Color
p2.CFrame = CFrame.new(0,3,0)

print(p1.CFrame:VectorToObjectSpace(p1.CFrame.Position))

Lerp(Value,Goal,Time)

Lets say A is the value you want to lerp. You have to add (B - A) to A in order to get the distance of A and B while changing the variable.

local z = 0
for i = 1,10 do
    z = Lerp(z,10,.1) -- Value, Goal, Alpha
    print(z) -- Print what changed
end

I don’t have a ton of time so I made this vaguely.

staff pls dont delete this

15 Likes

Can I replicate this to r15? Like changing the attachments to r15 attachments?

2 Likes

I humbly ask, may i please use this script as well?

3 Likes