VR Arms acting strange from rotations. Motor6D Hell and it's consequences

Hello! I am currently creating a game that I would be adding on lots of support for XBOX, VR and Mobile.

I got VR a month ago and I have been having a lot of fun, but since I realized that there’s VR in ROBLOX nowadays, I been trying coding mostly the stuff myself, but I am currently facing with concurrent issues to say atleast.

Screenshots of the issue:
Discord_wDpRloHsXB

Code:

local function VR()
	RunService.PreSimulation:Connect(function()
		local RightTrack = VRService:GetUserCFrame(Enum.UserCFrame.RightHand)
		local LeftTrack = VRService:GetUserCFrame(Enum.UserCFrame.LeftHand)

		local TorsoCFrame = Character.Torso.CFrame
		local RightCFrame = Camera.CFrame * RightTrack
		local LeftCFrame = Camera.CFrame * LeftTrack

		local angles = CFrame.new(0, 0, 0) * CFrame.Angles(0,0,0)
		
		local finalR = TorsoCFrame:ToObjectSpace(RightCFrame * RightArm.C0:Inverse())-- * angles
		local finalL = TorsoCFrame:ToObjectSpace(LeftCFrame * LeftArm.C0:Inverse())-- * angles
		
		RightArm.Transform = finalR
		LeftArm.Transform = finalL

		if time() >= currentTime + timecooldown then
			currentTime = time() + timecooldown
			game.ReplicatedStorage.Remotes.ArmsRotate:FireServer({finalR, finalL}, true)
		end
	end)
end

If there is anything needed included, like Motor6D origins, please do let me know!

1 Like

Your arms flip because you feed camera-space controller CFrames into Motor6D.Transform and you ignore C1. GetUserCFrame returns a pose offset in user space. Convert to world space and use the joint equation. GetUserCFrame

Keeps your structure. Adds world-space conversion and the correct Transform math.

local cam        = workspace.CurrentCamera
local vrs        = game:GetService("VRService")
local RunService = game:GetService("RunService")

local RIGHT_SHOULDER_OFFSET = CFrame.new(0, -0.25, 0) * CFrame.Angles(math.rad(-40), 0, 0)
local LEFT_SHOULDER_OFFSET  = CFrame.new(0, -0.25, 0) * CFrame.Angles(math.rad(-40), 0, 0)

local function solveTransform(motor: Motor6D, part0World: CFrame, desiredPart1World: CFrame): CFrame
	
	return motor.C0:Inverse() * part0World:Inverse() * desiredPart1World * motor.C1
end

local function VR()
	RunService.PreSimulation:Connect(function()
		if not vrs.VREnabled then return end

		local rightHandWorld = cam.CFrame * vrs:GetUserCFrame(Enum.UserCFrame.RightHand)
		local leftHandWorld  = cam.CFrame * vrs:GetUserCFrame(Enum.UserCFrame.LeftHand)

		local torso = Character:FindFirstChild("UpperTorso") or Character:FindFirstChild("Torso")
		if not torso then return end
		local torsoCF = torso.CFrame

		local desiredRightUpperArm = rightHandWorld * RIGHT_SHOULDER_OFFSET
		local desiredLeftUpperArm  = leftHandWorld  * LEFT_SHOULDER_OFFSET

		local finalR = solveTransform(RightArm, torsoCF, desiredRightUpperArm)
		local finalL = solveTransform(LeftArm,  torsoCF, desiredLeftUpperArm)

		RightArm.Transform = finalR
		LeftArm.Transform  = finalL

		if time() >= currentTime + timecooldown then
			currentTime = time() + timecooldown
			game.ReplicatedStorage.Remotes.ArmsRotate:FireServer({finalR, finalL}, true)
		end
	end)
end

GetUserCFrame is not world space. Multiply by Camera.CFrame to place the device in the world. GetUserCFrame

Correct joint formula: Transform = C0⁻¹ * Part0⁻¹ * DesiredPart1World * C1. You were missing C1. Roblox also notes Motor6D.Transform is often overwritten by the Animator if you don’t update at the right time. Motor6D

  • Write on RunService.PreSimulation so the Animator does not stomp your values. task-scheduler
  • Guard with VRService.VREnabled. VREnabled

If shoulders still wobble

Use IKControl with a target part per hand. It solves shoulder+elbow+wrist, and you can add a Pole to control elbow bend. IKControl

I forgot that forums existed, but I was able to find my own by setting all C0’s into CFrame.new(), somehow was able to patch this. I would look into more what you said on here!

1 Like

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