Two handed VR grabbing?

So I’m creating a very basic VR system using Nexus VR as a base. I’ve gotten the basics of grabbing objects down, using AlignPositions and AlignOrientations to allow the object to move towards the hand but still be both unable to go through walls and able to be thrown.

I’d like to implement a system where an object can be held with two hands, using a second attachment for second hand placement. My idea was to apply two different AlignPositions and disable the AlignOrientations, so the attachments would move to each hand without disrupting the other’s by trying to align rotation. However, it doesn’t seem to work like this, and instead the object just gets stuck in the first hand and then flops over as if it was a BallSocket. So how would I go about moving the part in such a way that both hands are attached?

The following script has a lot of parts, so I’ll spoiler it to not flood the post too much.

GrabScript
local plr = game.Players.LocalPlayer
local char = plr.Character
local handhitbox = game.ReplicatedStorage.VR.HandHitbox

local lefthand
local righthand
local leftweld
local rightweld
local leftweldrot
local rightweldrot

local runServ = game:GetService("RunService")
local contextServ = game:GetService("ContextActionService")

function init()
	-- Setup the Hand Hitboxes
	lefthand = handhitbox:Clone()
	lefthand.Name = 'LHandHitbox'
	lefthand.Parent = char
	righthand = handhitbox:Clone()
	righthand.Name = 'RHandHitbox'
	righthand.Parent = char
	-- Now setup welds used for grabbin.
	leftweld = Instance.new("AlignPosition", char:FindFirstChild('LeftHand').LeftGripAttachment)
	leftweld.Attachment1 = leftweld.Parent
	leftweld.Responsiveness = 50
	leftweldrot = Instance.new("AlignOrientation", char:FindFirstChild('LeftHand').LeftGripAttachment)
	leftweldrot.Attachment1 = leftweld.Parent
	leftweldrot.Responsiveness = 50
	rightweld = Instance.new("AlignPosition", char:FindFirstChild('RightHand').RightGripAttachment)
	rightweld.Attachment1 = rightweld.Parent
	rightweld.Responsiveness = 50
	rightweldrot = Instance.new("AlignOrientation", char:FindFirstChild('RightHand').RightGripAttachment)
	rightweldrot.Attachment1 = rightweld.Parent
	rightweldrot.Responsiveness = 50
	
	
	-- Roblox jank fix
	righthand.Touched:Connect(function() end)
	lefthand.Touched:Connect(function() end)
end

runServ.RenderStepped:Connect(function()
	-- Update Hand Position
	local lhandpos = char:FindFirstChild("LeftHand").CFrame
	local rhandpos = char:FindFirstChild("RightHand").CFrame
	lefthand.CFrame = lhandpos
	righthand.CFrame = rhandpos
end)

function grabbity(hand)
	local weld
	local rotweld
	local otherweld
	local otherrotweld
	-- Incredibly unreadable because i like compactness >_<
	if hand == righthand then rotweld = rightweldrot weld = rightweld otherweld = leftweld otherrotweld = leftweldrot 
	else weld = leftweld rotweld = leftweldrot otherweld = rightweld otherrotweld = rightweldrot end
	-- Find the stuff touching the hand hitbox
	for i, v in pairs(hand:GetTouchingParts()) do
		if v:FindFirstChild('GrabObj') then
			local grabclass = require(v:FindFirstChild('GrabObj'))
			local target = grabclass[1]
			if otherweld.Attachment0 and otherweld.Attachment0.Parent == v and otherweld.Attachment0 ~= grabclass[2]  then
				-- Change to the second weld if the first one is taken by the other hand.
				target = grabclass[2]
				rotweld.Enabled = false
				otherrotweld.Enabled = false
			end
			-- Weld it up, then break so that you dont loop through parts unnescesarily (dont worry i can speling)
			weld.Attachment0 = target or v:FindFirstChild('Grab')
			rotweld.Attachment0 = target or v:FindFirstChild('Grab')
			break
		end
	end
end

function release(hand)
	local weld
	local rotweld
	local otherweld
	local otherrotweld
	-- Incredibly unreadable because i like compactness >_<
	if hand == righthand then rotweld = rightweldrot weld = rightweld otherweld = leftweld otherrotweld = leftweldrot 
	else weld = leftweld rotweld = leftweldrot otherweld = rightweld otherrotweld = rightweldrot end
	
	weld.Attachment0 = nil
	rotweld.Attachment0 = nil
	rotweld.Enabled = true
	otherrotweld.Enabled = true
end

local function grab(str, inputState, inputObj)
	-- Check input
	if inputState == Enum.UserInputState.Begin then
		if inputObj.KeyCode == Enum.KeyCode.ButtonR1 then
			grabbity(righthand)
		elseif inputObj.KeyCode == Enum.KeyCode.ButtonL1 then
			grabbity(lefthand)
		end
	elseif inputState == Enum.UserInputState.End then
		if inputObj.KeyCode == Enum.KeyCode.ButtonR1 then
			release(righthand)
		elseif inputObj.KeyCode == Enum.KeyCode.ButtonL1 then
			release(lefthand)
		end
	end
end

contextServ:BindAction("GrabL", grab, false, Enum.KeyCode.ButtonL1, Enum.KeyCode.ButtonR1)



init()
1 Like

Bumping this post a bit because I still haven’t found a solution.

What seems to work for me is to use a Weld Constraint. Weld both your physics hands to the object. Make sure the physics hands are using an align position or a body position. It’ll even have weighted grabbing so if an object is too heavy to grab with one hand then if you grab it with two hands you can actually lift it.

2 Likes

This is an interesting approach. I’ll be sure to see how this works!