I need help understanding a specific part of CFrames!

CFrame.new(0,0,0,1,0,0,0,1,0,0,0,1)

char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,0,0))

You dont seem to understand what you’re doing since these expressions evaluate to neutral elements (identity matrix, zero vector). I dont either.
The X/Y/Z vectors describe the base of the rotated part where it would have “no rotation”, since rotation of a part and rotation of a coordinate system are pretty much equivalent. You shouldn’t use them in the first place since .Look/Right/UpVector actually conform to the eye = [0,0,-1] convention.
Also x/y/z components of a vector have absolutely no relation to the xyz Euler angles.

More or less you’re right, I really don’t know what I’m supposed to be doing, a simple task such as just making something only rotate on two axis’s seems impossible when the most basic methods don’t seem to help with this stuff.

However I do know the reference is necessary, otherwise it starts spinning out of control.
Because that means that instead of going from wherever it was currently, it goes back to a default state before applying changes.

As someone experienced with handling Motor6D’s for my inverse kinematics projects I have no idea what is going on like @NachtHemd said.

The most striking is the usage of X vector which is a combination of the X values of the RightVector, UpVector, and Look vector and consequently don’t represent how the CFrame is facing so I’m not sure why you are using math.asin there it seems like it’s just a coincidence you get your intended result. Are you perhaps trying to make the board’s orientation vary with velocity?

From the video, it looks like you just want your board to rotate counter-clockwise when you jump. You probably could get away by just making and playing a predetermined animation when the jump button is pressed. Yeah more information will be required.

The intended effect, is for it to follow the right hand in terms of rotation, for example, when the player jumps the right hand shifts slightly from the board’s center in regards to slightly upwards and a bit forwards, and the board should rotate itself to match where the hand is at that given time. This is to make it seem like the player is constantly holding it, even during other animations.


UNFORTUNATELY, while it does TECHNICALLY work it only works when the player is facing a specific direction, any other angle results in it orbiting around the player and NOT sticking to the player’s side.

I hope that gives enough context for what I’m trying to achieve.

So I’m just going to assume the goal rotation is correct which is the CFrame.new, and use a trial and error method.

So First I set the C0 CFrame to CFrame.new() relative to the world by inversing the part 0 because the C0 is relative to the Motor6D.Part0 so to undo it we inverse it.

        local relativeToWorld = motor6d.Part0.CFrame:Inverse()

Then I rotate it anticlockwise on it’s current y axis which is (0,1,0), in order to make the board’s right vector face forwards to the look vector which by default is (0,0,1). You can adjust this value.

local antiClockwiseOnYAxisCFrame = CFrame.angles(0,math.rad(90),0)

Then I make it equal to the lookAtCFrame that you already made which I’m assuming it’s in world space.

All of it combined using CFrame multiplication is this line of the code below:

motor6d.C0 = relativeToWorld*antiClockwiseOnYAxisCFrame*look
Full code
local antiClockwiseOnYAxisCFrame = CFrame.angles(0,math.rad(90),0)

runService.RenderStepped:Connect(function()
 	if running == true then
		local look = CFrame.new(char.HumanoidRootPart.Position + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,0,0)), char.RightHand.Position + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,1,0)) )

		local x,y,z = look:ToEulerAnglesXYZ()
        local relativeToWorld = motor6d.Part0.CFrame:Inverse()
		motor6d.C0 = relativeToWorld*antiClockwiseOnYAxisCFrame*look
	end

After trying that, it kinda just ended up with this.
https://streamable.com/n7qc6e
If you want I can post the full script if that would help…

Honestly, the place file would be better as I need to know how you set up the Motor6D and to trial and error more. Currently, it looks like only the position component is off so I’ll submit my trial and error proposal.

Such examples of trial error include separating CFrame statements like this:

motor6d.C0 = relativeToWorld-- try this out and see what happens?
--How can you use this result to get the goal CFrame you want?

Currently, I believe the issue is CFrame order of operations which messes up with the final position of the CFrame as the rotation can affect position when multiplying. So I Separated it:

revised trial and error
local antiClockwiseOnYAxisCFrame = CFrame.angles(0,math.rad(90),0)

runService.RenderStepped:Connect(function()
 	if running == true then
		local look = CFrame.new(char.HumanoidRootPart.Position + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,0,0)), char.RightHand.Position + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,1,0)) )
        local lookPositionCF = CFrame.new(look.Position)
        local lookRotationOnly = look - look.Position

        local relativeToWorld = motor6d.Part0.CFrame:Inverse()
        motor6d.C0 = relativeToWorld*lookPositionCF*antiClockwiseOnYAxisCFrame*lookRotationOnly
        
    end
end)

Alright then, here’s the file. I’ll try to do my own work on trying to fix it in the meantime…Hoverboard stuff.rbxl (39.9 KB)

All it is really is just two scripts, a serverscript that gives players the correct parts when their character spawns in and plays a permanent animation to hold the board (or make it seem like so)

and a localscript that does most of the visualwork on rotating the board.

Update 1: This current attempt had me a bit annoyed due to how close it was to getting the result I wanted.
Here’s the code:

repeat wait() until game.Players.LocalPlayer.Character:FindFirstChild("TestBoard") ~= nil
local runService = game:GetService("RunService")
local CAS = game:GetService("ContextActionService")
local board = game.Players.LocalPlayer.Character:FindFirstChild("TestBoard")
local char = game.Players.LocalPlayer.Character
local motor6d = char.HumanoidRootPart.Motor6D
local running = true
local actualreference = motor6d.C0
local anim = char.Humanoid.Animator:LoadAnimation(script.GetOnBoard)


function BoardE(c,o,p)
	if o == Enum.UserInputState.Begin then
		if running == true then
			running = false
			wait(0.05)
			motor6d.C0 = actualreference
			anim:Play()
		else
			running = true
		end
	end
end

CAS:BindAction("Ride",BoardE,true,Enum.KeyCode.LeftShift)

runService.RenderStepped:Connect(function()
	local reference = CFrame.new(0,0,0,1,0,0,0,1,0,0,0,1)
	if running == true then
		local look = CFrame.new(Vector3.new(char.HumanoidRootPart.Position.X,char.HumanoidRootPart.Position.Y,char.HumanoidRootPart.Position.Z) + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,0,0)), char.RightHand.Position + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,1,0)) )
		motor6d.C0 = CFrame.fromEulerAnglesXYZ(0,0,-math.asin(look.ZVector.Y) * 0.5) * CFrame.fromEulerAnglesXYZ(0,-math.asin(look.XVector.Z) * 0.5,0)
	end
end)

And here are the results:
https://streamable.com/ni3e0u
As you can see, when it comes to jumping the effect it has is PERFECT!

However, turning around causes a not-wanted effect where it kinda see-saws along the other axis I want it to rotate on…

As a reminder, it’s supposed to follow the right hand in terms of rotation and only on two axis’s, meaning I still have a semi-long way to go on this…

It will take a while before you can wrap your head around CFrames. You just got to keep using them and figuring out more stuff. Trial and error is a great way to learn them.

When you are using C0 and C1, the cframes are local space offsets, so you have to think about it differently than if you were setting global positions. For something like this you should use both C0 and C1. Together they create a center point which allows you to pivot on. If you want the board to not stay in place on the hip when rotating, moving toward the front and back of the leg a little, adjust the positions of C0 and C1 to place the pivot where you want.

Here is an example of what I think you are looking for. I tried to do it in a way that is easy to understand. It might not be the best way but I hope it helps. It does not use a joint because I thought it was simpler to do it this way and I think you are overthinking it. You might also want to interpolate the movements over time to make it smoother.

local board = workspace.Board --Just a part
local player = game.Players.LocalPlayer
local charactr = player.Character or player.CharacterAdded:Wait()

game["Run Service"].RenderStepped:Connect(function()
	local hip = charactr.HumanoidRootPart.CFrame  hip = hip + hip.UpVector * -0.8
	local hand = charactr.RightHand.CFrame
	
	
	local offsetFromBody = hip.RightVector * 1
	
	local boardPos = hip + offsetFromBody
	
	--local rotation = CFrame.new(Vector3.new(), hand.p - boardPos.p) --If you want the board to always face directly to the hand if it is not in line with hip to hand
	local rotation = CFrame.new(Vector3.new(), hand.p - hip.p) --Keep the hit to hand orientation even after offsetting the board
	
	rotation = rotation * CFrame.Angles(math.rad(-90),math.rad(90),0) --rotate so the correct face is facing the hand
	
	board.CFrame = rotation + boardPos.p
end)

Wait, so if they require local positions, and I was using global positions this entire time… Does that mean that I was doing it wrong this entire time!?

If the solution is that simple I guess I really didn’t know what I was doing…

Yes, C0 and C1 are local offset from the parts they are attached to. In addition to this they are sorta inverted from each other because they are determine how the ‘pivot’ point attaches to Part0 and Part1. So, an offset of Vector3.new(1,0,0) on C0 might make part1 be offset one stud to the right, but the same offset on C1 will make it move left. This is why I think it is simpler to understand CFrames in global space and calculate the global position from your offset and direction.

So how would I go about making this pivot point be automated for a script for example? Would it just be moving the two joints constantly to make it seem like it’s rotating to face the hand or would it be more complex than that? And even then, how would I translate that to a local position?

Suddenly even though I don’t fully understand this topic, I’ve suddenly become a lot more interested in it!

Check out the CFrame Math Operations link but skip the matrix stuff. I think it does a good job at explaining offsets and C0/C1.

Also, know that the order of operations matters. If you rotate then move along an axis, the part will move like on a polar graph. If you move then rotate, the part will move as you would expect and then be rotated at that position.

Using C0 and C1 is not necessarily more complex, just have to think about it a little different because it is local space. You want the board positioned on the hip and face the hand? Set the pivot point there by setting C0 as the offset between that point and Part0. For your case it would be something like CFrame.new(1,-1,0). If this is all the complexity you want (meaning the pivot point is exactly on Part0 or Part1) then you can apply the rotation to rotate to the hand on either C0 or C1.

Lets say you wanted Part 1 to orbit a position. You can use C1 to set the radius of the orbit (offset it from the pivot point), and use c1 to rotate the pivot point, or offset the pivot point and rotate it. Whatever kind of movement you want can be achieved by the order of translations and rotations. The same goes for global cframes. The only time you need to use both C0 and C1 is if you want to animate an arcing movement simply by changing one number in the rotation instead of having a complicated calculation.

Would that also mean that in terms of rotation, a local rotation and a global rotation would exist?

ALRIGHT, I SOLVED THE PROBLEM!

So it turns out, I was doing EVERYTHING wrong for the rotation!
What I was using was math.asin(), as it seemed to work for the y axis, what I NEEDED however…

Was math.atan2(), as I needed to use both the X, and Z axis’s. And even though I didn’t understand what math.atan2() does, it makes it work perfectly now.
For reference, and to prevent everyone else from suffering, here’s the important part of the code:

runService.RenderStepped:Connect(function()
	local reference = CFrame.new(0,0,0,1,0,0,0,1,0,0,0,1)
	if running == true then
		local look = CFrame.new(Vector3.new(char.HumanoidRootPart.Position.X,char.HumanoidRootPart.Position.Y,char.HumanoidRootPart.Position.Z) + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,0,0)), char.RightHand.Position + char.HumanoidRootPart.CFrame:VectorToWorldSpace(Vector3.new(0,1,0)) )
		local dist = (Vector3.new(0,char.HumanoidRootPart.Position.y - 1,0) - Vector3.new(0,char.RightHand.Position.y,0)).Magnitude
		local tangant = math.atan2(look.LookVector.X,look.LookVector.Z) * 1
		local tangant2 = math.atan2(char.HumanoidRootPart.CFrame.RightVector.X,char.HumanoidRootPart.CFrame.RightVector.Z) * 1
		local theta = tangant - tangant2
		motor6d.C1 = CFrame.new(0,0,dist) * CFrame.fromEulerAnglesXYZ(math.asin(-look.LookVector.Y) * 0.2,0,theta)
	end
end)

I also took reference to this: How do i make the player's head move with the camera

Although, I probably would also like to ask what the hell math.atan2() does, as I found the initial explanation a bit confusing…

atan(y / x) = theta
-y/-x = y/x
-y/x = y/-x
atan2(y, x) handles this by taking y and x separately

Ah, I see.
I’ll try my best to keep this in mind in the future, thank you!