Help with scripting aim down sights

Hey guys, I am trying to achieve scripting aim down sights for my roblox game.
I’ve been trying to look for a solution for a while now so I thought I might as well make a post to see if anyone could help me.

This is what my aim down sights looks like right now:

What I would like to accomplish:

Viewmodel Script:

player.CameraMode = Enum.CameraMode.LockFirstPerson

local arms = game.ReplicatedStorage.Storage.Arms:Clone()
arms.Parent = camera --workspace["global_Arms"]

local mouseSway = springModule.new(Vector3.new())
mouseSway.Speed = 20
mouseSway.Damper = 0.5

local movementSway = springModule.new(Vector3.new())
movementSway.Speed = 25
movementSway.Damper = 0.7

local recoilSpring = springModule.new(Vector3.new())
recoilSpring.Speed = 25
recoilSpring.Damper = 1

local function getBobbing(addition, speed, modifier)
	return math.sin(time() * addition * speed) * modifier
end

RunService:BindToRenderStep("Viewmodel", 301, function(deltaTime)
	local getGunStats = gun.guns[holding[1]]

	local movementSwayAmount = Vector3.new(getBobbing(10, 1, 0.2), getBobbing(5, 1, 0.2), getBobbing(5, 1, 0.2))
		local humanoidRootPart = player.Character.HumanoidRootPart


		-- Checks if the player is scoping
		if getGunStats and #holding > 0 and not table.find(holding, "Melee") and getGunStats.Scoping == true then
			--Stops the player's arms and mouse from shaking while scoping
			movementSway.Damper = 0.7
			mouseSway.Damper = 0.6

			local mouseDelta = UserInputService:GetMouseDelta()
			mouseSway.Velocity += (Vector3.new(mouseDelta.X / 600, mouseDelta.Y / 600))
	
			movementSway.Velocity += ((movementSwayAmount / 40) * deltaTime * 60 * humanoidRootPart.AssemblyLinearVelocity.Magnitude)	
		else
			movementSway.Damper = 0.7
			mouseSway.Damper = 0.5

			local mouseDelta = UserInputService:GetMouseDelta()
			mouseSway.Velocity += (Vector3.new(mouseDelta.X / 450, mouseDelta.Y / 450))
	
			movementSway.Velocity += ((movementSwayAmount / 25) * deltaTime * 60 * humanoidRootPart.AssemblyLinearVelocity.Magnitude)	
		end

		movementSway.Velocity += ((movementSwayAmount / 25) * deltaTime * 60 * humanoidRootPart.AssemblyLinearVelocity.Magnitude)

	-- CFrames the arms
	arms:PivotTo(
		camera.CFrame
			* CFrame.Angles(movementSway.Position.X, movementSway.Position.Y / 2, 0)
			--* aimingCF
			* CFrame.Angles(0, -mouseSway.Position.X, mouseSway.Position.Y)
			* CFrame.Angles(0, movementSway.Position.Y, movementSway.Position.X)
	)
end)

Gun Script:

function gunModule:equip(gunName)
	if not gunName or gunName == "" then
		warn("gunName is invalid")
		return
	end

	local gunInfo = gunModule.guns[gunName]

	local viewmodel: Model = camera.Arms
	local rightArm = viewmodel["Right Arm"]

	local gun: Model = game.ReplicatedStorage.Storage.Guns[gunName]:Clone()
	local animations: Folder = gun.Animations

	-- Sets the gunInfos gun to the gun model
	gunInfo.Gun = gun

	local animator: Animator = viewmodel.AnimationController.Animator

	-- Loads in the all of the gun animations
	for _, animation: Animation in pairs(animations:GetChildren()) do
		gunInfo.RunningAnimationTracks[animation.Name] = animator:LoadAnimation(animation)
	end

	-- Parents the gun to the viewmodel
	gun.Parent = viewmodel

	-- Stops running animation tracks
	stopAnimTracks(gunName)

	-- Plays the equip animation and than the idle animation
	gunInfo.RunningAnimationTracks["Equip"]:Play()
	gunInfo.RunningAnimationTracks["Idle"]:Play()

	-- Creates a motor6D to connect the gun to the rightArm
	local motor6D = Instance.new("Motor6D")
	motor6D.Part0 = rightArm
	motor6D.Part1 = gun.FRAME -- FRAME is the main part of the gun
	motor6D.C0 = gunInfo.Stats.C0
	motor6D.Name = "FRAME"
	motor6D.Parent = rightArm

	if UserInputService.KeyboardEnabled then
		gunInfo.Connections["InputBegan"] = UserInputService.InputBegan:Connect(function(input, gameProcessedEvent)
			if gameProcessedEvent then
				return
			end

			if typeof(gunModule.guns[gunName].Gun) ~= "Instance" then
				return
			end

			if gunModule.guns[gunName].Owner.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then
				return
			end

			if input.UserInputType == Enum.UserInputType.Keyboard then
				if input.KeyCode == Enum.KeyCode.R then
					gunModule:reload(gunName)
				elseif input.KeyCode == Enum.KeyCode.Q then
					gunModule:scope(gunName)
				end
			elseif input.UserInputType == Enum.UserInputType.MouseButton1 then
				-- If the gun is not an automatic gun then do normal click shooting
				if not gunInfo.Stats.Auto then
					gunModule:shoot(gunName)
				else
					local lastShot = tick()
					local autoShooting = Instance.new("BoolValue")
					local wasAiming = false

					gunModule.guns[gunName].Connections["AutoShooting"] = RunService.Heartbeat:Connect(function()
						--or gunModule.guns[gunName].ReloadDebounce
						if
							not UserInputService:IsMouseButtonPressed(Enum.UserInputType.MouseButton1)
							or gunInfo.Ammo <= 0
							or gunInfo.MaxAmmo <= 0
						then
							--print("Disconnecting Auto...")
							gunModule.guns[gunName].Connections["AutoShooting"]:Disconnect()
							autoShooting.Value = false
							--wasAiming = false
							return
						end

						if (tick() - lastShot) >= gunInfo.Stats.ShootDebounceTime then
							--print("Shooting...")

							if autoShooting.Value == false then
								if gunModule.guns[gunName].Scoping then
									wasAiming = true
								end
							end

							autoShooting.Value = true

							gunModule:shoot(gunName)
							lastShot = tick()
						end
					end)

					autoShooting:GetPropertyChangedSignal("Value"):Connect(function()
						if not wasAiming then
							gunInfo.RunningAnimationTracks["Idle"]:Play()
						else
							-- gunInfo.RunningAnimationTracks["AimIn"]:Play()
							-- gunInfo.RunningAnimationTracks["AimLoop"]:Play()

							-- gunModule.guns[gunName].AimingCF = gunModule.guns[gunName].AimingCF:Lerp(
							-- 	gun.AimPoint.CFrame:ToObjectSpace(camera.Arms.Primary.CFrame),
							-- 	0.25
							-- )

							gunModule:scope(gunName)

							wasAiming = false
							-- if gunInfo.Stats["C0Aim"] then
							-- 	TweenService:Create(camera.Arms["Right Arm"].FRAME, TweenInfo.new(1), { C0 = gunInfo.Stats.C0Aim }):Play()
							-- end
						end
					end)
				end
			elseif input.UserInputType == Enum.UserInputType.MouseButton2 then
				if not gunModule.guns[gunName].Scoping then
					gunModule:scope(gunName)
				end
			end
		end)

		gunInfo.Connections["InputEnded"] = UserInputService.InputEnded:Connect(function(input, gameProcessedEvent)
			if typeof(gunModule.guns[gunName].Gun) ~= "Instance" then
				return
			end

			if gunModule.guns[gunName].Owner.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then
				return
			end

			if input.UserInputType == Enum.UserInputType.MouseButton2 then
				if gunModule.guns[gunName].Scoping then
					gunModule:scope(gunName)
				end
			end
		end)
	elseif UserInputService.TouchEnabled then
		local lastShot = tick()
		local autoShooting = false

		gunModule.guns[gunName].Connections["AutoShooting"] = RunService.Heartbeat:Connect(function(deltaTime)
			if autoShooting then
				if gunInfo.Owner.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then
					return
				end

				if (tick() - lastShot) >= gunInfo.Stats.ShootDebounceTime then
					gunModule:shoot(gunName)
					lastShot = tick()
				end
			end
		end)

		local function shoot(actionName, inputState, inputObject)
			--or gunModule.guns[gunName].ReloadDebounce

			if gunInfo.Owner.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then
				return
			end

			if typeof(gunModule.guns[gunName].Gun) ~= "Instance" then
				return
			end

			if not gunInfo.Stats.Auto then
				gunModule:shoot(gunName)
			else
				if inputState == Enum.UserInputState.End then
					autoShooting = false
				elseif inputState == Enum.UserInputState.Begin then
					autoShooting = true
				end
			end
		end

		local function reload()
			if gunInfo.Owner.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then
				return
			end

			if typeof(gunModule.guns[gunName].Gun) ~= "Instance" then
				return
			end

			gunModule:reload(gunName)
		end

		ContextActionService:BindAction("ShootButton", shoot, true)
		ContextActionService:SetImage("ShootButton", "rbxassetid://15011746962")
		ContextActionService:SetPosition("ShootButton", UDim2.new(0.3, 0, 0.155, 0))
		ContextActionService:GetButton("ShootButton").Size = UDim2.new(0, 70, 0, 70)

		ContextActionService:BindAction("ReloadButton", reload, true)
		ContextActionService:SetImage("ReloadButton", "rbxassetid://15011709775")
		ContextActionService:SetPosition("ReloadButton", UDim2.new(0.25, 0, 0.58, 0))
		ContextActionService:GetButton("ReloadButton").Size = UDim2.new(0, 50, 0, 50)

		ContextActionService:BindAction("AimButton", function() end, true)
		ContextActionService:SetImage("AimButton", "rbxassetid://15011699699")
		ContextActionService:SetPosition("AimButton", UDim2.new(0.585, 0, -0.025, 0))

		local aimButton = ContextActionService:GetButton("AimButton")

		aimButton.Size = UDim2.new(0, 55, 0, 55)

		-- Aiming toggle function for mobile
		gunModule.guns[gunName].Connections["AimButton"] = aimButton.MouseButton1Up:Connect(function()
			if gunInfo.Owner.Character:FindFirstChildOfClass("Humanoid").Health <= 0 then
				return
			end

			if typeof(gunModule.guns[gunName].Gun) ~= "Instance" then
				return
			end

			gunModule:scope(gunName)
		end)
	end
end

-- Unequips the gun
function gunModule:unequip(gunName)
	local gunInfo = gunModule.guns[gunName]

	if gunInfo.Unequipping then
		return
	end
	gunModule.guns[gunName].Unequipping = true

	-- print(gunInfo.Connections, "CONNECTIONS BEFORE UNEQUIP")

	if gunInfo.Scoping then
		gunModule:scope(gunName)
	end

	-- Disconnects all of the guns connections to boost performance
	for _, connection: RBXScriptConnection in pairs(gunInfo.Connections) do
		connection:Disconnect()
	end

	-- Stops the animations
	stopAnimTracks(gunName)

	-- print(gunInfo.RunningAnimationTracks)

	-- Plays the unequip animation
	gunInfo.RunningAnimationTracks["Unequip"]:Play()

	task.wait(gunInfo.RunningAnimationTracks["Unequip"].Length - 0.1)

	if typeof(gunInfo.Gun) == "Instance" then
		-- Destroys the gun
		gunInfo.Gun:Destroy()
		gunInfo.Gun = ""
	else
		warn("newGun.Gun is not a model")
	end

	gunModule.guns[gunName].Unequipping = false
end

function gunModule:scope(gunName)
	local gunInfo = gunModule.guns[gunName]

	if type(gunInfo.Gun) == "string" then
		return
	end

	if gunInfo.ShootDebounce or gunInfo.ReloadDebounce then
		return
	end

	local viewmodel = camera.Arms
	local frame: Motor6D = viewmodel["Right Arm"]["FRAME"]
	local aimPoint: Attachment = gunInfo.Gun:FindFirstChild("AimPoint")

	if gunModule.guns[gunName].Scoping == false then
		gunModule.guns[gunName].Scoping = true

		stopAnimTracks(gunName)

		if gunInfo.Stats["AimFov"] then
			TweenService:Create(camera, TweenInfo.new(0.5), { FieldOfView = gunInfo.Stats.AimFov }):Play()
		end

		TweenService:Create(cursor, TweenInfo.new(0.25), { ImageTransparency = 1 }):Play()

		if gunInfo.Stats["C0Aim"] then
			TweenService:Create(frame, TweenInfo.new(1), { C0 = gunInfo.Stats.C0Aim }):Play()
		end

		-- if aimPoint then
		-- 	TweenService:Create(aimPoint, TweenInfo.new(1), { CFrame = aimPoint.CFrame })
		-- end

		gunInfo.RunningAnimationTracks["AimIn"]:Play()
		gunInfo.RunningAnimationTracks["AimLoop"]:Play()

		-- gunModule.guns[gunName].AimingCF =
		-- 	gunModule.guns[gunName].AimingCF:Lerp(aimPoint.CFrame:ToObjectSpace(camera.Arms.Primary.CFrame), 0.25)

		--gunModule.guns[gunName].AimingCF:Lerp(aimPoint.CFrame:ToObjectSpace(camera.Arms.Primary.CFrame), 0.1)
	else
		gunModule.guns[gunName].Scoping = false

		--stopAnimTracks(gunName)

		if gunInfo.Stats["AimFov"] then
			local cameraTween = TweenService:Create(camera, TweenInfo.new(0.5), { FieldOfView = 70 })
			cameraTween:Play()
		end

		TweenService:Create(cursor, TweenInfo.new(0.25), { ImageTransparency = 0 }):Play()

		if gunInfo.Stats["C0Aim"] then
			TweenService:Create(frame, TweenInfo.new(1), { C0 = gunInfo.Stats.C0 }):Play()
		end

		gunInfo.RunningAnimationTracks["AimLoop"]:Stop()
		gunInfo.RunningAnimationTracks["AimOut"]:Play()
		gunInfo.RunningAnimationTracks["Idle"]:Play()

		--gunModule.guns[gunName].AimingCF = gunModule.guns[gunName].AimingCF:Lerp(CFrame.new(), 1)
	end
end

If anyone could help me, I would really appreciate it! :slight_smile:

2 Likes

In the example you gave, it looks like they decrease the FOV while aiming down sights. Maybe that would help.

nevermind completely wrong, sorry

make the inside of the scope invisible using normals and add a shadow effect

This article may help: