[Help with Script] Character's workspace CFrame to Keyframe CFrame

I’m trying to make a script based on converting character limb CFrames from workspace to CFrame poses in an animation.

Right now, I’m having trouble getting the formula for converting the CFrame into the correct place in the animation, because if I just set the pose .CFrame to the CFrame I want, it doesn’t display the poses in workspace properly. If I could get help finding the correct formula for the script that would be appreciated,

-- This is what I have to far:
-- The array is sorted like {Head.CFrame, LeftArm.CFrame, RightArm.CFrame, Torso.CFrame, LeftLeg.CFrame, RightLeg.CFrame}

for keyframeindex, arrayofcframes in ipairs(keyframes) do
		local keyframeindexmodel = folder:FindFirstChild(keyframeindex)		
		local keyframe = Instance.new("Keyframe")
		keyframe.Time = animationtime
		keyframe.Name = tostring(keyframeindex)
		local rootPose = Instance.new("Pose")
		rootPose.Name = "HumanoidRootPart"
		rootPose.Weight = 0
		local torsopart = keyframeindexmodel:FindFirstChild("Torso")		
		local torsopose = Instance.new("Pose")
		torsopose.Name = torsopart.Name
		torsopose.Weight = 1
		torsopose.CFrame = arrayofcframes[4] --need help with this line
		for arrayindex, cframe in ipairs(arrayofcframes) do
			if cframe ~= nil then						
				local bodypart = keyframeindexmodel:FindFirstChild(bodyparts[arrayindex][2])				
				if bodypart.Name ~= "Torso" then		
					local pose = Instance.new("Pose")
					pose.Name = bodypart.Name
					pose.Weight = 1
					local joint = getJointBetween(keyframeindexmodel:FindFirstChild("Torso"), bodypart)					
					pose.CFrame *= joint.Transform:Inverse() * joint.C1:Inverse() --and this line
		animationtime += .016
	return keyframeSequence

I don’t fully understand your problem statement, can you draw like a diagram of what CFrame you’re trying to convert and what the result should be relative to?

Adding a repro file would make a solution easy to test.

I have never worked with poses before, I would assume it is related to the .Transform equation

PartParent.CFrame * CParent * Transform == PartChild.CFrame * Child

For solving into object space like for Motor6D C0 here is an example from my tutorial. The point from the link is that you would need to apply the :Inverses in a specific order to get the solution. Consequently, you should probably write the equation and try experimenting with it for poses instead of Motor6D C0 perhaps.

Sorry for the late response.
I’m trying to get the position of the body part CFrames in the Animation Editor:
To match with the position in workspace:

However if I just set the CFrame that is in workspace to the one in the animation editor, it will not display the correct position and orientation.

Could you explain what PartParent, CParent, PartChild, and Child are?

They are

PartParent.CFrame * CParent * Transform == PartChild.CFrame * Child
--Is equivalent to:
--If Part0 is closest to the root part
Part0.CFrame * C0 * motor.Transform = part1.CFrame * C1

Now that it is clear that Animation editor modifies the .Transform values

we can solve for the .Transform

Part0.CFrame * C0 * motor.Transform = part1.CFrame * C1
--Inverse Part0 left side

 C0 * motor.Transform = Part0.CFrame:Inverse()*part1.CFrame * C1
--Inverse the C0 leftside

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

--Since we want to modify the Part1 CFrame, which is the part that is moving. (example right arm for the right shoulder motor6d)

 motor.Transform = C0:Inverse()*Part0.CFrame:Inverse()*someCFrameWeWant * C1

Turning it into a function with an example script:

local leftShoulder = script.Parent

local head = workspace.R6Dummy.Head

local function solveForTransform(desiredCF, motor : Motor6D)
	local C0 = motor.C0
	local Part0 = motor.Part0
	local C1 = motor.C1
	return C0:Inverse()*Part0.CFrame:Inverse()*desiredCF * C1

local goalTransform = solveForTransform(head.CFrame, leftShoulder)

local x,y,z = goalTransform:ToOrientation()

print(goalTransform.Position, math.deg(x),math.deg(y), math.deg(z))

So if we want the animation values to go to the head position of this R6 head, the function will output the values.

This works, but how would you do it with the torso, since there are no motor6d’s that have Part1 connected to the torso?

Have you tried using the root motor?

Yeah but it offsets the character in the wrong position, at least for me

I believe you are using R6, R6 is weirder as the Motor6Ds have rotation.

Try this:

local function solveForTransform(desiredCF, motor : Motor6D)
	local C0 = motor.C0
	local Part0 = motor.Part0
	local C1 = motor.C1

	return C0:Inverse()*Part0.CFrame:Inverse() * desiredCF * C1:Inverse()


The edit makes the r6 rig not work as intended, no :Inverse() on the C1 seemed to work.
The only problem is that the torso position is offset from the position it needs to be.

The character starts around the spawn point, but the animation is offset to the right.

Can you elaborate further on what you mean by where the position needs to be?

Do you mean you want to center the character in the center of the animation grid? Then can’t you set the Torso pose CFrame position to 0,0,0?

BTW you can send over the place file which should explain the problem more clearer, or perhaps send a labeled diagram of the situation.

Alright, so essentially the animation is supposed to start when the timer hits 0, allowing for the script to start recording the players movements.
The first frame of the players’ movements was supposed to be around the spawn location, but was offset in the animation to the right. I need the character in the animation to be located at the same location as the players movements in the workspace, if that makes sense.
Place file (2.3 MB)

However to make coding a bit easier the animation dummy should be set to 0,0,0 in workspace, then we can work from there (made an edit to the place file to set the animation dummy to 0,0,0).

Interesting system you got there.

The issue is not the CFrame function it is collisions.

Using collision groups seem to work and they end up overlapping and to me it seems to work as intended.


--Set for dummy clones
				for i,v : BasePart in pairs(dummyclone:GetDescendants()) do
					if v:IsA("BasePart") then
						v.CollisionGroup = "Dummy"
--Server script to create collision group
local PhysicsService = game:GetService("PhysicsService")
local Players = game:GetService("Players")

PhysicsService:CollisionGroupSetCollidable("Characters", "Characters", false)

PhysicsService:CollisionGroupSetCollidable("Dummy", "Characters", false)

local function onDescendantAdded(descendant)
	-- Set collision group for any part descendant
	if descendant:IsA("BasePart") then
		descendant.CollisionGroup = "Characters"

local function onCharacterAdded(character)
	-- Process existing and new descendants for physics setup
	for _, descendant in pairs(character:GetDescendants()) do

	-- Detect when the player's character is added

Also maybe the .Stepped loop setting can collide to false did the job, but I assume it’s the collision groups that made it work and record more accurately.

Can you send the place download for this? Can’t seem to replicate but I got the collisiongroup down.

I got it to work, I set the animation dummy to 0,0,0 in workspace and used these lines:

local torsocframe = solveForTransform(arrayofcframes[4], getJointBetween(keyframeindexmodel:FindFirstChild("HumanoidRootPart"), torsopart))
torsopose.CFrame = CFrame.new(torsocframe.Position) * CFrame.Angles(torsocframe:ToOrientation())

Thanks for your help!

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