Viewmodel Sway Problem

Greetings, fellow developers! I am working on a game where you hold a flashlight with a spotlight using the new future lighting. I’m using a viewmodel to hold the flashlight and not a tool. The spotlight and flashlight look really nice, but when I move the camera around, the flashlight looks stuck on the camera. So, I want to make some sort of sway so the flashlight doesn’t look stuck on the camera. However, I don’t really know how to make sway. I think it has something to do with calculating the magnitude and lookVecter, and changing the CFrame of the viewmodel.

Here is the script for the viewmodel:

local player = game:GetService("Players").LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = workspace.Camera
local runService = game:GetService("RunService")

local viewModel = game:GetService("ReplicatedStorage"):WaitForChild("ViewModel"):Clone()
viewModel.Parent = camera

runService.RenderStepped:Connect(function()
	if character:FindFirstChild("Humanoid").Health == 0 then
		if camera:FindFirstChild("ViewModel") ~= nil then
			camera.ViewModel:Destroy()
		end
	end
	if camera:FindFirstChild("ViewModel") ~= nil then
		camera.ViewModel:SetPrimaryPartCFrame(camera.CFrame * CFrame.new(0.25,-1.5,-1) * CFrame.Angles(0.05,0,0))
	end
end)

Any help will be appreciated. Thanks!

2 Likes

If you want sway I’d recommend using someone’s spring module or make one yourself.

1 Like

SpringModule.rbxm (1.3 KB)
Here’s one that’s pretty simple to use.

It has 3 functions:

  1. SpringModule.new()
  2. Spring:shove(Vector3.new())
  3. Spring:update(dt)

You basically just create a new spring using function #1

local SwaySpring = SpringModule.new()

then you apply some shove to it, in this case whenever the mouse moves
I recommend using

game:GetService("UserInputService"):GetMouseDelta())

and then use the X and Y values for that with the :shove() function.
Finally you can then update the spring with :update(dt) and there you go.

Thanks, I’ll try to see if I can make it work.

Let me know if you need more help.

1 Like

So, I feel like I’m getting closer, but I don’t know how to fix this error.

Players.MrFinkyDoodles.PlayerGui.LocalScript:24: attempt to call a nil value 

Here is my script:

local runService = game:GetService("RunService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local userInputService = game:GetService("UserInputService")

local modules = replicatedStorage:WaitForChild("Modules")
local SpringModule = require(modules:FindFirstChild("SpringModule"))

local player = game:GetService("Players").LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = workspace.Camera
local viewModel = game:GetService("ReplicatedStorage"):WaitForChild("ViewModel"):Clone()
viewModel.Parent = camera

runService.RenderStepped:Connect(function()
	if character:FindFirstChild("Humanoid").Health == 0 then
		if camera:FindFirstChild("ViewModel") ~= nil then
			camera.ViewModel:Destroy()
		end
	end
	if camera:FindFirstChild("ViewModel") ~= nil then
		camera.ViewModel:SetPrimaryPartCFrame(camera.CFrame * CFrame.new(0.25,-1.5,-1) * CFrame.Angles(0.05,0,0))
		
		local mouseDelta = userInputService:GetMouseDelta()
		SpringModule:shove(Vector3.new(-mouseDelta.X / 500,mouseDelta.Y / 200,0)) --line with error
		local updatedSpring = SpringModule:update(dt)
		
		viewModel:FindFirstChild("HumanoidRootPart").CFrame *= CFrame.new(updatedSpring.X,updatedSpring.Y,0)
	end
end)

Thanks for the help!

Ok ok, so you have gotten the SpringModule but then haven’t created the spring. Instead you are calling for the module itself which is incorrect.

Try:

local Sway = SpringModule.new()

and then only refer to “Sway” in your renderstepped function.

1 Like

I got another error:

attempt to perform arithmetic (mul) on nil and number

This error was in the 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 -- line with error
		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

I think that’s because you attempted to update the spring by “dt” which you haven’t defined. “dt” stands for deltatime and can be defined inside the brackets of the renderstepped function:

runService.RenderStepped:Connect(function(dt)

Try that and see if it works, if not, send me a model file or place file and I’ll debug it myself.

Here is a place file:
swayTroubles.rbxl (133.3 KB)

local player = game:GetService("Players").LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()
local camera = workspace.Camera
local runService = game:GetService("RunService")

local viewModel = game:GetService("ReplicatedStorage"):WaitForChild("ViewModel"):Clone()
viewModel.Parent = camera

local swayEffect=.7
local swayCFrame=CFrame.new()
local lastCamCFrame=CFrame.new()

runService.RenderStepped:Connect(function()
	if character:FindFirstChild("Humanoid").Health == 0 then
		if camera:FindFirstChild("ViewModel") ~= nil then
			camera.ViewModel:Destroy()
		end
	end
	if camera:FindFirstChild("ViewModel") ~= nil then

local rotation=camera.CFrame:ToObjectSpace(lastCamCFrame)
local X,Y,Z=rotation:ToOrientation()
swayCFrame=swayCFrame:Lerp(CFrame.Angles(math.sin(X)*swayEffect,math.sin(Y)*swayEffect,0),.1)

lastCamCFrame=camera.CFrame
		camera.ViewModel:SetPrimaryPartCFrame(camera.CFrame *swayCFrame*CFrame.new(0.25,-1.5,-1))
	end
end)
3 Likes