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