-
What do you want to achieve?
I wanted to create a viewmodel bopping system that I was satisfied with. I tried using a devforum post to create it, but there were a few bugs that I didn’t know how to fix and I wasn’t satisfied. I experimented with the old script and made new code based on a gun kit I found. -
What is the issue?
Whenever the player stops moving, the viewmodel glitches out a little.
robloxapp-20231108-1719354.wmv (2.0 MB)
Viewmodel I’m using:
fpa.obj (16.4 KB)
-
What solutions have you tried so far?
I compared the viewmodel script from the gunkit to my script, and didn’t see anything wrong.
local RunService = game:GetService("RunService")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
local UserInputService = game:GetService("UserInputService")
local PlayerEvents = ReplicatedStorage:WaitForChild("PlayerEvents")
local LookUpDown = PlayerEvents:WaitForChild("LookUpDown")
local Player = game.Players.LocalPlayer
local mouse = Player:GetMouse()
local Camera = workspace.CurrentCamera
local Arms = ReplicatedStorage:WaitForChild("FirstPersonArms"):Clone()
Arms.Humanoid.PlatformStand = true
Arms.Parent = Player
local ClientModules = ReplicatedStorage:WaitForChild("ClientModules")
local SpringModule = require(ClientModules:WaitForChild("SpringModule"))
local primaryPart: Part = nil
local humanoid: Humanoid = nil
local deltaX = 0
local deltaY = 0
local breathScale = 0.01
local breathe = 0
local swayX = 0
local swayY = 0
local tiltZ = 0
local tiltY = 0
local currentAngularShake = CFrame.Angles(0,0,0)
local startedRunning = 0
local runDebounce = false
local runDt = 0
local cameraOffset = CFrame.new(0, -1, 1.5)
local prevCamCframe = Camera.CFrame
local swayCframe = CFrame.new()
local swaySpring = SpringModule.new()
local bobSpring = SpringModule.new()
local targets = {}
local function lerp(num, goal, i)
return num + (goal - num) * i
end
local function Bob(addition)
return math.sin(tick() * addition * 1.3) * 0.25
end
local function makeNewTarget()
local Target = Instance.new("Part")
Target.Name = "Target"
Target.Anchored = true
Target.CanCollide = false
Target.CanQuery = false
Target.Transparency = 1
Target.Size = Vector3.one
return Target
end
local function setupIkControl(character: Model, looker: Player)
local head: Part = character:WaitForChild("Head")
local uppertorso: Part = character:WaitForChild("UpperTorso")
if not head then
return nil
end
local humanoid = character:WaitForChild("Humanoid")
local IkControl: IKControl = humanoid:WaitForChild("LookUpDown")
if not humanoid then
return
end
IkControl.Target = targets[looker]
IkControl.EndEffector = head
IkControl.Type = Enum.IKControlType.LookAt
IkControl.ChainRoot = uppertorso
return humanoid
end
local function update()
if runDebounce then
return
end
runDebounce = true
LookUpDown:FireServer(mouse.Hit.Position, Camera.CFrame)
task.wait(.1 - runDt)
runDebounce = false
end
local function setArmsToCam(dt: number)
if not primaryPart then
return
end
local mouseDelta = UserInputService:GetMouseDelta()
runDt = dt
deltaX = lerp(deltaX, mouseDelta.X*0.001, .2)
deltaY = lerp(deltaY, mouseDelta.Y*0.001, .2)
local plrVelocity = primaryPart.AssemblyLinearVelocity
local velocityXZ = plrVelocity*Vector3.new(1, 0, 1)
if velocityXZ.Magnitude > .1 then
if not startedRunning then
startedRunning = tick()
end
local swayScaleX = 0.04
local swayScaleY = 0.08
local swaySpeed = velocityXZ.Magnitude/1.8
local timeRunning = tick()-startedRunning
swayX = math.sin(timeRunning*swaySpeed)*swayScaleX
swayY = math.abs(math.cos(timeRunning*swaySpeed))*-swayScaleY
local plrDirection = humanoid.MoveDirection
if plrDirection.Z < 0 then --Pressing D
tiltZ = lerp(tiltZ, -0.05, .1)
elseif plrDirection.Z > 0 then --Pressing A
tiltZ = lerp(tiltZ, 0.05, .1)
else
tiltZ = lerp(tiltZ, 0, .1)
end
else
startedRunning = nil
swayX = lerp(swayX, 0, .1)
swayY = lerp(swayY, 0, .1)
tiltZ = lerp(tiltZ, 0, .1)
end
local velocityY = plrVelocity*Vector3.new(0,1,0)
tiltY = velocityY.Magnitude > .1 and lerp(tiltY, velocityY.Y*0.003,.1) or lerp(tiltY, 0, 0.3)
breathe = math.sin(tick()*2)*breathScale
swaySpring:shove(Vector3.new(-mouseDelta.X/500, mouseDelta.Y/500, 0))
bobSpring:shove(Vector3.new(Bob(3), Bob(6), Bob(3)) / 12 * (primaryPart.AssemblyLinearVelocity.Magnitude) / 12)
local UpdatedSway = swaySpring:update(dt)
local UpdatedBob = bobSpring:update(dt)
local camCframe = Camera.CFrame:ToWorldSpace(cameraOffset)
local vmCframe = camCframe * CFrame.Angles(breathe + deltaY + tiltY, swayX + deltaX, tiltZ)
vmCframe += Vector3.new(0, breathe + swayY, 0)
--[[
Arms:SetPrimaryPartCFrame(camCframe*
CFrame.new(UpdatedSway.X, UpdatedSway.Y, 0)*
CFrame.new(UpdatedBob.X, UpdatedBob.Y, 0)
)
]]--
Arms:SetPrimaryPartCFrame(vmCframe)
end
local function charAdded(char: Model)
Arms.Parent = Camera
update()
humanoid = char:WaitForChild("Humanoid")
primaryPart = char.PrimaryPart
local lookConnection = Camera:GetPropertyChangedSignal("CFrame"):Connect(update)
humanoid.Died:Connect(function()
lookConnection:Disconnect()
Arms.Parent = Player
end)
end
local function lookUpDown(looker: Player, mousePos: Vector3)
if not looker.Character or looker.Character.Parent ~= workspace then
print("not qualified")
return
end
if not targets[looker] then
targets[looker] = makeNewTarget()
end
setupIkControl(looker.Character, looker)
targets[looker].Position = mousePos
end
if Player.Character then
charAdded(Player.Character)
end
Player.CharacterAdded:Connect(charAdded)
RunService.RenderStepped:Connect(setArmsToCam)
LookUpDown.OnClientEvent:Connect(lookUpDown)
Gunkit Script:
local runSer = game:GetService("RunService")
local uis = game:GetService("UserInputService")
local ts = game:GetService("TweenService")
local rnd = Random.new()
local client = game.Players.LocalPlayer
local plrGui = client:WaitForChild("PlayerGui")
local crosshairGui = plrGui:WaitForChild("Crosshair")
local topLinePos = crosshairGui.CrosshairContainer.TopLine.Position
local botLinePos = crosshairGui.CrosshairContainer.BottomLine.Position
local leftLinePos = crosshairGui.CrosshairContainer.LeftLine.Position
local rightLinePos = crosshairGui.CrosshairContainer.RightLine.Position
local ammoGui = plrGui:WaitForChild("Ammo")
crosshairGui.Enabled, ammoGui.Enabled = false, false
local mouse = client:GetMouse()
local cam = workspace.CurrentCamera
local viewmodelRS = game:GetService("ReplicatedStorage"):WaitForChild("ViewmodelReplicatedStorage")
local remotes = viewmodelRS:WaitForChild("RemoteEvents")
local toolViewmodels = viewmodelRS:WaitForChild("ToolViewmodels")
local effects = viewmodelRS:WaitForChild("Effects")
local viewmodel:Model = cam:FindFirstChild(client.Name .. "'s Viewmodel") or workspace:FindFirstChild(client.Name .. "'s Viewmodel") or viewmodelRS:WaitForChild("ArmsViewmodel"):Clone()
viewmodel.Name = client.Name .. "'s Viewmodel"
viewmodel.HumanoidRootPart.Anchored = true
viewmodel.Parent = cam
if not viewmodel.HumanoidRootPart:FindFirstChild("VIEWMODEL M6D") then
local viewmodelAttach = Instance.new("Motor6D")
viewmodelAttach.Name = "VIEWMODEL M6D"
viewmodelAttach.Part0 = viewmodel.HumanoidRootPart
viewmodelAttach.Parent = viewmodel.HumanoidRootPart
end
local char = script.Parent
local hum = char:WaitForChild("Humanoid")
local root = char:WaitForChild("HumanoidRootPart")
local leftDown = false
local rightDown = false
local equippedTool = nil
local toolConfig = nil
local lastShot = 0
local numBulletsShot = 0
local justShot = false
local startedRunning = nil
local defaultFOV = 70
local swayX = 0
local swayY = 0
local deltaX = 0
local deltaY = 0
local breathe = 0
local tiltZ = 0
local tiltY = 0
local currentZRecoil = 0
local goalZRecoil = 0
local currentSprayRecoil = Vector2.new(0, 0)
local goalSprayRecoil = Vector2.new(0, 0)
local currentAngularShake = CFrame.Angles(0, 0, 0)
local goalAngularShake = CFrame.Angles(0, 0, 0)
local idleAnim = viewmodel.AnimationController:LoadAnimation(viewmodelRS["ArmAnimations"].IDLE)
local allToolAnimations = {}
local currentIdleAnim = idleAnim
function lerp(a:number, b:number, t:number)
return a + (b - a) * t
end
function moveVM(deltaTime)
if char and char.Humanoid.Health > 0 and char:FindFirstChild("Head") then
local currentFOV = cam.FieldOfView
local vXZ = (root.AssemblyLinearVelocity * Vector3.new(1, 0, 1))
if vXZ.Magnitude > 0.1 then
if not startedRunning then
startedRunning = tick()
end
local swayScaleX = 0.04
local swayScaleY = 0.08
if equippedTool and currentIdleAnim == allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM then
swayScaleX = 0.01
swayScaleY = 0.02
end
local swaySpeed = vXZ.Magnitude/1.8
local timeRunning = tick() - startedRunning
swayX = math.sin(timeRunning * swaySpeed) * swayScaleX
swayY = math.abs(math.cos(timeRunning * swaySpeed)) * -swayScaleY
local plrDirection = hum.MoveDirection
if uis:IsKeyDown(Enum.KeyCode.D) and not uis:IsKeyDown(Enum.KeyCode.A) then
tiltZ = lerp(tiltZ, -0.05, 0.1)
elseif uis:IsKeyDown(Enum.KeyCode.A) and not uis:IsKeyDown(Enum.KeyCode.D) then
tiltZ = lerp(tiltZ, 0.05, 0.1)
else
tiltZ = lerp(tiltZ, 0, 0.1)
end
local goalFOV = defaultFOV + (vXZ.Magnitude * 0.5)
currentFOV = lerp(cam.FieldOfView, goalFOV, 0.1)
else
swayX = lerp(swayX, 0, 0.1)
swayY = lerp(swayY, 0, 0.1)
tiltZ = lerp(tiltZ, 0, 0.1)
startedRunning = nil
local goalFOV = defaultFOV
currentFOV = lerp(cam.FieldOfView, goalFOV, 0.1)
end
if equippedTool and rightDown and toolConfig.Type == "Gun" and toolConfig.CanAim == true and currentIdleAnim ~= allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM and equippedTool:WaitForChild("CurrentStats").CanShoot.Value == true then
currentIdleAnim:Stop()
currentIdleAnim = allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM
allToolAnimations[equippedTool.Name].THIRD_PERSON.AIM:Play()
hum.WalkSpeed = toolConfig.AimMoveSpeed
elseif equippedTool then
if not rightDown and toolConfig.Type == "Gun" and toolConfig.CanAim and currentIdleAnim == allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM then
currentIdleAnim:Stop()
currentIdleAnim = allToolAnimations[equippedTool.Name].FIRST_PERSON.IDLE
allToolAnimations[equippedTool.Name].THIRD_PERSON.AIM:Stop()
end
hum.WalkSpeed = toolConfig.MoveSpeed
elseif not equippedTool then
hum.WalkSpeed = 16
end
if equippedTool and toolConfig.Type == "Gun" and currentIdleAnim == allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM then
currentFOV = currentFOV * toolConfig.AimZoom
end
cam.FieldOfView = currentFOV
if not currentIdleAnim.IsPlaying then
currentIdleAnim:Play()
end
local vY = (root.AssemblyLinearVelocity * Vector3.new(0, 1, 0))
if vY.Magnitude > 0.1 then
tiltY = lerp(tiltY, vY.Y * 0.003, 0.1)
else
tiltY = lerp(tiltY, 0, 0.3)
end
local mouseDelta = uis:GetMouseDelta()
deltaX = lerp(deltaX, mouseDelta.X * 0.001, 0.2)
deltaY = lerp(deltaY, mouseDelta.Y * 0.001, 0.2)
local breatheScale = 0.01
if equippedTool and toolConfig.Type == "Gun" and currentIdleAnim == allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM then
breatheScale = 0.003
end
breathe = math.sin(tick() * 2) * breatheScale
currentZRecoil = lerp(currentZRecoil, goalZRecoil, 0.2)
if justShot then
local scale = 0.2
if equippedTool and toolConfig.CanAim and allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM.IsPlaying then
scale = 0.05
end
local noiseX = (math.noise(goalZRecoil, tick(), rnd:NextNumber())) * scale
local noiseY = (math.noise(goalZRecoil + 1, tick(), rnd:NextNumber())) * scale
local noiseZ = (math.noise(goalZRecoil + 2, tick(), rnd:NextNumber())) * scale
local angularShake = CFrame.Angles(noiseX, noiseY, noiseZ)
goalAngularShake *= angularShake
justShot = false
end
currentAngularShake = currentAngularShake:Lerp(goalAngularShake, 0.1)
goalAngularShake = goalAngularShake:Lerp(CFrame.Angles(0, 0, 0), 0.3)
local goalSprayRecoilIndex = math.round(numBulletsShot)
if toolConfig and toolConfig.Type == "Gun" and goalSprayRecoilIndex > 0 then
if goalSprayRecoilIndex <= #toolConfig.SprayPattern.Initial then
goalSprayRecoil = toolConfig.SprayPattern.Initial[goalSprayRecoilIndex]
else
goalSprayRecoilIndex -= #toolConfig.SprayPattern.Initial
while goalSprayRecoilIndex > #toolConfig.SprayPattern.Looped do
goalSprayRecoilIndex -= #toolConfig.SprayPattern.Looped
end
goalSprayRecoil = toolConfig.SprayPattern.Looped[goalSprayRecoilIndex]
end
goalSprayRecoil += Vector2.new(rnd:NextNumber(-0.1, 0.1), rnd:NextNumber(-0.1, 0.1))
else
goalSprayRecoil = goalSprayRecoil:Lerp(Vector2.new(0, 0), 0.3)
end
local sprayRecoilAlpha = deltaTime
if toolConfig and toolConfig.Type == "Gun" then
sprayRecoilAlpha = deltaTime / (1 / toolConfig.FireRate)
if allToolAnimations[equippedTool.Name].FIRST_PERSON.AIM.IsPlaying then
goalSprayRecoil = goalSprayRecoil / (toolConfig.BulletInaccuracy / toolConfig.ADSInaccuracy)
end
end
currentSprayRecoil = currentSprayRecoil:Lerp(goalSprayRecoil, sprayRecoilAlpha)
local recoilX = math.rad(currentSprayRecoil.X)
local recoilY = math.rad(currentSprayRecoil.Y)
cam.CFrame *= CFrame.Angles(recoilY, recoilX, 0)
local vmCFrame = cam.CFrame
vmCFrame = vmCFrame * CFrame.Angles(breathe + deltaY + tiltY + currentZRecoil/65, swayX + deltaX, tiltZ) * currentAngularShake + Vector3.new(0, breathe + swayY, 0) + (vmCFrame.LookVector * -currentZRecoil)
if viewmodel.Parent == cam then
viewmodel:PivotTo(vmCFrame)
else
viewmodel:PivotTo(CFrame.new(0, 10000, 0))
end
end
end