Multiple Player Viewmodels overlapping on all clients

I am making a horror game and got to the part where I have to make a flashlight. I have a view model where once the player presses F, the right arm comes up while holding a flashlight that is on. Pressing F again brings the left arm up to pull the right arm down off screen while turning off the flashlight.

I am having a problem where once the view model runs an animation, you can see other player’s arms overlapping yours as well. It is supposed to be only the client players’ arms. It shows up for every players clients.

Can I get help making it so ONLY the player’s arms appear and not other’s arms? (This is a model I found on the toolbox and i am trying to fix the bugs to make it better.)

Here is the model i used from the toolbox:

I should also mention, that whenever a new player joins the server the animation on line 76 plays for all clients again, if there is a way to stop this please let me know.

Here is the Explorer of the necessary stuff:
image

image

image

SpringModule:

-- Constants

local ITERATIONS	= 8

-- Module

local SPRING	= {}

-- Functions 

function SPRING.new(self, mass, force, damping, speed)

	local spring	= {
		Target		= Vector3.new();
		Position	= Vector3.new();
		Velocity	= Vector3.new();

		Mass		= mass or 5;
		Force		= force or 50;
		Damping		= damping or 4;
		Speed		= speed  or 4;
	}

	function spring.getstats(self)
		return self.Mass, self.Force, self.Damping, self.Speed
	end

	function spring.changestats(self, mass, force, damping, speed)
		self.Mass = mass or self.Mass
		self.Force = force or self.Force
		self.Damping = damping or self.Damping
		self.Speed = speed or self.Speed
	end

	function spring.shove(self, force)
		local x, y, z	= force.X, force.Y, force.Z
		if x ~= x or x == math.huge or x == -math.huge then
			x	= 0
		end
		if y ~= y or y == math.huge or y == -math.huge then
			y	= 0
		end
		if z ~= z or z == math.huge or z == -math.huge then
			z	= 0
		end
		self.Velocity	= self.Velocity + Vector3.new(x, y, z)
	end

	function spring.update(self, dt)
		local scaledDeltaTime = dt * self.Speed / ITERATIONS
		for i = 1, ITERATIONS do
			local iterationForce= self.Target - self.Position
			local acceleration	= (iterationForce * self.Force) / self.Mass

			acceleration		= acceleration - self.Velocity * self.Damping

			self.Velocity	= self.Velocity + acceleration * scaledDeltaTime
			self.Position	= self.Position + self.Velocity * scaledDeltaTime
		end

		return self.Position
	end

	return spring
end

-- Return

return SPRING

FlashlightClient

local userinputservice = game:GetService("UserInputService")
local flashlight = false
local char = script.Parent
local humanoid = char:FindFirstChildWhichIsA("Humanoid")
local Player = game.Players.LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Camera = workspace.Camera
local RunService = game:GetService("RunService")
local flashbutton = Player.PlayerGui.fleshbleight.ImageButton

if userinputservice.TouchEnabled then
	flashbutton.Parent.Enabled = true
end

local ViewModel = game.ReplicatedStorage.viewmodel:Clone()

ViewModel.Parent = Camera

local springmodule = require(game.ReplicatedStorage.SpringModule)

local debounce = false
local debounceTime = 1 -- 1 second debounce time

local viewmodelflashlight = ViewModel.Flashlight
local viewmodelidle = ViewModel.Animation:LoadAnimation(game.ReplicatedStorage.Flashlight.ViewmodelUp)
local viewmodeldown = ViewModel.Animation:LoadAnimation(game.ReplicatedStorage.Flashlight.ViewmodelDown)
local viewmodelequip = ViewModel.Animation:LoadAnimation(game.ReplicatedStorage.Flashlight.Viewmodelequip)
local viewmodelunequip = ViewModel.Animation:LoadAnimation(game.ReplicatedStorage.Flashlight.ViewmodelUnequip)
local viewmodelrun = ViewModel.Animation:LoadAnimation(game.ReplicatedStorage.Flashlight.ViewmodelWalk)

local function Bob(addition)
	return math.sin(tick() * addition * 1.3) * 0.5
end

local swayspring = springmodule.new()
local bobspring = springmodule.new()

RunService.RenderStepped:Connect(function(dt)
	local distance = (Player.Character.Head.Position - workspace.CurrentCamera.CFrame.Position).Magnitude
	if distance < 1.5 and ViewModel then
      ViewModel.Parent = workspace.CurrentCamera
	else
		ViewModel.Parent = game.Lighting
	end
	if Player.Character.Humanoid.Health == 0 then
		if ViewModel ~= nil then
			ViewModel:Destroy()
		end
	end
	local Delta = game.UserInputService:GetMouseDelta()
	
	swayspring:shove(Vector3.new(-Delta.X/500, Delta.Y/500, 0))
	bobspring:shove(Vector3.new(Bob(5), Bob(10), Bob(5)) / 10 * (Character.PrimaryPart.Velocity.Magnitude) / 10)
	
	local UpdatedSway = swayspring:update(dt)
	local UpdatedBob = bobspring:update(dt)
	if ViewModel ~= nil then
		ViewModel:PivotTo(Camera.CFrame * CFrame.new(UpdatedSway.X, UpdatedSway.Y, 0) *
			CFrame.new(UpdatedBob.X, UpdatedBob.Y, 0) )
	end
	--character shirt and color
	if char:FindFirstChildWhichIsA("Shirt") then
		ViewModel["Left Arm"].Decal.Texture = char:FindFirstChildWhichIsA("Shirt").ShirtTemplate
		ViewModel["Right Arm"].Decal.Texture = char:FindFirstChildWhichIsA("Shirt").ShirtTemplate
	end
	if char:FindFirstChild("Left Arm") then
		ViewModel["Left Arm"].Color = char:FindFirstChild("Left Arm").Color
	end
	if char:FindFirstChild("Right Arm") then
		ViewModel["Right Arm"].Color = char:FindFirstChild("Right Arm").Color
	end
	--shirt and color end

end)

viewmodeldown:Play()
viewmodelflashlight.Light.Light.Enabled = false
viewmodelflashlight.Front.SurfaceLight.Enabled = false
viewmodelflashlight.Light.Shadow.Enabled = false

wait(5) --wait period so player wont press F before animations load in, causing overlapping of anims
userinputservice.InputBegan:Connect(function(input,istyping)--open
	if istyping then return end
	if input.KeyCode == Enum.KeyCode.F and not debounce then
		debounce = true
		if flashlight == false then
			script.Toggle:FireServer("on")--if no value it means its closing the flashlight
			flashlight = true
			local lib = char:WaitForChild("Flashlight")
			if lib then
				lib.Light.Light.Enabled = false
				lib.Front.SurfaceLight.Enabled = false
				lib.Light.Shadow.Enabled = false
				lib.Light.Sound.Volume = 0
				lib.Light.Sound2.Volume = 0
			end
			--viewmodel stuff
			viewmodelflashlight.Light.Sound:Play()
			viewmodelequip:Play()
			viewmodeldown:Stop()
			viewmodelidle:Play()
			viewmodelflashlight.Light.Light.Enabled = true
			viewmodelflashlight.Front.SurfaceLight.Enabled = true
			viewmodelflashlight.Light.Shadow.Enabled = true
		else
			script.Toggle:FireServer()--if no value it means its closing the flashlight
			flashlight = false
			--viewmodelstuff
			viewmodelflashlight.Light.Sound2:Play()
			viewmodelunequip:Play()
			viewmodeldown:Play()
			viewmodelidle:Stop()
			viewmodelflashlight.Light.Light.Enabled = false
			viewmodelflashlight.Front.SurfaceLight.Enabled = false
			viewmodelflashlight.Light.Shadow.Enabled = false
		end
		task.wait(debounceTime)
		debounce = false
	end

end)


flashbutton.MouseButton1Click:Connect(function()
	if not debounce then
		debounce = true
		if flashlight == false then
			script.Toggle:FireServer("on")--if no value it means its closing the flashlight
			flashlight = true
			local lib = char:WaitForChild("Flashlight")
			if lib then
				lib.Light.Light.Enabled = false
				lib.Front.SurfaceLight.Enabled = false
				lib.Light.Shadow.Enabled = false
				lib.Light.Sound.Volume = 0
				lib.Light.Sound2.Volume = 0
			end
			--viewmodel stuff
			viewmodelflashlight.Light.Sound:Play()
			viewmodelequip:Play()
			viewmodeldown:Stop()
			viewmodelidle:Play()
			viewmodelflashlight.Light.Light.Enabled = true
			viewmodelflashlight.Front.SurfaceLight.Enabled = true
			viewmodelflashlight.Light.Shadow.Enabled = true
		else
			script.Toggle:FireServer()--if no value it means its closing the flashlight
			flashlight = false
			--viewmodelstuff
			viewmodelflashlight.Light.Sound2:Play()
			viewmodelunequip:Play()
			viewmodeldown:Play()
			viewmodelidle:Stop()
			viewmodelflashlight.Light.Light.Enabled = false
			viewmodelflashlight.Front.SurfaceLight.Enabled = false
			viewmodelflashlight.Light.Shadow.Enabled = false
		end	
		task.wait(debounceTime)
		debounce = false
	end
	
end)

FlashlightServer

local char = script.Parent.Parent
local thirdequip = char:FindFirstChildWhichIsA("Humanoid"):LoadAnimation(game.ReplicatedStorage.Flashlight.EquipThirdPerson)
local thirdidle = char:FindFirstChildWhichIsA("Humanoid"):LoadAnimation(game.ReplicatedStorage.Flashlight.IdleThirdPerson)
local flashlight
local motor
script.Parent.Toggle.OnServerEvent:Connect(function(plr,enabled)
	if enabled ~= nil then
		flashlight = game.ReplicatedStorage.Flashlight.Flashlight:Clone()
		flashlight.Parent = char
		motor = Instance.new("Motor6D",char)
		motor.Part0 = char:FindFirstChild("Right Arm")
		motor.Part1 = flashlight.Handle
		thirdequip:Play()
		thirdidle:Play()
		flashlight.Light.Sound:Play()
	else
		if flashlight then
			flashlight.Light.Sound2:Play()
			thirdidle:Stop()
			thirdequip:Stop()
			if motor then
				motor:Destroy()
			end
			flashlight:Destroy()
		end
	end	
end)

Thanks for any help solving this. I am not the most experienced developer.

1 Like

Can you possibly attach video with issue, I kinda understand the issue but I need it like wrapped in video.

So the viewmodels are overlapping eachother, being delayed on animations, and as you could see swap arms one at a time. If there were 5 people in that server, there would be 5 arms overlapping eachother. But on the Kuri account (first one to join the server) everything was fine, no overlapping.

1 Like

You’d need to add a check if player’s user id is local player’s user id

hmm… This can’t be server-side issue, since the opposite player doesn’t see the overlapping viewmodel. Also we can close out modulescript, since it’s just handleing physics, not the viewmodel duplication. So what we have left is FlashlightClient.

Still few questions:

  1. Is there any other script, which does something with viewmodel?
  2. Did you double-check if the other FlashlightClient script is disabled?

And why are you parenting viewmodel to Lightning?

Try putting FlashlightServer into ServerScriptService. As for the Toggle RemoteEvent, you can just put that in ReplicatedStorage so that both FlashlightClient and FlashlightServer can access it.

  1. There are no other scripts that interact with the viewmodel. Only FlashlightClient and FlashlightServer.

  2. Yes that one is disabled. I kept a duplicate incase i broke something and wanted to revert back. (although just noticing now that the FlashlightServer script inside the disabled FlashlightClient wasnt disabled.)

And I mentioned before that this is a model I found on toolbox, so most of that code is not mine.

Let me test it out again with the other FlashlightServer disabled.

1 Like

So I moved them to where you said I should. Fixed the parts where it fires a server event . And now im getting errors.

image

image

Explorer:
image
FlashlightClient still the same spot

Here is the parts where I updated the code:

FlashlightServer:

local char = script.Parent.Parent
local thirdequip = char:FindFirstChildWhichIsA("Humanoid"):LoadAnimation(game.ReplicatedStorage.Flashlight.EquipThirdPerson)
local thirdidle = char:FindFirstChildWhichIsA("Humanoid"):LoadAnimation(game.ReplicatedStorage.Flashlight.IdleThirdPerson)
local flashlight
local motor

game.ReplicatedStorage.Toggle.OnServerEvent:Connect(function(plr,enabled)
	if enabled ~= nil then
		flashlight = game.ReplicatedStorage.Flashlight.Flashlight:Clone()
		flashlight.Parent = char
		motor = Instance.new("Motor6D",char)
		motor.Part0 = char:FindFirstChild("Right Arm")
		motor.Part1 = flashlight.Handle
		thirdequip:Play()
		thirdidle:Play()
		flashlight.Light.Sound:Play()
	else
		if flashlight then
			flashlight.Light.Sound2:Play()
			thirdidle:Stop()
			thirdequip:Stop()
			if motor then
				motor:Destroy()
			end
			flashlight:Destroy()
		end
	end	
end)

FlashlightClient:

userinputservice.InputBegan:Connect(function(input,istyping)--open
	if istyping then return end
	if input.KeyCode == Enum.KeyCode.F and not debounce then
		debounce = true
		if flashlight == false then
			game.ReplicatedStorage.Toggle:FireServer("on")--if no value it means its closing the flashlight
			flashlight = true
			local lib = char:WaitForChild("Flashlight")
			if lib then
				lib.Light.Light.Enabled = false
				lib.Front.SurfaceLight.Enabled = false
				lib.Light.Shadow.Enabled = false
				lib.Light.Sound.Volume = 0
				lib.Light.Sound2.Volume = 0
			end
			--viewmodel stuff
			viewmodelflashlight.Light.Sound:Play()
			viewmodelequip:Play()
			viewmodeldown:Stop()
			viewmodelidle:Play()
			viewmodelflashlight.Light.Light.Enabled = true
			viewmodelflashlight.Front.SurfaceLight.Enabled = true
			viewmodelflashlight.Light.Shadow.Enabled = true
		else
			game.ReplicatedStorage.Toggle:FireServer()--if no value it means its closing the flashlight
			flashlight = false
			--viewmodelstuff
			viewmodelflashlight.Light.Sound2:Play()
			viewmodelunequip:Play()
			viewmodeldown:Play()
			viewmodelidle:Stop()
			viewmodelflashlight.Light.Light.Enabled = false
			viewmodelflashlight.Front.SurfaceLight.Enabled = false
			viewmodelflashlight.Light.Shadow.Enabled = false
		end
		task.wait(debounceTime)
		debounce = false
	end

end)


flashbutton.MouseButton1Click:Connect(function()
	if not debounce then
		debounce = true
		if flashlight == false then
			game.ReplicatedStorage.Toggle:FireServer("on")--if no value it means its closing the flashlight
			flashlight = true
			local lib = char:WaitForChild("Flashlight")
			if lib then
				lib.Light.Light.Enabled = false
				lib.Front.SurfaceLight.Enabled = false
				lib.Light.Shadow.Enabled = false
				lib.Light.Sound.Volume = 0
				lib.Light.Sound2.Volume = 0
			end
			--viewmodel stuff
			viewmodelflashlight.Light.Sound:Play()
			viewmodelequip:Play()
			viewmodeldown:Stop()
			viewmodelidle:Play()
			viewmodelflashlight.Light.Light.Enabled = true
			viewmodelflashlight.Front.SurfaceLight.Enabled = true
			viewmodelflashlight.Light.Shadow.Enabled = true
		else
			game.ReplicatedStorage.Toggle:FireServer()--if no value it means its closing the flashlight
			flashlight = false
			--viewmodelstuff
			viewmodelflashlight.Light.Sound2:Play()
			viewmodelunequip:Play()
			viewmodeldown:Play()
			viewmodelidle:Stop()
			viewmodelflashlight.Light.Light.Enabled = false
			viewmodelflashlight.Front.SurfaceLight.Enabled = false
			viewmodelflashlight.Light.Shadow.Enabled = false
		end	
		task.wait(debounceTime)
		debounce = false
	end
	
end)

I think that the error coming from FlashlightServer is that the variable char isnt defined right.

Edit: I decided the move Flashlight server in its original spot and keep the remoteevent in Replicated Storage. Now when pressing F, All of the players flashlights go down server sided, their viewmodels arms are still up.

1 Like

Actually ignore the errors in the console log except the first one. Realized I had the original model in the workspace. whoops!

So i have been testing other things that could be helpful in finding the fix for this, and finding another bug along the way. When loading into a local server test, in every players workspace.Camera. There are all players viewmodels stored in it.


(ignore the errors. those are some errors from the mirror script)

When loading into the game, the viewmodeldown animation plays once, then an extra time for every player in the server. For example if it is JUST me in the server, it will play once, cloning a viewmodel into my Camera, then again shorty after. it clones another one inside camera a second time.

The other bug I found is when my player dies, the viewmodels in camera are destroyed, and the Output is spammed with an error every frame. Causing frames to drop immensely.

Most likely from this in FlashlightClient:

if distance < 1.5 and ViewModel then
      ViewModel.Parent = workspace.CurrentCamera
	else
		ViewModel.Parent = game.Lighting
	end
	if Player.Character.Humanoid.Health == 0 then
		if ViewModel ~= nil then
			ViewModel:Destroy()
		end
	end

Here is the output:

When respawning. the output continues to give errors. Frames still drop. viewmodel gets placed into Camera, playing the viewmodeldown animation to play. Then a second time.

Sometimes some players have more viewmodels inside of their camera than others when loading in.

When dying in the 3 player local server. It only assigns 2 viewmodels to the player who died’s camera (top left), and assigns an extra one to the others? Only player who dies gets spammed by the errors again.

I have no idea how to start fixing this.

1 Like

bro just stop using the char variable and use plr.Character instead in the remoteevent callback in FlashlightServer? i dont get why you need to define character variable before hand if its on the server as theres multiple players? theres a reason why RemoteEvents have a player argument

1 Like

im sorry im not the best developer scripting wise. How would I go with doing that?

I think I got what you were saying. There is no more duplication of viewmodels in the players camera. This is how the FlashlightServer script looks like as of now:

game.ReplicatedStorage.RemoteEvents.Toggle.OnServerEvent:Connect(function(plr,enabled)
	local thirdequip = plr.Character:FindFirstChildWhichIsA("Humanoid"):LoadAnimation(game.ReplicatedStorage.Flashlight.EquipThirdPerson)
	local thirdidle = plr.Character:FindFirstChildWhichIsA("Humanoid"):LoadAnimation(game.ReplicatedStorage.Flashlight.IdleThirdPerson)
	local flashlight
	local motor

	if enabled ~= nil then
		flashlight = game.ReplicatedStorage.Flashlight.Flashlight:Clone()
		flashlight.Parent = plr.Character
		motor = Instance.new("Motor6D",plr.Character)
		motor.Part0 = plr.Character:FindFirstChild("Right Arm")
		motor.Part1 = flashlight.Handle
		thirdequip:Play()
		thirdidle:Play()
		flashlight.Light.Sound:Play()
	else
		if flashlight then
			flashlight.Light.Sound2:Play()
			thirdidle:Stop()
			thirdequip:Stop()
			if motor then
				motor:Destroy()
			end
			flashlight:Destroy()
		end
	end	
end)

FlashlightClient:

userinputservice.InputBegan:Connect(function(input,istyping)--open
	if istyping then return end
	if input.KeyCode == Enum.KeyCode.F and not debounce then
		debounce = true
		if flashlight == false then
			toggleEvent:FireServer(Player, "on")--if no value it means its closing the flashlight
			flashlight = true
			local lib = char:WaitForChild("Flashlight")
			if lib then
				lib.Light.Light.Enabled = false
				lib.Front.SurfaceLight.Enabled = false
				lib.Light.Shadow.Enabled = false
				lib.Light.Sound.Volume = 0
				lib.Light.Sound2.Volume = 0
			end
			--viewmodel stuff
			viewmodelflashlight.Light.Sound:Play()
			viewmodelequip:Play()
			viewmodeldown:Stop()
			viewmodelidle:Play()
			viewmodelflashlight.Light.Light.Enabled = true
			viewmodelflashlight.Front.SurfaceLight.Enabled = true
			viewmodelflashlight.Light.Shadow.Enabled = true
		else
			toggleEvent:FireServer(Player)--if no value it means its closing the flashlight
			flashlight = false
			--viewmodelstuff
			viewmodelflashlight.Light.Sound2:Play()
			viewmodelunequip:Play()
			viewmodeldown:Play()
			viewmodelidle:Stop()
			viewmodelflashlight.Light.Light.Enabled = false
			viewmodelflashlight.Front.SurfaceLight.Enabled = false
			viewmodelflashlight.Light.Shadow.Enabled = false
		end
		task.wait(debounceTime)
		debounce = false
	end

end)

One problem is that when you press F for the first time you will see the viewmodel animation and the right light that youre supposed to see. and now if i press F again to put it away, the animation plays, but another light shows up from the animation thirdidle. Pressing it again will bring up your viewmodel light AND the thirdidle animation light. So now there are overlapping lights.

First F press:

Second F press:

Third F press:

Here is a video of it with another account in the game:

1 Like

So is it working now?
Also is it absolutely necessary to parent the viewmodel under camera?

The main problem of viewmodels overlapping seems to be working now. Only problem is that flashlights are overlapping. The animations that other players are supposed see of you equipping the flashlight keep playing on every F key press. And pressing F again doesnt put it away. Also the viewmodels flashlight spotlight works fine. Its just the flashlight that is attatched to the players actual arm keeps getting equipped. And the flashlight attached to the players arm has a ring in it, making it look bad having 2 rings. In the video I sent you can see what I am talking about.

I think it is necessary for performance and a smoother viewmodel movement. The viewmodel’s pos and orientation gets updated to match the current cameras CFrame, while doing the sway and bobbing effects.

Play the animations on the client rather than the server