Tool randomly disappears with viewmodel script

A viewmodel is needed for a fps game. The desired result is that the tool must be in the hands of the viewmodel once the player is in first person.

The issue is that the tool keeps randomly dissapearing every time the tool is welded to the viewmodel. The tool is completely deleted without a trace, destroying event doesn’t fire.

No input



After staying in first person for a few seconds


The issue also occurs if zooming in and out, so the Part0 of the RightGrip is changed.

Here is a video demonstrating the bug:

Tricks I have tried that but didn’t work:

  1. Parenting the viewmodel to the workspace instead of the camera.
  2. Using a weldconstraint.
  3. Destroy event in the tool’s script with a warn(), did not output anything.

A trick that Did work is removing the localscript of the ak-47 whilst playtesting locally. However this is very unusual and the ak-47 doesn’t have anything that can influence this bug as has been tested with commenting some lines of code.

If anyone has any idea or rather an alternative please comment. Help is really appreciated!

Viewmodels script

local RunService = game:GetService("RunService")
local PhysicsService = game:GetService("PhysicsService")
local REP = game:GetService("ReplicatedStorage")
local Camera = game:GetService("Workspace").CurrentCamera
if Camera:FindFirstChild("ViewModel") then
	Camera.ViewModel:Destroy()
end
local player = game.Players.LocalPlayer;
local character = script.Parent
local Animator = character:WaitForChild("Humanoid"):WaitForChild("Animator")
character.Archivable = true
local Head = character:WaitForChild("Head")
character:WaitForChild("Left Arm")
local RightArm = character:WaitForChild("Right Arm")
local humrp = character:WaitForChild("HumanoidRootPart")
character:WaitForChild("Torso")
local ViewModel = character:Clone()
print(ViewModel)
ViewModel.Parent = Camera
ViewModel.Name = "ViewModel"
character.Archivable = false
local ViewModelAnimator = ViewModel:WaitForChild("Humanoid"):WaitForChild("Animator")
ViewModel.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None 
ViewModel.Humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOff
ViewModel.Humanoid.BreakJointsOnDeath = false
ViewModel.PrimaryPart = ViewModel:WaitForChild("Head")
ViewModel.PrimaryPart.Anchored = true
ViewModel:SetPrimaryPartCFrame(CFrame.new(0, 5, 10))
for _, v in pairs(ViewModel:GetDescendants()) do
	if v:IsA("BasePart") then
		v.Massless = true
		v.CastShadow = false
		v:AddTag("ForceCanCollide_OFF")
		local lowername = v.Name:lower()
		if lowername:match("leg") or lowername:match("foot") then
			v:Destroy()
		elseif not(lowername:match("arm") or lowername:match("hand")) then
			v.Transparency = 1
		end
	elseif v:IsA("Decal") then
		v:Destroy()
	elseif v:IsA("Accessory") then
		v:Destroy()
	elseif v:IsA("LocalScript") then
		v:Destroy()
	end
	
end

local LoadedAnimations = {}
Animator.AnimationPlayed:Connect(function(AnimationTrack)
	if AnimationTrack:GetAttribute("ViewModelAnimation") ~= true then return end -- // Skip animation if it isn't supposed to play on ViewModel.
	if LoadedAnimations[AnimationTrack] == nil then -- // Indexing using the animation track.
		-- // If this animation was not already laoded then load it.
		LoadedAnimations[AnimationTrack] = ViewModelAnimator:LoadAnimation(AnimationTrack.Animation) -- // Load animation on the ViewModel.
		LoadedAnimations[AnimationTrack].Priority = AnimationTrack.Priority
	end
end)
local function RenderStepped()
	--update viewmodel position
	ViewModel:SetPrimaryPartCFrame(Camera.CFrame)
	local isfirstperson = (Head.CFrame.Position - Camera.CFrame.Position).Magnitude < 0.65 ;
	for _, part in pairs({ViewModel:FindFirstChild("Left Arm"), ViewModel:FindFirstChild("Left Arm"):GetDescendants(), ViewModel:FindFirstChild("Right Arm"), ViewModel:FindFirstChild("Right Arm"):GetDescendants()}) do
		if part.ClassName == "BasePart" or part.ClassName == "Part" or part.ClassName == "MeshPart" then
			part.Transparency = if isfirstperson then 0 else 1
		end
	end
	local RightGrip = RightArm:FindFirstChild("RightGrip") or ViewModel:WaitForChild("Right Arm"):FindFirstChild("RightGrip")
	if(RightGrip) then
		RightGrip.Parent = if isfirstperson then ViewModel:WaitForChild("Right Arm") else RightArm
		RightGrip.Part0 = if isfirstperson then ViewModel["Right Arm"] else character["Right Arm"]
	end
	
	--update shirt and body colors
	local ViewModelShirt = ViewModel:FindFirstChildWhichIsA("Shirt") or Instance.new("Shirt", ViewModel) -- // Create a shirt if there is no shirt found.
	local CharacterShirt = character:FindFirstChildWhichIsA("Shirt")
	if CharacterShirt then
		-- // If a shirt was found in the player's character, then set the ViewModel's shirt to the same shirt.
		ViewModelShirt.ShirtTemplate = CharacterShirt.ShirtTemplate
	end

	for _, Part in pairs(ViewModel:GetChildren()) do
		if Part:IsA("BasePart") then
			-- // Set the color of each part of the ViewModel to the color of the part with same name in the character.
			Part.Color = character[Part.Name].Color
		end
	end
	--update animations
	for CharAnim, Anim in pairs(LoadedAnimations) do
		if CharAnim.IsPlaying ~= Anim.IsPlaying then
			if CharAnim.IsPlaying then
				Anim:Play()
			else
				Anim:Stop()
			end
		end

		Anim.TimePosition = CharAnim.TimePosition
		Anim:AdjustWeight(CharAnim.WeightCurrent, 0) -- // 0 Fade time so it's instantly set.
	end
end

RunService.RenderStepped:Connect(RenderStepped)

If the destroying event doesn’t fire, I wonder if it’s somehow being adjusted to NaN bounds. Sometimes if you don’t account for certain edge cases you can end up w/ unexpected behavior such as this. If disabling the AK script fixed the issue, then there could be an edge case that causes it.

Hey, nothing has been found in the Ak-47 localscript. The problem persists even when disabling the shooting. The code is basically just events that do little work:

local function InputEnded(input, processed)
	if input ~= userInput then return end
	userInput = nil
	if connectionInputEnded and connectionInputEnded.Connected then
		connectionInputEnded:Disconnect()
	end
	connnectionLoopBool = false
end

local function InputBegan(input, processed)
	if userInput ~= nil then return end
	if processed == true then return end
	if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
	userInput = input
	connectionInputEnded = UIS.InputEnded:Connect(InputEnded)
	connnectionLoopBool = true
	--while connnectionLoopBool do
	--	OnShoot()
	--	task.wait()
	--end
end

tool.Equipped:Connect(function()
	AnimationTrack:Play()
	AmmoText.Parent.Visible = true
	AmmoText.Parent.Interactable = true
	if reloading then
		AmmoText.Text = "RELOADING"
	else
		AmmoText.Text = tostring(magazine) .. " | " .. tostring(MAGAZINE_MAX)
	end
	connectionInputBegan = UIS.InputBegan:Connect(InputBegan)
	ContextActionService:BindAction("Reload", Reload, false, Enum.KeyCode.R)
	mouse.Icon = "rbxassetid://316279304"
end)

tool.Unequipped:Connect(function()
	if connectionInputBegan and connectionInputBegan.Connected then
		connectionInputBegan:Disconnect()
	end
	if connectionInputEnded and connectionInputEnded.Connected then
		connectionInputEnded:Disconnect()
	end
	userInput = nil
	connnectionLoopBool = false
	AmmoText.Parent.Visible = false
	AmmoText.Parent.Interactable = false
	AnimationTrack:Stop()
	mouse.Icon = defaultMouseIcon
end)

It might have to do with the RightGrip’s property called Part0 being changed, which just breaks the internal code of Roblox. However I am ignorant of how to make the tool welded to the viewmodel instead of the character, in a different way.

It could be tbh… I haven’t really done any FPS games so I probably can’t be of any help in regards to this. If this is a local script, it’s possible the client is making these changes and the server is taking an authoritative step and somehow breaking it. Is your tool completely gone in workspace when this happens?

This is anchored, right? And it exists only on the client?

Yes it exists only on the client.
No the Right Arm is not anchored, however the Torso still exists so it shouldn’t fall.

What has been tried so far in the project is:

Creating the viewmodel on the server. It works, however because the viewmodel is in the server it causes allot of issues with animation, as well as how the other players see the player’s arms. So Viewmodel on the server is abandoned.

And the tool itself is also just on the client? If it’s not, and the server doesn’t weld it to anything, it probably falls into the void.

The solution has been found. Thanks everybody for wanting to help!

The solution is:

  1. create the Viewmodel on the server
  2. create your own weld (not a weldconstraint) that replaces RightGrip but has the same function.
  3. delete the RightGrip on the server every RenderStepped
  4. All animations on the client

Feel free to copy-paste!

Here is the client script:

local RunService = game:GetService("RunService")
local PhysicsService = game:GetService("PhysicsService")
local REP = game:GetService("ReplicatedStorage")
local Camera = game:GetService("Workspace").CurrentCamera
local player = game.Players.LocalPlayer;
local character = script.Parent
local Animator = character:WaitForChild("Humanoid"):WaitForChild("Animator")
local Head = character:WaitForChild("Head")
character:WaitForChild("Left Arm")
local RightArm = character:WaitForChild("Right Arm")
local humrp = character:WaitForChild("HumanoidRootPart")
character:WaitForChild("Torso")

local Animator = character:WaitForChild("Humanoid"):WaitForChild("Animator")
--prepare character for being cloned
character.Archivable = true
local Head = character:WaitForChild("Head")
character:WaitForChild("Left Arm")
local RightArm = character:WaitForChild("Right Arm")
local humrp = character:WaitForChild("HumanoidRootPart")
local torso = character:WaitForChild("Torso")
--create and initialize viewmodel
local ViewModel = character:Clone()
ViewModel.Parent = workspace.Camera
ViewModel.Name = "ViewModel"
character.Archivable = false
local ViewModelAnimator = ViewModel:WaitForChild("Humanoid"):WaitForChild("Animator")
ViewModel.Humanoid.DisplayDistanceType = Enum.HumanoidDisplayDistanceType.None 
ViewModel.Humanoid.HealthDisplayType = Enum.HumanoidHealthDisplayType.AlwaysOff
ViewModel.Humanoid.BreakJointsOnDeath = false
ViewModel.PrimaryPart = ViewModel:WaitForChild("Head")

ViewModel.PrimaryPart.Anchored = true
ViewModel:SetPrimaryPartCFrame(CFrame.new(0, 5, 10))
ViewModel:WaitForChild("HumanoidRootPart"):WaitForChild("RootJoint"):Destroy()
for _, v in pairs(ViewModel:GetDescendants()) do
	if v:IsDescendantOf(ViewModel:WaitForChild("Head")) then
		v:Destroy()
	end
	if v:IsA("BasePart") or v.ClassName == "MeshPart" then
		v.Anchored = false
		v.Massless = true
		v.CastShadow = false
		v:AddTag("ForceCanCollide_OFF")
		local lowername = v.Name:lower()
		if lowername:match("leg") or lowername:match("foot") then
			v:Destroy()
		elseif not(lowername:match("arm") or lowername:match("hand")) then
			v.Transparency = 1
		end
	elseif v:IsA("Decal") then
		v:Destroy()
	elseif v:IsA("Accessory") then
		v:Destroy()
	elseif v:IsA("LocalScript") then
		v:Destroy()
	end
end
ViewModel:FindFirstChild("Left Arm").Transparency = 1
ViewModel:FindFirstChild("Left Arm").CastShadow = true
ViewModel:FindFirstChild("Right Arm").Transparency = 1
ViewModel:FindFirstChild("Right Arm").CastShadow = true
local ViewModel_MainArm = ViewModel:FindFirstChild("Right Arm")

local ViewModelAnimator = ViewModel:WaitForChild("Humanoid"):WaitForChild("Animator")

local LoadedAnimations = {}
Animator.AnimationPlayed:Connect(function(AnimationTrack)
	--if AnimationTrack.Animation:GetAttribute("ViewModelAnimation") ~= true then return end -- // Skip animation if it isn't supposed to play on ViewModel.
	if LoadedAnimations[AnimationTrack] == nil then -- // Indexing using the animation track.
		-- // If this animation was not already laoded then load it.
		LoadedAnimations[AnimationTrack] = ViewModelAnimator:LoadAnimation(AnimationTrack.Animation) -- // Load animation on the ViewModel.
		LoadedAnimations[AnimationTrack].Priority = AnimationTrack.Priority
	end
end)

RunService.RenderStepped:Connect(function()
	--update viewmodel positionv
	local cameradirection = humrp.CFrame:toObjectSpace(Camera.CFrame).LookVector
	local cf = CFrame.new(Camera.CFrame.Position) * torso:WaitForChild('Neck').C0 * CFrame.Angles(-math.asin(cameradirection.Y), 0, -math.asin(cameradirection.X))
	ViewModel:SetPrimaryPartCFrame(Camera.CFrame)
	
	--update transparency
	local isfirstperson = (Head.CFrame.Position - Camera.CFrame.Position).Magnitude < 0.80 ;
	for _, part in pairs({ViewModel:WaitForChild("Left Arm"), ViewModel:WaitForChild("Left Arm"):GetDescendants(), ViewModel:WaitForChild("Right Arm"), ViewModel:WaitForChild("Right Arm"):GetDescendants()}) do
		if part.ClassName == "BasePart" or part.ClassName == "Part" or part.ClassName == "MeshPart" then
			part.Transparency = if isfirstperson then 0 else 1
		end
	end
	if isfirstperson then
		for _, part in pairs(character:GetChildren()) do
			if part.ClassName == "BasePart" or part.ClassName == "Part" or part.ClassName == "MeshPart" then
				part.CastShadow = true
			end
		end
	end
	local RightGrip = character:WaitForChild("Right Arm"):FindFirstChild("RightGrip")
	if RightGrip and RightGrip.Part1 and RightGrip.Part0 == character:WaitForChild("Right Arm") then
		local Grip = character:WaitForChild("Right Arm"):FindFirstChild('Grip') 
		if Grip == nil then
			Grip = Instance.new("Weld")
			Grip.Name = 'Grip'
			Grip.Parent = character:WaitForChild("Right Arm")
		end
		Grip.Part1 = RightGrip.Part1
		Grip.Part0 = ViewModel_MainArm
		Grip.C0 = RightGrip.C0
		Grip.C1 = RightGrip.C1
		game:GetService("ReplicatedStorage").ServerScripts.Test.RemoteEvent:FireServer()
	end
	--update shirt and body colors
	local ViewModelShirt = ViewModel:FindFirstChildWhichIsA("Shirt") or Instance.new("Shirt", ViewModel) -- // Create a shirt if there is no shirt found.
	local CharacterShirt = character:FindFirstChildWhichIsA("Shirt")
	if CharacterShirt then
		ViewModelShirt.ShirtTemplate = CharacterShirt.ShirtTemplate
	end
	for _, Part in pairs(ViewModel:GetChildren()) do
		if Part:IsA("BasePart") then
			
			Part.Color = character[Part.Name].Color
		end
	end
	
	--Play loaded animations
	for CharAnim, Anim in pairs(LoadedAnimations) do
		if CharAnim.IsPlaying ~= Anim.IsPlaying then
			if CharAnim.IsPlaying then
				Anim:Play()
			else
				Anim:Stop()
			end
		end

		Anim.TimePosition = CharAnim.TimePosition 
		Anim:AdjustWeight(CharAnim.WeightCurrent, 0) -- // 0 Fade time so it's instantly set.
	end
end)

On the server:

script.RemoteEvent.OnServerEvent:Connect(function(player)
	if player == nil or player.Parent == nil then return end
	local char = player.character
	if char == nil or char.Parent == nil then return end
	local RightArm = char["Right Arm"]
	if RightArm == nil or RightArm.Parent == nil then return end
	local RightGrip = RightArm["RightGrip"]
	if RightGrip == nil or RightGrip.Parent == nil then return end
	RightGrip.Enabled = false
end)

If you are wondering what the ForceCanCollide_OFF tag does, there is a separate client script called “collisions” with this code inside it:

local RunService = game:GetService("RunService")
local CS = game:GetService("CollectionService")
RunService.PreSimulation:Connect(function()
	local Parts = CS:GetTagged("ForceCanCollide_OFF")
	for _, part in pairs(Parts) do
		if part.ClassName == "Part" or part.ClassName == "MeshPart" then 
			part.CanCollide = false
		end
	end
end)

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