Help with ViewModel bobbing!

  • I want to fix the viewmodel bobbing when sprinting, aiming or sliding as it seems to have a few problems:

  • For some reason, when I reEquip the gun more than once, the animation of running/aiming/sliding/shooting seems to be faster (almost like an instant) than what I lerped it to , (using the RenderStep function)

  • I have tried to set the Aiming Cframe and the Sprinting Cframe to its original position before applying any of the new Cframes but it didn’t fix the problem, I looked on the developer hub for problems like this but found none.

I have a little bit of understanding of CFrames but this one doesn’t seem to cut it out for me.

Here is my local script for both the viewmodel and the gun:

--> CUSTOMISATION
local Damage = 20
local MaxAmmo = 9
local ReloadTime = 110/60
local FireCooldown = .12
local FireAnimSpeed = 3.5
local ReloadAnimSpeed = 8
local GunFireSpeeds = {.9, .95, 1, 1.05, 1.1}
local debounce

--> LOCAL VARIABLES & SERVICES
local Pistol = script.Parent
local PistolHandle = Pistol:WaitForChild("BodyAttach")
local Player = game:GetService("Players").LocalPlayer
local Char = Player.Character or Player.CharacterAdded:Wait()
local PlayerGui = Player:WaitForChild("PlayerGui")
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local players = game:GetService("Players")
local ReloadAnimation = Pistol:WaitForChild("ReloadAnimation") 
local FireAnimation = Pistol:WaitForChild("FireAnimation")
local IdleAnimation = Pistol:WaitForChild("IdleAnimation") 
local AimAnimation = Pistol:WaitForChild("Aim")
local AimShootAnimation = Pistol:WaitForChild("AimShoot")
local OriginalMouseIcon = Player:GetMouse().Icon
local CurrentAmmoValue = Pistol:WaitForChild("BulletHoles"):WaitForChild("CurrentAmmo")
local CurrentAmmo = MaxAmmo
local TextDisplay = Pistol:WaitForChild("PistolGui"):WaitForChild("AmmoText")
TextDisplay.Text = CurrentAmmo.." / "..MaxAmmo
CurrentAmmoValue.Value = CurrentAmmo

local Humanoid = Player.Character:WaitForChild("Humanoid")
local IdleTrack = Humanoid:LoadAnimation(IdleAnimation)
local AimTrack = Humanoid:LoadAnimation(AimAnimation)

local camera = workspace.CurrentCamera
local cf = CFrame.new()

local IsShooting = false
local Reloading = false
local Aiming = false
local Sprinting = false
local SprintValue = game.Workspace:WaitForChild("Values"):WaitForChild("SprintValue")
local AimValue = game.Workspace:WaitForChild("Values"):WaitForChild("AimValue")
local SlideValue = game.Workspace:WaitForChild("Values"):WaitForChild("SlideValue")
local ReloadValue = game.Workspace:WaitForChild("Values"):WaitForChild("ReloadValue")

local SpringModule = require(ReplicatedStorage:WaitForChild("SpringModule"))
local MainFOV = camera.FieldOfView

local infoBegin = TweenInfo.new(
	0.2,
	Enum.EasingStyle.Sine,
	Enum.EasingDirection.In,
	0,
	false,
	0
)
local infoEnd = TweenInfo.new(
	0.2,
	Enum.EasingStyle.Sine,
	Enum.EasingDirection.In,
	0,
	false,
	0
)

local GoalBegin = {
	FieldOfView = MainFOV - 20
}
local GoalEnd = {
	FieldOfView = MainFOV
}

local cameraAnimationBegin = TS:Create(camera,infoBegin,GoalBegin)
local cameraAnimationEnd = TS:Create(camera,infoEnd,GoalEnd)

local X,Y,Z

--> FUNCTIONS
function UpdateUI()
	TextDisplay.Text = CurrentAmmo.." / "..MaxAmmo
	PlayerGui:WaitForChild("PistolGui"):WaitForChild("AmmoText").Text = CurrentAmmo.." / "..MaxAmmo
end
function Shoot() 
	if not SprintValue.Value then
		IsShooting = true
		local model = camera:WaitForChild("FirstGlock")
		local config = require(model:WaitForChild("Config"))
		cf = cf:Lerp(cf * CFrame.new(
			0, -- X
			-0.3, -- Y
			1.3) * CFrame.Angles(math.rad(25),0,0), -- Z
			.2) -- Factor
		IsShooting = false
	end
end
X,Y,Z = 1,-1.15,0.1
function Reload()
	Reloading = true
	ReloadValue.Value = true
	if Aiming == true then 
		cameraAnimationEnd:Play()
	end
	local Humanoid = Player.Character:WaitForChild("Humanoid")
	local ReloadAnimationTrack = Humanoid:LoadAnimation(ReloadAnimation)
	ReloadAnimationTrack:Play(.1, 1)
	Pistol.GunReload:Play()
	if camera:FindFirstChild("FirstGlock") then
		local model = camera.FirstGlock
		local config = require(model:WaitForChild("Config"))
		Pistol.GunReload:Play()
		local Reload = model.AnimationController.Animator:LoadAnimation(model.Animations.Reload)
		Reload:Play()
		for i=0, 1 ,0.3 do
			wait()
			cf = cf:Lerp(CFrame.new(
				X, -- 1
				Y, -- -1.15
				Z),-- 0.1
				i
			)
		end
		wait(ReloadTime)
		for i=1, 0.2 ,0.4 do
			wait(0.01)
			cf = cf:Lerp(CFrame.new(config.OffsetFromCamera),i)
		end
	end
	CurrentAmmo = MaxAmmo
	CurrentAmmoValue.Value = CurrentAmmo
	UpdateUI()
	ReloadValue.Value = false
	Reloading = false
end

local Factor

Pistol.Equipped:Connect(function()
	local AimingCF = CFrame.new()
	if Aiming == true or AimValue.Value == true then
		print("Was Aiming")
		Aiming = false
		AimValue.Value = false
	end
	debounce = true
	for _,v in pairs(Pistol:GetDescendants()) do
		if v:IsA("BasePart") or v:IsA("UnionOperation") then
			v.Transparency = 1
		end
	end
	local model = ReplicatedStorage:WaitForChild("MainGame"):WaitForChild("Guns"):WaitForChild("FirstGlock"):Clone()
	model.Name = "FirstGlock"
	model:WaitForChild("Righty").Color = Char:WaitForChild("RightHand").Color
	model:WaitForChild("Lefty").Color = Char:WaitForChild("LeftHand").Color
	local config = require(model:WaitForChild("Config"))
	local AimPart = model:WaitForChild("Glock"):WaitForChild("AimPart")
	local HRP = model:WaitForChild("HumanoidRootPart")
	
	local AimCount = 0
	local JumpCount
	local check = false
	
	model.Parent = camera
	
	local animations = {
		["Hold"] = model.AnimationController.Animator:LoadAnimation(model.Animations.Hold);
	}

	animations["Hold"]:Play()
	cf = config.OffsetFromCamera
	
	local MouseSway = SpringModule.new(Vector3.new())
	
	local MovSway = SpringModule.new(Vector3.new())
	
	local function GetBobbing(Add,Speed,Mod)
		return math.sin(time()*Add*Speed)*Mod
	end
	
	local Params = RaycastParams.new()
	Params.FilterType = Enum.RaycastFilterType.Blacklist
	Params.FilterDescendantsInstances = {model,Char,workspace.fx}
	
	UIS.InputBegan:Connect(function(Key)
		if Pistol.Parent == Player.Backpack then return end
		if Key.KeyCode == Enum.KeyCode.R and Reloading == false and CurrentAmmo ~= MaxAmmo  then
			Reload()
		end
		if Key.UserInputType == Enum.UserInputType.MouseButton2 then
			while SprintValue.Value == false do
				Aiming = true
				AimValue.Value = true
				AimCount = 1
				break
			end
		end
	end)
	
	UIS.InputEnded:Connect(function(Key,GPE)
		if Pistol.Parent == Player.Backpack then return end
		if Key.UserInputType == Enum.UserInputType.MouseButton2 then
			while SprintValue.Value == false do
				Aiming = false
				AimValue.Value = false
				AimCount = 2
				break
			end
		end
	end)
	
	
	JumpCount = 0
	
	RunService:BindToRenderStep("model",301,function(DT)
		
		Factor = 0.2
		if Pistol.Parent == Player.Backpack then return end
		if AimValue.Value == false then
			AimTrack:Stop()
			IdleTrack:Play()
		else 
			IdleTrack:Stop()
			AimTrack:Play()
		end
		
		MouseSway.Speed = 20
		MouseSway.Damper = 0.35
		
		MovSway.Speed = 20
		MovSway.Damper = 0.25
		
		if SprintValue.Value == true then
			MovSway.Speed = 0.0001
			MovSway.Damper = 0.0002
		elseif AimValue.Value == true then
			MouseSway.Speed = 20
			MouseSway.Damper = 1.35
			MovSway.Speed = 100000
			MovSway.Damper = 1000000
		end
		if Humanoid.FloorMaterial == Enum.Material.Air and (Reloading == false or ReloadValue.Value == false) then
			JumpCount = JumpCount + 0.01
			if JumpCount == 0.2 then JumpCount = 0 end
			MouseSway.Speed = 40
			MouseSway.Damper = 0.02
			MovSway.Speed = 0.1
			MovSway.Damper = 0.02
		elseif Humanoid.FloorMaterial == Enum.Material.Air and (Reloading == true or ReloadValue.Value == true) then
			JumpCount = 0
		else
			JumpCount = JumpCount - 0.2
			if JumpCount <= 0 then JumpCount = 0 end
		end
		
		local SprintCount = 1
		local mouse = game.Players.LocalPlayer:GetMouse()
		local Mouse_Icon = "rbxassetid://13055632462"
		mouse.Icon = Mouse_Icon
		
		local MouseDelta = UIS:GetMouseDelta()
		MouseSway.Velocity += (Vector3.new(MouseDelta.X / 450,MouseDelta.Y / 450))
		
		local MovSwayAmount = Vector3.new(GetBobbing(10,1,.2),GetBobbing(5,1,.2),GetBobbing(5,1,.2))
		MovSway.Velocity = ((MovSwayAmount/25)*DT*60*Char:WaitForChild("HumanoidRootPart").AssemblyLinearVelocity.Magnitude)
		if SprintValue.Value == true and SprintCount == 1 and not Reloading and SlideValue.Value == false then
			cf = cf:Lerp(cf * CFrame.new(
				0.7, -- X
				1.2, -- Y
				1.2) * --Z
				CFrame.Angles(math.rad(-25),math.rad(15),math.rad(15)), --Angles(X,Y,Z)
				Factor) -- Factor
			print(cf)
		elseif SprintValue.Value == false and SprintCount == 0 and not Reload() then
			cf = cf:Lerp(CFrame.new(),0.9)
			print(cf)
		end
		if SlideValue.Value == true and not Reloading then
			cf = CFrame.new()
			cf = cf:Lerp(cf * CFrame.new(
				0.7, -- X
				-1, -- Y
				1.2) * --Z
					CFrame.Angles(math.rad(-25),math.rad(15),math.rad(15)), --Angles(X,Y,Z)
				.1) -- Factor
		end
		
		local MouseDelta = UIS:GetMouseDelta()
		if (Aiming and AimCount == 1) and not Reloading then
			AimingCF = AimingCF:Lerp(AimPart.CFrame:ToObjectSpace(HRP.CFrame), 0.7)
			cameraAnimationBegin:Play()
		elseif ((AimCount == 2 and not Aiming) or Reloading) then
			AimingCF = AimingCF:Lerp(CFrame.new(),0.9)
			cameraAnimationEnd:Play()
		end
		if model and model.PrimaryPart then
			model:SetPrimaryPartCFrame(camera.CFrame*cf)
		end
		
		if (Humanoid.MoveDirection.Magnitude > 0 and IsShooting == false and not Reloading) or Humanoid.FloorMaterial == Enum.Material.Air and not Reloading and not IsShooting then
			cf = cf:Lerp(config.OffsetFromCamera * 
				AimingCF *
				CFrame.new(MovSway.Position.X/2,MovSway.Position.Y/2 + JumpCount,0) *
				CFrame.Angles(0,-MouseSway.Position.X,MouseSway.Position.Y) *
				CFrame.Angles(0,MovSway.Position.Y,MovSway.Position.X)
				,.2)
		elseif Humanoid.MoveDirection.Magnitude == 0 and IsShooting == false and not Reloading then
			cf = cf:Lerp(config.OffsetFromCamera * AimingCF * CFrame.Angles(0,-MouseSway.Position.X,MouseSway.Position.Y),Factor)
		end
	end)
	Pistol.GunEquip:Play()
	Pistol:WaitForChild("PistolGui"):Clone().Parent = PlayerGui
	UpdateUI()
end)

Pistol.Unequipped:Connect(function()
	if Aiming == true or AimValue.Value == true then
		Aiming = false
		AimValue.Value = false
		cameraAnimationEnd:Play()
	end
	if SprintValue.Value == true or Sprinting == true then
		SprintValue.Value = false
		Sprinting = false
	end
	debounce = false
	
	if camera:FindFirstChild("FirstGlock") then
		camera.FirstGlock:Destroy()
	end
	IdleTrack:Stop()
	AimTrack:Stop()
	if PlayerGui:FindFirstChild("PistolGui") then
		PlayerGui:WaitForChild("PistolGui"):Destroy()
	else end
	local Mouse = Player:GetMouse()
	Mouse.Icon = OriginalMouseIcon
end)

Humanoid.Died:Connect(function()
	camera:WaitForChild("FirstGlock"):Destroy()
end)

Pistol.Activated:Connect(function()
	local Mouse = Player:GetMouse()
	if CurrentAmmo > 0 and not Reloading and not SprintValue.Value then
		if debounce == false then return end
		debounce = false
		Shoot()
		--Effect.Enabled = true
		--Effect:Emit(1)
		--Effect.Enabled = false
		--if camera:FindFirstChild("FirstGlock") then
		--local EffectFirst = camera.FirstGlock.Pistol.Handle.Effect.ParticleEmitter
			--EffectFirst.Enabled = true
			--EffectFirst:Emit(1)
			--EffectFirst.Enabled = false
		--end
		
		Humanoid = Player.Character:WaitForChild("Humanoid")
		local ShootFirst = camera:WaitForChild("FirstGlock").AnimationController.Animator:LoadAnimation(camera:WaitForChild("FirstGlock"):WaitForChild("Animations"):WaitForChild("Shoot")):Play()
		if AimValue.Value == false then
			Humanoid:LoadAnimation(FireAnimation):Play()
		else
			Humanoid:LoadAnimation(AimShootAnimation):Play()
		end
		
		CurrentAmmo = CurrentAmmo - 1
		UpdateUI()
		CurrentAmmoValue.Value = CurrentAmmo
		Pistol.GunFire.PlaybackSpeed = GunFireSpeeds[math.random(#GunFireSpeeds)]
		if camera:FindFirstChild("FirstGlock") then
			Pistol.GunFire:Play()
		end
		
		if Mouse.Target ~= nil then
			local HitPart = Mouse.Target
			if Mouse.Target.Name == "Head" then Damage = 40
			elseif Mouse.Target.Name == ("Right Leg" or "Left Leg") then Damage = 10 end
			if Mouse.Target.Parent:FindFirstChild("Humanoid") then
				local Victim = Mouse.Target.Parent
				if game.Players:FindFirstChild(Victim.Name) then
					if game.Players[Victim.Name].TeamColor ~= Player.TeamColor then
						Pistol.DealDamage:FireServer(Victim, Damage, HitPart)
					else end
				else
					Pistol.DealDamage:FireServer(Victim, Damage, HitPart)
				end
			elseif Mouse.Target.Parent.Parent:FindFirstChild("Humanoid") then
				local Victim = Mouse.Target.Parent.Parent
				if game.Players:FindFirstChild(Victim.Name) then
					if game.Players[Victim.Name].TeamColor ~= Player.TeamColor then
						Pistol.DealDamage:FireServer(Victim, Damage, HitPart)
					else end
				else
					Pistol.DealDamage:FireServer(Victim, Damage, HitPart)
				end
			else end
		end
		wait(FireCooldown)
		debounce = true
	elseif Reloading == false and not SprintValue.Value then
		Reload()
	end
end)

Player.CharacterAdded:Connect(function(char)
	Char = char
	Humanoid = Char:WaitForChild("Humanoid")
end)
  • Please if you have any idea why this issue is accruing, Please let me know, Thanks!

That is a lot of variables.
I would put the singleton services at the top of the script, then the parts/objects, then other variables. (You can copy and paste this in between the Customisation variables and the SpringModule variable)


local CurrentAmmo = MaxAmmo

--> SERVICES
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local TS = game:GetService("TweenService")
local UIS = game:GetService("UserInputService")
local players = game:GetService("Players")
local PlayerGui = players.LocalPlayer:WaitForChild("PlayerGui")

--> PLAYER
local Char = Player.Character or Player.CharacterAdded:Wait()
local Humanoid = Char:WaitForChild("Humanoid")
local OriginalMouseIcon = Player:GetMouse().Icon

local Pistol = script.Parent
local PistolHandle = Pistol:WaitForChild("BodyAttach")

--> ANIMATIONS
local ReloadAnimation = Pistol:WaitForChild("ReloadAnimation")
local FireAnimation = Pistol:WaitForChild("FireAnimation")
local IdleAnimation = Pistol:WaitForChild("IdleAnimation")
local AimAnimation = Pistol:WaitForChild("Aim")
local AimShootAnimation = Pistol:WaitForChild("AimShoot")
local IdleTrack = Humanoid:LoadAnimation(IdleAnimation)
local AimTrack = Humanoid:LoadAnimation(AimAnimation)

local CurrentAmmoValue = Pistol.BulletHoles:WaitForChild("CurrentAmmo")
local TextDisplay = Pistol.PistolGui:WaitForChild("AmmoText")
TextDisplay.Text = CurrentAmmo.." / "..MaxAmmo
CurrentAmmoValue.Value = CurrentAmmo

--> OTHER
local camera = workspace.CurrentCamera
local cf = CFrame.new()

local IsShooting = false
local Reloading = false
local Aiming = false
local Sprinting = false
local SprintValue = workspace.Values:WaitForChild("SprintValue")
local AimValue = workspace.Values:WaitForChild("AimValue")
local SlideValue = workspace.Values:WaitForChild("SlideValue")
local ReloadValue = workspace.Values:WaitForChild("ReloadValue")

...

This way you don’t use GetService() twice. Also, I doubt you’d need a LocalPlayer variable, it’s not much clutter if you did Players.LocalPlayer. Also also, you don’t have to use large chains of :WaitForChild(). You can just do it for the instance you’re waiting for. (e.g. Pistol.BulletHoles:WaitForChild("CurrentAmmo") instead of Pistol:WaitForChild("BulletHoles"):WaitForChild("CurrentAmmo"))

Anyways, I think your problem here may be part of a memory leak. You don’t appear to be unbinding from the RenderStep, which may be causing this increased speed, as there are two binds at once. I may be wrong, but just to make sure, add a print() statement in the RenderStep function. If there are duplicates running, there would be more than one print every frame.
Sorry for taking so long, and I may not have helped at all, but wish to clean up any potential causes for this issue, and make the code more maintainable.

1 Like

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