FPS Aiming Positioning Bug

Please help me with this AimController Module. I’m trying to get the characters camera to tween/lerp to the aimCamera, but the position it completely messing up. I’ve attached a video plus the entire Module script. Everything works except for the viewModel positioning.

local UserInputService = game:GetService("UserInputService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local Workspace = game:GetService("Workspace")
local RunService = game:GetService("RunService")
local TweenService = game:GetService("TweenService")

local Constants = require(ReplicatedStorage.WeaponConfig.Constants)
local viewModels = ReplicatedStorage.WeaponConfig.ViewModels
local camera = Workspace.CurrentCamera
local disconnectAndClear = require(ReplicatedStorage.Utility.disconnectAndClear)

local AimController = {}
AimController.__index = AimController

function AimController.new(viewModelController, weapon)
	local handle = weapon:WaitForChild("Handle")
	local sounds = weapon:WaitForChild("Sounds")

	local viewModelName = weapon:GetAttribute(Constants.VIEW_MODEL_ATTRIBUTE)
	local viewModelTemplate = viewModels[viewModelName]

	local viewModel = viewModelTemplate:Clone()
	local muzzle = viewModel:FindFirstChild("MuzzleAttachment", true)
	local aimCamera = viewModel:FindFirstChild("AimCamera", true)
	local aimSpeed = weapon:GetAttribute(Constants.AIM_SPEED_ATTRIBUTE)
	local aimFOV = weapon:GetAttribute(Constants.DEFAULT_FOV - 10)
	local zoomDuration = weapon:GetAttribute(Constants.ZOOM_DURATION_ATTRIBUTE)

	local self = {
		viewModelController = viewModelController,
		weapon = weapon,
		aimCamera = aimCamera, -- The CFrame of this part determines the aim offset
		isAiming = false,
		aimSpeed = aimSpeed,
		aimFOV = aimFOV,
		zoomDuration = zoomDuration,

		currentAimCFrame = Constants.VIEW_MODEL_OFFSET, -- Initial CFrame of the view model relative to camera
		zoomTween = nil,
		connections = {},
	}
	setmetatable(self, AimController)

	-- Connect to RenderStepped for continuous aiming Lerp
	table.insert(self.connections, RunService.RenderStepped:Connect(function(deltaTime)
		self:updateAim(deltaTime)
	end))

	-- Connect input for aiming
	table.insert(self.connections, UserInputService.InputBegan:Connect(function(input, gpe)
		if gpe then return end
		if input.UserInputType == Enum.UserInputType.MouseButton2 or input.KeyCode == Constants.KEYBOARD_AIM_KEY_CODE then
			self:startAim()
		end
	end))

	table.insert(self.connections, UserInputService.InputEnded:Connect(function(input, gpe)
		if gpe then return end
		if input.UserInputType == Enum.UserInputType.MouseButton2 or input.KeyCode == Constants.KEYBOARD_AIM_KEY_CODE then
			self:stopAim()
		end
	end))

	return self
end

function AimController:startAim()
	if self.isAiming then return end
	self.isAiming = true
end

function AimController:stopAim()
	if not self.isAiming then return end
	self.isAiming = false
end

function AimController:updateAim(deltaTime)
	local targetOffsetCFrame

	local primaryPart = self.viewModelController.model.PrimaryPart
	if not primaryPart then return end

	if self.isAiming then
		targetOffsetCFrame = (primaryPart.CFrame:ToObjectSpace(self.aimCamera.CFrame)):Inverse()
		self:zooming(self.aimFOV, self.zoomDuration)
	else
		targetOffsetCFrame = Constants.VIEW_MODEL_OFFSET
		self:zooming(Constants.DEFAULT_FOV, self.zoomDuration)
	end

	local alpha = math.min(deltaTime * self.aimSpeed, 1)
	self.currentAimCFrame = self.currentAimCFrame:Lerp(targetOffsetCFrame, alpha)

	self.viewModelController:setAimCFrame(self.currentAimCFrame)
end

function AimController:zooming(targetFOV, duration)
	if self.zoomTween then
		self.zoomTween:Cancel()
		self.zoomTween = nil
	end

	local tweenInfo = TweenInfo.new(duration, Enum.EasingStyle.Sine, Enum.EasingDirection.InOut)
	local zoomGoal = { FieldOfView = targetFOV }

	self.zoomTween = TweenService:Create(camera, tweenInfo, zoomGoal)
	self.zoomTween:Play()
	
	self.zoomTween.Completed:Connect(function()
		self.zoomTween:Destroy()
	end)
end

function AimController:destroy()
	disconnectAndClear(self.connections)
	if self.zoomTween then
		self.zoomTween:Cancel()
		self.zoomTween = nil
	end
	camera.FieldOfView = Constants.DEFAULT_FOV
end

return AimController

Your FPS Aiming view model doesn’t seem to likely be moving when your character/camera is moving.

Are you positioning the view model with WASD?
Or is the view model moving by itself?

Also, how is your weapons offset set?

I currently have it in a Module script that handles ViewModels. I think I have found the issue, but I’m not sure on how to fix it. When aiming in the aimCamera part stays at it’s CFrame in the workspace and it causes my viewmodel to act weird. I completely scrapped the AimController module because I realized I could just manage it with this one.

Here is the part where I think the issue is happening:

function ViewModelController.new(weapon: Tool)
	local handle = weapon:WaitForChild("Handle")
	local sounds = weapon:WaitForChild("Sounds")

	local viewModelName = weapon:GetAttribute(Constants.VIEW_MODEL_ATTRIBUTE)
	local viewModelTemplate = viewModels[viewModelName]

	local viewModel = viewModelTemplate:Clone()
	local muzzle = viewModel:FindFirstChild("MuzzleAttachment", true)
	local aimCamera = viewModel:FindFirstChild("AimCamera", true)
	local primaryPart = viewModel.PrimaryPart
	assert(muzzle, `{viewModel} is missing MuzzleAttachment`)

	local animator = viewModel.AnimationController.Animator
	local animationsFolder = viewModel.Animationsr

	viewModel.Parent = ReplicatedStorage

	local animations = {}
	for _, animation in animationsFolder:GetChildren() do
		local animationTrack = animator:LoadAnimation(animation)
		animations[animation.Name] = animationTrack

		bindSoundsToAnimationEvents(animationTrack, sounds, audioTarget)
	end

	local self = {
		enabled = false,
		weapon = weapon,
		handle = handle,
		model = viewModel,
		primaryPart = primaryPart,
		muzzle = muzzle,
		animations = animations,
		toolInstances = {},
		connections = {},
		stride = 0,
		bobbing = 0,
		aimCamera = aimCamera,
		isAiming = false,
	}
	setmetatable(self, ViewModelController)

	return self
end

function ViewModelController:update(deltaTime: number)
	-- Hide tool instances
	for _, instance in self.toolInstances do
		instance.LocalTransparencyModifier = 0
	end

	-- View model bobbing animation
	local moveSpeed = (self.handle.AssemblyLinearVelocity * Vector3.new(1, 0, 1)).Magnitude
	local bobbingSpeed = moveSpeed * Constants.VIEW_MODEL_BOBBING_SPEED
	local bobbing = math.min(bobbingSpeed, 1)

	self.stride = (self.stride + bobbingSpeed * deltaTime) % (math.pi * 2)
	self.bobbing = lerp(self.bobbing, bobbing, math.min(deltaTime * Constants.VIEW_MODEL_BOBBING_TRANSITION_SPEED, 1))

	local x = math.sin(self.stride)
	local y = math.sin(self.stride * 2)
	local bobbingOffset = Vector3.new(x, y, 0) * Constants.VIEW_MODEL_BOBBING_AMOUNT * self.bobbing
	local bobbingCFrame = CFrame.new(bobbingOffset)
	
	
	if not self.isAiming then
		self.primaryPart:PivotTo(camera.CFrame * Constants.VIEW_MODEL_OFFSET * bobbingCFrame)
	elseif self.isAiming then
		self.primaryPart:PivotTo(self.aimCamera.CFrame * Constants.VIEW_MODEL_OFFSET * bobbingCFrame)
-- I set it to self.aimCamera.CFrame to see exactly where my viewModel was going
		print(self.aimCamera.CFrame)
	end

end

I have resolved my headache of an issue. I’ll put my solution down below in case someone else has an issue with this. But I just used ToObjectSpace to make the aimCamera’s CFrame change relative to the model.

if not self.isAiming then
	self.model:PivotTo(camera.CFrame * Constants.VIEW_MODEL_OFFSET * bobbingCFrame)
elseif self.isAiming then
	local aimCF = CFrame.new()
	aimCF = self.aimCamera.CFrame:ToObjectSpace(self.primaryPart.CFrame)
	self.model:PivotTo(camera.CFrame * aimCF * bobbingCFrame, 1)
	print(self.aimCamera.CFrame, camera.CFrame)
end
2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.