How do I make my FPS gun aim down sights?

Hello, I am currently working on an FPS gun. Everything is done, but I would like to know how I make it so that when you hold right click, it aims down the sight, then goes back to normal when you release right-click.

What it looks like so far:

What I hope to achieve:

1 Like

u can use CFrame.LookAt for it

I am assuming you have the viewmodel humanoidroot part set to the camera CFrame or something similar.

	local goalCFrame = camera.CFrame
	psuedoHRP.CFrame = goalCFrame

You can obtain a CFrame that goes from this HRP → sightCFrame

local STATIC_OFFSET = psuedoHRP.CFrame:ToObjectSpace(aimAttach.WorldCFrame)

If you notice this is also pretty similar to EgoMooses tutorial which I learnt from as well. I also recommend checking it out.

However here I chose to use both tween service and lerping. TweenService because it can provide multiple easing styles, lerping is just used to adjust the aiming CFrame offset from 0 to 1.

--outside the loop
local i = 0
local duration = 0.5 --Time it takes to get into aiming position
local STATIC_OFFSET = psuedoHRP.CFrame:ToObjectSpace(aimAttach.WorldCFrame)

--In the renderstep loop
	local goalCFrame = camera.CFrame

	psuedoHRP.CFrame = goalCFrame
	
	if UIS:IsMouseButtonPressed(Enum.UserInputType.MouseButton2) then
		i = math.min(i + dt/duration, 1)
		else
		i = math.max(i - dt/duration, 0)
	end
	local addedOffsetToAimAttach = STATIC_OFFSET:Inverse()*CFrame.new(0,-0.1,-3)

	local lerpAlpha = TweenService:GetValue(i, Enum.EasingStyle.Quad, Enum.EasingDirection.Out)
	psuedoHRP.CFrame *= CFrame.new():Lerp(addedOffsetToAimAttach, lerpAlpha)

This code was taken from my Spring viewmodel post which constructs the view model purely in code for sharing purposes.

This should be a good starting point for reference.

Because as you have noticed the rotation point of the gun when aiming is pretty weird which is because the gun is offset to the right of the view model.

Normally when aiming the gun becomes centered in the middle changing the rotation point through an aim animation.

10 Likes

For the position I want the camera to be facing, do I add a part, or what do I add in order to view down that sight?

For me I added an attachment

You could also add a part and weld.

I’ve tried my best with this, but I completely do not understand it. I have made a part in the model named “aimPart”. Please guide me on how I change my script.

Local Script:

local runService = game:GetService("RunService")
local replicatedStorage = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")

local viewModel = replicatedStorage:WaitForChild("gunSystem"):WaitForChild("Guns"):WaitForChild(script.Parent.Name):Clone()
local camera = game.Workspace.CurrentCamera

local config = require(viewModel:WaitForChild("Config"))
local plr = game.Players.LocalPlayer
local char = plr.Character or plr.CharacterAdded:Wait()
local h = char:WaitForChild("Humanoid") or char:FindFirstChild("Humanoid") or nil
local cframe = CFrame.new()
viewModel.Parent = game.ReplicatedStorage.unequippedGuns
local mouse = plr:GetMouse()

local tool = script.Parent

local walkAnimationSpeed = 5
local walkAnimationSize = 10

local recoilSpeed = 8
local recoilSize = 0.5

local equipConnection

local cooldown = false
local isHolding = false
local isEquipped = false

local animations = {
	['Hold'] = viewModel.AnimationController.Animator:LoadAnimation(viewModel.Animations.hold),
	['Shoot'] = viewModel.AnimationController.Animator:LoadAnimation(viewModel.Animations.Shoot)
}

if h ~= nil then
	animations['CharacterHold'] = char:WaitForChild("Humanoid"):LoadAnimation(viewModel.CharacterAnimations.Hold)
	animations['CharacterShoot'] = char:WaitForChild("Humanoid"):LoadAnimation(viewModel.CharacterAnimations.Shoot)
end

cframe = config.Offset_From_Camera

local function positionModel()
	if plr.character then
		viewModel:SetPrimaryPartCFrame(camera.CFrame * cframe)


		if plr.character.Humanoid.MoveDirection.Magnitude > 0 then
			cframe = cframe:Lerp(CFrame.new(config.Offset_From_Camera.X + math.sin(time() * walkAnimationSpeed) / walkAnimationSize, config.Offset_From_Camera.Y + math.cos(time() * walkAnimationSpeed) / walkAnimationSize, config.Offset_From_Camera.Z), 0.2)
		else
			cframe = cframe:Lerp(config.Offset_From_Camera, 0.2)
		end
	end
end

local function shoot(target)
	if not cooldown then
		cooldown = true
		if plr.character:FindFirstChild(script.Parent.Name) then
			animations['Shoot']:Play()
			animations['CharacterShoot']:Play()
			local sin = math.sin(time() * recoilSpeed) / recoilSize
			cframe = cframe:Lerp(CFrame.new(config.Offset_From_Camera.X + math.sin(time() * walkAnimationSpeed) / walkAnimationSize, config.Offset_From_Camera.Y + math.cos(time() * walkAnimationSpeed) / walkAnimationSize, config.Offset_From_Camera.Z + sin), 0.2)
			viewModel["Glock Sound"]:Play()
		end
		if target then
			if target.Parent:FindFirstChild("Humanoid") then
				game.ReplicatedStorage.gunSystem.Events.Shoot:FireServer(mouse.Target, script.Parent.Name)
			end
		end
		wait(config.Cooldown_Between_Each_Click)
		cooldown = false
	end
end

tool.Activated:Connect(function()
	if config.IsAutomatic == false then
		shoot(mouse.Target)
	end
end)

mouse.Button1Down:Connect(function()
	if config.IsAutomatic == true then
		isHolding = true
	end
end)

mouse.Button1Up:Connect(function()
	if config.IsAutomatic == true then
		isHolding = false
	end
end)

local function equip()
	if not tool and viewModel then return end
	for _, part in pairs(tool:GetChildren()) do
		if part:IsA("BasePart") or part:IsA("UnionOperation") then
			part.Transparency = 1
		end
	end
	viewModel.Parent = camera
	viewModel["LeftArm"].Color = plr.character["Body Colors"].LeftArmColor3
	viewModel["RightArm"].Color = plr.character["Body Colors"].RightArmColor3
	plr.CameraMode = Enum.CameraMode.LockFirstPerson
	animations['Hold']:Play()
	if h ~= nil then
		animations['CharacterHold']:Play()
	end
	equipConnection = game["Run Service"].RenderStepped:Connect(function()
		if char then
			if plr.character.Humanoid.Health > 0 then
				positionModel()
			end
		end
	end)
end

local function unequip()
	if not tool then return end
	equipConnection:Disconnect()
	viewModel.Parent = game.ReplicatedStorage.unequippedGuns
	plr.CameraMode = Enum.CameraMode.Classic
	if h ~= nil then
		animations['CharacterHold']:Stop()
	end
end

tool.Equipped:Connect(function()
	isEquipped = true
	mouse.Icon = "http://www.roblox.com/asset?id=79658449"
	equip()
end)

tool.Unequipped:Connect(function()
	isEquipped = false
	mouse.Icon = "rbxasset://SystemCursors/Arrow"
	unequip()
end)

runService.RenderStepped:Connect(function()
	while isHolding == true and isEquipped == true do
		wait(config.Cooldown_Between_Each_Click)
		shoot(mouse.Target)
	end
end)

Sure the first step is to add a aimCFrame to your view model positioning function.

viewModel:SetPrimaryPartCFrame(camera.CFrame *aimCFrame* cframe)

AimCFrame will equal the offset as discussed previously between viewmodel rootpart or GetPivot and the aimpart cframe. This should be calculated the line before since this offset betwen aimpart and viewmodel rootpart changes due to animations commonly.

Then either use lerping to adjust this CFrame to CFrame.new() when the aim button is not pressed.

Sorry to be so complicated, but I’m completely new to lerping and barely know how to work it.