Help with viewmodel wall detection

I recently tried creating the effect of the Viewmodel arms moving back to avoid clipping into the wall. It almost works fine but the arms sort of flicker when close to a wall. I know this is because the code uses the arm and gun muzzle’s position to calculate the arm length and distance from a wall and then modifies the arm’s position, which in turn changes the distance, causing a loop. Is there a way of doing this without causing the loop?

Code:

game:GetService("RunService"):BindToRenderStep("MoveArms", Enum.RenderPriority.Character.Value, function()
		--Creates Cframe which the Position of the VM arm's Position looking in the same direction as the muzzle
		local armPos = CFrame.lookAlong(vm.RightUpperArm.RightShoulderAttachment.WorldCFrame.Position, script.Parent.Handle.MuzzleExit.WorldCFrame.LookVector)
		--Creates Cframe which the local offset between the arm and muzzle
		local offset = armPos:ToObjectSpace(script.Parent.Handle.MuzzleExit.WorldCFrame)
		
		local params = RaycastParams.new()
		params.FilterType = Enum.RaycastFilterType.Exclude
		params.FilterDescendantsInstances = {vm, script.Parent.Parent}
		--casts a ray from the arm to get the distance from a wall
		local ray2 = workspace:Raycast((armPos * CFrame.new(offset.X, 0, 0)).Position, script.Parent.Handle.MuzzleExit.WorldCFrame.LookVector.Unit * 10, params)
		
		if ray2 then
			--Distance between the wall and gun muzzle
			local diff = ray2.Distance + offset.Z
			
			--if the difference is negative it means the muzzle is inside the wall
			---moves arms back the same distance to move arms out of wall
			if diff < 0 then
				print(diff)
				
				local rightCO = vm.RightUpperArm.RightShoulder
				local leftCO = vm.LeftUpperArm.LeftShoulder
				
				leftCO.C0 = CFrame.new(leftCO.C0.X, leftCO.C0.Y, 0.0019 - diff) * leftCO.C0.Rotation 
				rightCO.C0 = CFrame.new(rightCO.C0.X, rightCO.C0.Y, 0.0019 - diff) * rightCO.C0.Rotation 
			end
		end
	end)

Video:

I swear. devforum members ONLY HELP for basic things.

1 Like

To solve this issue in the script, you would either have to add the old difference to the Raycast or subtract the old difference when calculating the new difference. I also noticed that the arms wouldn’t change position if there was no ray intersection, so I added a little check at the end to fix this issue. I hope I wasn’t too late with the solution.

local diff = 0

local rightCO = vm.RightUpperArm.RightShoulder
local leftCO = vm.LeftUpperArm.LeftShoulder

local function ChangeArmsC0(Difference)
	leftCO.C0 = CFrame.new(leftCO.C0.X, leftCO.C0.Y, 0.0019 - Difference) * leftCO.C0.Rotation 
	rightCO.C0 = CFrame.new(rightCO.C0.X, rightCO.C0.Y, 0.0019 - Difference) * rightCO.C0.Rotation 
end

game:GetService("RunService"):BindToRenderStep("MoveArms", Enum.RenderPriority.Character.Value, function()
	--Creates Cframe which the Position of the VM arm's Position looking in the same direction as the muzzle
	local armPos = CFrame.lookAlong(vm.RightUpperArm.RightShoulderAttachment.WorldCFrame.Position, script.Parent.Handle.MuzzleExit.WorldCFrame.LookVector)
	--Creates Cframe which the local offset between the arm and muzzle
	local offset = armPos:ToObjectSpace(script.Parent.Handle.MuzzleExit.WorldCFrame)
	
	local params = RaycastParams.new()
	params.FilterType = Enum.RaycastFilterType.Exclude
	params.FilterDescendantsInstances = {vm, script.Parent.Parent}
	--casts a ray from the arm to get the distance from a wall
	local ray2 = workspace:Raycast((armPos * CFrame.new(offset.X, 0, 0)).Position, script.Parent.Handle.MuzzleExit.WorldCFrame.LookVector.Unit * 10, params)
	
	if ray2 then
		-- We add this offset to avoid the looping that occurs
		local ArmZOffset = if diff < 0 then diff else 0 
		--Distance between the wall and gun muzzle
		diff = ray2.Distance + ArmZOffset + offset.Z 
		
		--if the difference is negative it means the muzzle is inside the wall
		---moves arms back the same distance to move arms out of wall
		if diff < 0 then
			print(diff)
			
			ChangeArmsC0(diff)
		end
	elseif diff ~= 0 then
		diff = 0

		ChangeArmsC0(0)
	end
end)
1 Like

i dont even work on the project no more, and also i did end up figuring it out using what you said. I used the rig attachments for the right shoulder and the gun muzzle to get the normal arm length, then shot a cast from that same shoulder position and subtracting it from the normal to get the difference and working from there. Thanks for the help tho ill mark it as a solution since it does fix it.

1 Like

So real, i have so many very specific posts for actual hard problems that have no replies at all. luckily for most of them i do end up figuring it out myself after a couple of days to a couple of weeks down the line and marking them as a solution incase some poor soul also runs into it.

1 Like

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