How do I rotate a motor6D in world space instead of its part1's space?

The title says it all, imagine 2 parts, A and B, and you want to rotate part A on part B’s X-axis:


image

Something like in the images, but by script, I researched a lot in the dev forum but I only found stuff about rotating in an object in world space or its own, nothing about rotating in another object’s space.

I also tried doing:

local partA = workspace.partA
local partB = workspace.partB

partA.CFrame = (partA.CFrame * CFrame.Angles(math.rad(45), 0, 0)):ToObjectSpace(partB.CFrame)

but it doesn’t work as intended:

Any help is appreciated.

Now tried:

local partA = workspace.partA
local partB = workspace.partB

partA.CFrame = CFrame.new(partA.Position) * CFrame.fromAxisAngle(partB.CFrame.XVector, math.rad(45))

and it works:

However, although I gave this example with parts I’m actually trying to do this with motor6Ds, I used this tutorial:

to make my character’s torso rotate and it works but I have some animations that rotate the upper and lower torso, thus, the waist C0 must rotate on the HumanoidRootPart’s space because it’s the only body part that is always facing where the camera is facing:


edit: C0 on the annotations refers to upperTorso.waist.C0

1 Like

My code rn:

local neckC0 = CFrame.new(0, 0.8, 0)
local waistC0 = CFrame.new(0, 0.2, 0)

tilt.OnServerEvent:Connect(function(player, angle)
	local tiltMotor = player.Character.Head:FindFirstChild("tiltCharacter")
	
	if not tiltMotor then return end
	
	tiltMotor.DesiredAngle = angle
end)

setup.OnServerEvent:Connect(function(player)
	local character = player.Character
	
	local tiltMotor = character.Head.tiltCharacter
	local neck = character.Head.Neck
	local waist = character.UpperTorso.Waist
	
	runService.Heartbeat:Connect(function()
		local angle = tiltMotor.CurrentAngle
		
		neck.C0 = neckC0 * CFrame.Angles(angle * 0.45, 0, 0)
		
		if math.deg(angle) > 45 or math.deg(angle) < -60 then return end
		
		waist.C0 = waistC0 * CFrame.Angles(angle, 0, 0)
	end)
end)

Tilt is fired in time intervals, and “setup” is called when a player is added.

edit: when I try to apply the same logic as the parts, changing:

waist.C0 = waistC0 * CFrame.Angles(angle, 0, 0)

to

waist.C0 = waistC0 * CFrame.fromAxisAngle(rootPart.CFrame.XVector, angle)

The results are mostly the same, it’s not rotating correctly

Just noticed it’s because C0 is just an offset, meaning any rotation there is on its local space, my new question is:

how do I rotate a motor6D in world space? I printed

CFrame.fromAxisAngle(rootPart.CFrame.XVector, angle)

converting everything to orientation and degrees, this CFrame is correct, but when applying to waist.C0 it goes to the local space of upperTorso or lowerTorso (?), how can I apply rotation to the motor waist in world space?

maybe:

localSpaceRotation = waist.Part0.CFrame:ToObjectSpace(CFrame.Angles(angle,0,0))
waist.C0 = waist.C0 * localSpaceRotation

if CFrame.Angles(angle,0,0) is the worldspace rotation you want.

The rotation I want is

However if you just throw this on the waistC0 like I did here:

the rotation “on axis angle” is applied as an offset (c0 itself is an offset), messing up everything:

EDIT: only in this case, where CFrame.fromAxisAngle(rootPart.CFrame.XVector, angle) is mostly on Z, if I turn my character to face global X then those prints on output change form ~-0, ~0, rotationAmount to rotationAmount, ~0, ~0, but it always should be in global space.

The rotation printed should be applied in world space, when I apply it in a C0 the effect on the image above happens (not intended, and varies depending on where the character is facing) because it’s not really rotating on world space.

code:

local neckC0 = CFrame.new(0, 0.8, 0)
local waistC0 = CFrame.new(0, 0.2, 0)

tilt.OnServerEvent:Connect(function(player, angle)
	local tiltMotor = player.Character.Head:FindFirstChild("tiltCharacter")
	
	if not tiltMotor then return end
	
	tiltMotor.DesiredAngle = angle
end)

startReplicating.OnServerEvent:Connect(function(player)
	local character = player.Character
	
	local tiltMotor = character.Head.tiltCharacter
	local neck = character.Head.Neck
	local waist = character.UpperTorso.Waist
	
	local rootPart = character.HumanoidRootPart
	
	runService.Heartbeat:Connect(function()
		local angle = tiltMotor.CurrentAngle
		
		neck.C0 = neckC0 * CFrame.Angles(angle * 0.45, 0, 0)
		
		if math.deg(angle) > 45 or math.deg(angle) < -60 then return end
		
		local rotation = CFrame.fromAxisAngle(rootPart.CFrame.XVector, angle)
		
		local x, y, z = rotation:ToEulerAnglesXYZ()
		print("x: " .. math.deg(x))
		print("y: " .. math.deg(y))
		print("z: " .. math.deg(z))
		print("-----------------------------")
		
		waist.C0 = waistC0 * rotation
	end)
end)
1 Like

I tried it and my upper torso just flies around very far away.

1 Like

Also I know that just doing

waist.C0 = waistC0 * CFrame.Angles(angle, 0, 0)

mostly fixes it, but if an animation rotates lower or upper torso it completely messes up, as I explained here:

Giving a better explanation to these images: basically, I want the character’s left arm to always point to humanoidRootPart’s direction, while also considering if the camera is pointing down or up, ignoring the lower and upper torso’s rotation on Y, so:

In image 1: waist is rotated as I want, even though lower and upper torso are rotated on Y, but it works because the camera is facing forward, not up and not down.

In image 2: waist isn’t rotated as I want, it’s rotating the correct amount but on upperTorso’s local Z axis, when I want it to rotate this same amount but on humanoidRootPart’s local Z axis.

I partially understand this happens because c0 is an offset, so applying 45 degrees on Z will be on motor6D.part1’s local Z axis, but I don’t know any way around it.

You can mess around with the Motor6D equation.

Example for welds from my tutorial, the basis of inversing the part0 CFrame as @iondrive used.

--Starting from the weld equation

part1.CFrame * C1 == Part0.CFrame * C0

--Apply Part0.CFrame:Inverse() to the left hand side of both equations

Part0.CFrame:Inverse()*part1.CFrame * C1 = Part0.CFrame:Inverse()*Part0.CFrame * C0

--Apply CFrame:Inverse() * CFrame = CFrame.new(), CFrame Inverse property
Part0.CFrame:Inverse()*part1.CFrame * C1 = CFrame.new() * C0

--CFrame.new() *anyCFrame = anyCFrame, Identity CFrame property
Part0.CFrame:Inverse()*part1.CFrame * C1 =  C0
--Rearrange equation, swap sides
C0 = Part0.CFrame:Inverse()*part1.CFrame * C1

--We want part1 CFrame to equal some CFrame like CFrame.lookAt()

C0 = Part0.CFrame:Inverse()*CFrame.lookAt(part1, part2) * C1

The difference for welds is .Transform is applied to the C0 as the Part0 is the part closest to the root part.

part1.CFrame * C1 == Part0.CFrame * C0 * Motor6D.Transform
1 Like

I played around with it a bit and looks like I’m in the right path, but there are some problems:

changing C0 with C0 = Part0.CFrame:Inverse()*CFrame.lookAt(part1, part2) * C1 to make upperTorso CFrame equal to some CFrame causes this:

and the same old problem:

here’s the current code:

local neckC0 = CFrame.new(0, 0.8, 0)
local waistC0 = CFrame.new(0, 0.2, 0)

tilt.OnServerEvent:Connect(function(player, angle)
	local tiltMotor = player.Character.Head:FindFirstChild("tiltCharacter")
	
	if not tiltMotor then return end
	
	tiltMotor.DesiredAngle = angle
end)

startReplicating.OnServerEvent:Connect(function(player)
	local character = player.Character
	
	local tiltMotor = character.Head.tiltCharacter
	local neck = character.Head.Neck
	local waist = character.UpperTorso.Waist
	
	local rootPart = character.HumanoidRootPart
	local part0 = waist.Part0
	local upperTorso = character.UpperTorso
	
	runService.Heartbeat:Connect(function()
		local angle = tiltMotor.CurrentAngle
		
		neck.C0 = neckC0 * CFrame.Angles(angle * 0.45, 0, 0)
		
		if math.deg(angle) > 45 or math.deg(angle) < -60 then return end
		
		local cf = rootPart.CFrame * CFrame.Angles(angle, 0, 0)
		
		waist.C0 = part0.CFrame:Inverse() * cf * waist.C1
	end)
end)

Is it even possible to achieve world space rotation with C0s? If not, should I just apply upperTorso.CFrame directly, using an angle from rootPart local X axis? This would mess up torso animations, right?

I know I could use lookAt to make the torso point to camera’s direction using the equation you provided but this would make the left arm point extremely to the left when I want it to point forward.

After some days working on other stuff, I decided to pick this again and I just realized there’s no way to do what I was trying to, C0 will always be on local space.

But what I can do is split rotation on X and Z based on how far away we’re from root part rotation Y to achieve the same effect, if anyone didn’t understand the effect I was talking about here it is:

and here’s the code, just in case future readers need it:

local neckC0 = CFrame.new(0, 0.8, 0)
local waistC0 = CFrame.new(0, 0.2, 0)

tilt.OnServerEvent:Connect(function(player, angle)
	local tiltMotor = player.Character.Head:FindFirstChild("tiltCharacter")
	
	if not tiltMotor then return end
	
	tiltMotor.DesiredAngle = angle
end)

startReplicating.OnServerEvent:Connect(function(player)
	local character = player.Character
	
	local tiltMotor = character.Head.tiltCharacter
	local neck = character.Head.Neck
	local waist = character.UpperTorso.Waist
	
	local rootPart = character.HumanoidRootPart
	local lowerTorso = character.LowerTorso
	local upperTorso = character.UpperTorso
	
	runService.Heartbeat:Connect(function()
		local angle = tiltMotor.CurrentAngle
		
		local lX, lY, lZ = lowerTorso.CFrame:ToObjectSpace(rootPart.CFrame):ToEulerAnglesXYZ()
		local uX, uY, uZ = upperTorso.CFrame:ToObjectSpace(rootPart.CFrame):ToEulerAnglesXYZ()

		local differenceFromRootPartRotationY = (lY + uY)/2

		local rotationXratio = 1 - differenceFromRootPartRotationY/math.rad(90)
		local rotationZratio = differenceFromRootPartRotationY/math.rad(90)
		
		neck.C0 = neckC0 * CFrame.Angles(angle*.5*rotationXratio, 0, -angle*.5*rotationZratio)
		
		if math.deg(angle) > 45 or math.deg(angle) < -60 then return end
		
		waist.C0 = waistC0 * CFrame.Angles(angle*rotationXratio, 0, -angle*rotationZratio)
	end)
end)

lY = lower torso rotation difference from root part on Y, in radians.
uY = same thing but for the upper torso.

and the same for other axes, just changing, of course, the respective axis.

2 Likes

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