Probably you can create a other piece (this piece must be hidden, with CanCollide in false and transparency in 1) with the local axis identical to the world axis, next weld the ‘bone’ piece to the new piece, and then attempt modify the orientation of the new piece, this should make ‘bone’ move in that same orientation (world axis).

I’ve been facing the same issue, and it seems this wasn’t solved yet.
I’m also trying to create a creature system and the head of the skinned mesh simply doesn’t want to rotate on the world axis.

I’ve tried a few solutions that were found on the dev forums :

local rot = CFrame.Angles(math.pi/4,0,0)
Part.CFrame=(rot*(Part.CFrame-Part.CFrame.p))+Part.CFrame.p

While these seem to work for part’s cframes, it doesn’t for bones. I believe it’s related to some specific behavior with attachments. Either way, this really needs a solution.

Bones follow a similar CFrame logic to Motor6D, execpt the C0 and C1 is replaced with Attachment0 and Attachment1 CFrame property, and the Part0 and Part1 is replaced with the parent parts the attachments are attached to.

--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

Now you should be able to repeat with bones

boneAttach0.Parent.CFrame * boneAttach0.CFrame = boneAttach1.CFrame* boneAttach1.CFrame
--Solve for a CFrame property
boneAttach0.CFrame = ...

I’m slightly confused on the meaning of boneAttach0 and boneAttach1.
I’m guessing boneAttach0 represents the bone we’re actively rotating, but so what is boneAttach1 ?
I don’t see it being a property of bones.

I see, thanks for your time.
I’ve tried applying the new code snippet in my case but it seems like it keeps rotating on the local axis…
Here’s the code :

local rot = CFrame.Angles(0,math.rad(90),0)
local newRotationWorldCFrame = (rot*(Neck.CFrame-Neck.CFrame.p))+Neck.CFrame.p
Neck.CFrame = Neck.Parent.CFrame:Inverse()*newRotationWorldCFrame* Neck.def_c_head_joint.CFrame

Here, “Neck” represents the bone I’m trying to rotate and “def_c_head_joint” is the child of it. (though this is still confusing to me, what if I’m trying to move the last bone ?)
Either way, this is the result :

local neckBone : Bone = script.Parent
while true do
task.wait()
local rot = CFrame.Angles(0,0.1,0)
--Same rotation as doing WorldSpace axis rotation through ctrl+L
local newRotationWorldSpace = rot*neckBone.WorldCFrame.Rotation+neckBone.WorldCFrame.Position
neckBone.WorldCFrame = newRotationWorldSpace
end

You probably need :Inverse() if you want to use .CFrame instead of .WorldCFrame like the above post for optimization.

Something like this which has the same effect

local neckBone : Bone = script.Parent
while true do
task.wait()
local rot = CFrame.Angles(0,0.1,0)
local newRotationWorldSpace = rot*neckBone.WorldCFrame.Rotation
local test = neckBone.Parent.WorldCFrame:Inverse()
neckBone.CFrame = (test*newRotationWorldSpace).Rotation + neckBone.CFrame.Position
end

The confusing part is that Bones can be children of Parts or other Bones, so sometimes using .WorldCFrame is mandatory for attachments whereas for parts you can just use .CFrame which is equivalent.

Unlike Attachment instances, Bones can be children of other Bones in addition to Parts. When parented to another Bone the child bone’s world position will be relative to the parent Bone’s position. Bones form an explicit hierarchy.

Hi
I’m trying to do a similar thing, but have the rotation be in the rootpart’s y-direction instead of world space. Also with a fixed offset relative to the default position of the bone.
Here’s my code:

local defaultCFrame = bone.WorldCFrame
local function getWorldOrientation(bone, offset)
local rotBase = rootPart.CFrame * CFrame.Angles(0, math.rad(offset), 0)
local newRotationWorldSpace = rotBase * defaultCFrame.Rotation
local test = bone.Parent.WorldCFrame:Inverse()
local destCFrame = (test*newRotationWorldSpace).Rotation + bone.CFrame.Position
local rx, ry, rz = destCFrame:ToOrientation()
local dx, dy, dz = math.deg(rx), math.deg(ry), math.deg(rz)
return Vector3.new(dx, dy, dz)
end
-- Rotate 'bone' with 45° along the rootpart's y-axis
bone.Orientation = getWorldOrientation(bone, 45)

This behaves exactly like a want apart from one minor thing: it works only if my model’s rootpart is aligned with the world root on startup. I want it to work in whatever orientation the rootpart is.

Hey there, I believe you should move the world CFrame inside the function as it will update as the model rotates. It probably only works initially for that reason.

local function getWorldOrientation(bone, offset, defaultCFrame)
local rotBase = rootPart.CFrame * CFrame.Angles(0, math.rad(offset), 0)
local newRotationWorldSpace = rotBase * bone.WorldCFrame.Rotation
-- Convert from world space to bone local space
local test = bone.Parent.WorldCFrame:Inverse()
local destCFrame = (test * newRotationWorldSpace).Rotation
-- Get Orientation from CFrame
local rx, ry, rz = destCFrame:ToOrientation()
local dx, dy, dz = math.deg(rx), math.deg(ry), math.deg(rz)
return Vector3.new(dx, dy, dz)
end
while true do
task.wait()
bone.Orientation = getWorldOrientation(bone, 45, defaultCFrame)
end

makes it spin around unfortunately. What I want is for the 45° to be a fixed y-rotation offset along the rootpart’s axis referenced against the bone’s original CFrame.

The code in the previous post works when rotating the rootpart, but it only works if the rootpart was originally aligned with the world root.
I want to make it work with an arbitrary rotation at the time ‘defaultCFrame’ is set.