Help on how to improve my gun positional sway

Issue:

Example of what it should be:

My issue is that when I push my camera down the gun keeps going down too but then when I push my camera upwards then the gun wont go up.

Here is my current code.

local Plrz = game:GetService("Players")
local Rep = game:GetService("ReplicatedStorage")
local UIs = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local Player = Plrz.LocalPlayer
local Char = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Char:WaitForChild("Humanoid")

local FrameWork_T = {
	CurrTool = nil,
	V_Model = nil,
	Settings = nil,
}

local Aiming = false
local AimCF = CFrame.new()
local AimOffset = Vector2.new()

Char.ChildAdded:Connect(function(Tool)
	if Tool:IsA("Tool") then
		UIs.MouseIconEnabled = true
		Player.CameraMode = Enum.CameraMode.Classic
		FrameWork_T.V_Model = Rep.Viewmodels:FindFirstChild("V_"..Tool.Name):Clone()
		FrameWork_T.V_Model.Parent = Camera
		FrameWork_T.Settings = require(Tool.Settings)
		CurrTool = Tool
		print("Equipped "..CurrTool.Name)
	end
end)

Char.ChildRemoved:Connect(function(Tool)
	if Tool:IsA("Tool") then
		UIs.MouseIconEnabled = true
		Player.CameraMode = Enum.CameraMode.Classic
		Camera:FindFirstChild("V_"..Tool.Name):Destroy()
		FrameWork_T.V_Model = nil
		FrameWork_T.Settings = nil
		CurrTool = nil
		print("Unequipped "..Tool.Name)
	end
end)

RunService.RenderStepped:Connect(function(DT)
	if Humanoid.Health > 0 and FrameWork_T.V_Model ~= nil then
		
		local MouseDelta = UIs:GetMouseDelta()
		
		AimOffset = AimOffset + Vector2.new(MouseDelta.X, MouseDelta.Y)
		
		local SwayCF = CFrame.Angles(math.rad(math.clamp(-AimOffset.Y/10, -10, 10)), math.rad(math.clamp(-AimOffset.X/10, -50, 15)), math.rad(0))
		
		if Aiming == true then
			local AimPart
			
			if FrameWork_T.V_Model:FindFirstChild("AimAtt") then
				AimPart = FrameWork_T.V_Model.AimAtt:FindFirstChild("AimPart")
			else
				AimPart = FrameWork_T.V_Model:FindFirstChild("AimPart")
			end
			
			local Offset = AimPart.CFrame:ToObjectSpace(FrameWork_T.V_Model.PrimaryPart.CFrame)
			AimCF = AimCF:Lerp(Offset, 1-FrameWork_T.Settings.AimSmooth^DT)
			TweenFOV(0.3, 90)
		else
			local Offset = CFrame.new()
			AimCF = AimCF:Lerp(Offset, 1-FrameWork_T.Settings.AimSmooth^DT)
			TweenFOV(0.3, 70)
		end
		
		FrameWork_T.V_Model:SetPrimaryPartCFrame(Camera.CFrame * AimCF * SwayCF)
		
	end
end)

I used this for a better understanding on how to make it.

1 Like

It looks like your gun is following the camera downward, but not upward. This likely happens because of how AimOffset is being modified and how SwayCF is applied.

Issue

  • Gun moves down with camera:
    • AimOffset accumulates MouseDelta.Y, increasing downward movement.
    • The SwayCF calculation correctly applies downward tilt.
  • Gun doesn’t move up properly:
    • Your math.clamp() on SwayCF limits upward rotation but might not correctly allow positive values for moving up.
    • If AimOffset.Y is increasing in only one direction (negative when looking down), it may not correctly apply the upward movement.

Fix

Modify the SwayCF calculation to correctly apply the camera’s movement in both directions.

:one: Fix SwayCF Calculation

Modify this line:

local SwayCF = CFrame.Angles(math.rad(math.clamp(-AimOffset.Y/10, -10, 10)), math.rad(math.clamp(-AimOffset.X/10, -50, 15)), math.rad(0))

To:

local SwayCF = CFrame.Angles(
    math.rad(math.clamp(AimOffset.Y / 10, -10, 10)),  -- Flip the sign to ensure correct upward movement
    math.rad(math.clamp(-AimOffset.X / 10, -50, 15)),
    0
)

:small_blue_diamond: Change -AimOffset.Y to AimOffset.Y (this fixes the downward movement overpowering upward movement).


:two: Reset AimOffset.Y More Gradually

In RunService.RenderStepped, AimOffset keeps accumulating. Try resetting it slightly each frame to prevent extreme drift:

AimOffset = AimOffset:Lerp(Vector2.new(0, 0), 0.05)  -- Smoothly reset sway

This prevents excessive downward tilt buildup.


Final Adjustments

If the gun still doesn’t move up properly, increase the clamp values:

local SwayCF = CFrame.Angles(
    math.rad(math.clamp(AimOffset.Y / 8, -15, 15)),  -- Increased range
    math.rad(math.clamp(-AimOffset.X / 10, -50, 15)),
    0
)

:small_blue_diamond: This allows the gun to move more freely while still keeping it realistic.


Summary of Fixes

  1. Change -AimOffset.Y to AimOffset.Y in SwayCF.
  2. Reset AimOffset.Y slightly each frame with Lerp().
  3. Increase clamp values if movement is still limited.

Let me know if the issue persists! :rocket:

2 Likes

Are you 100% sure you wrote that?

2 Likes

If I wanted to have a sway that would return to the initial middle position this would be a solution which is why I will definitly be using this for Aiming down sights, but this did not really solve my issue.

I made another video describing my issue in a better way.

The issue is that the Math.Clamp is not working really, my gun still moves farther and around me so in this video I removed the math.clamp and showed that all the math.clamp did was make it look like it couldn’t go any further but it still is going further.

I need to figure out a way to make a border for the gun so when it goes farther than the Math.Clamp it’ll go back.

Code as of right now:

local Plrz = game:GetService("Players")
local Rep = game:GetService("ReplicatedStorage")
local UIs = game:GetService("UserInputService")
local TweenService = game:GetService("TweenService")
local RunService = game:GetService("RunService")

local Camera = workspace.CurrentCamera
local Player = Plrz.LocalPlayer
local Char = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Char:WaitForChild("Humanoid")

local FrameWork_T = {
	CurrTool = nil,
	V_Model = nil,
	Settings = nil,
}

local Aiming = false
local AimCF = CFrame.new()
local AimOffset = Vector2.new()

Char.ChildAdded:Connect(function(Tool)
	if Tool:IsA("Tool") then
		UIs.MouseIconEnabled = true
		Player.CameraMode = Enum.CameraMode.Classic
		FrameWork_T.V_Model = Rep.Viewmodels:FindFirstChild("V_"..Tool.Name):Clone()
		FrameWork_T.V_Model.Parent = Camera
		FrameWork_T.Settings = require(Tool.Settings)
		CurrTool = Tool
		print("Equipped "..CurrTool.Name)
	end
end)

Char.ChildRemoved:Connect(function(Tool)
	if Tool:IsA("Tool") then
		UIs.MouseIconEnabled = true
		Player.CameraMode = Enum.CameraMode.Classic
		Camera:FindFirstChild("V_"..Tool.Name):Destroy()
		FrameWork_T.V_Model = nil
		FrameWork_T.Settings = nil
		CurrTool = nil
		print("Unequipped "..Tool.Name)
	end
end)

RunService.RenderStepped:Connect(function(DT)
	if Humanoid.Health > 0 and FrameWork_T.V_Model ~= nil then
		
		local LastCameraCF = Camera.CFrame
		
		local MouseDelta = UIs:GetMouseDelta()
		
		AimOffset = AimOffset + Vector2.new(MouseDelta.X, MouseDelta.Y)
		
		local Rot = Camera.CFrame:ToObjectSpace(LastCameraCF)
		local x, y, z = Rot:ToOrientation()
		
		local SwayCF = CFrame.Angles(
			math.rad(math.clamp(-AimOffset.Y / 8, -15, 15)),
			math.rad(math.clamp(-AimOffset.X / 10, -50, 15)),
			0
		)
		
		print(SwayCF)
		
		if Aiming == true then
			local AimPart
			
			if FrameWork_T.V_Model:FindFirstChild("AimAtt") then
				AimPart = FrameWork_T.V_Model.AimAtt:FindFirstChild("AimPart")
			else
				AimPart = FrameWork_T.V_Model:FindFirstChild("AimPart")
			end
			
			local Offset = AimPart.CFrame:ToObjectSpace(FrameWork_T.V_Model.PrimaryPart.CFrame)
			AimCF = AimCF:Lerp(Offset, 1-FrameWork_T.Settings.AimSmooth^DT)
			TweenFOV(0.3, 90)
			AimOffset = AimOffset:Lerp(Vector2.new(0, 0), 0.05)
		else
			local Offset = CFrame.new()
			AimCF = AimCF:Lerp(Offset, 1-FrameWork_T.Settings.AimSmooth^DT)
			TweenFOV(0.3, 70)
		end
		
		FrameWork_T.V_Model:SetPrimaryPartCFrame(Camera.CFrame * AimCF * SwayCF)
		
	end
end)

I thank you for your help this really did help me get a better understanding of what I am doing wrong.

1 Like