Fully automatic firearm slowly becomes inaccurate

Hi there,

I’m working on a gun for a bit of fun (no pun intended). Everything’s going well, but unfortunately, I have encountered two issues.

The first issue, whilst holding down mouse button 1 (the left button), the ray emitted from the firearm slowly moves upward despite the position of my mouse not changing.

The second issue, I’ve added a realistic bullet spread to the server script, however this becomes wildly inaccurate at close range and I’m not sure why.

Below I have provided my script and short clips of the two issues. Any help would be much appreciated, thank you very much!

Inaccuracy issue:
https://gyazo.com/f0a1c9061c580b6fbabe682861015308
Spread issue:
https://gyazo.com/f5e4cc359e48c4ef38e35a45cc8cd49c

Server script:

local tool = script.Parent
local barrel = tool.Handle.Attachment
local remote = script:WaitForChild("GunEvent")
local tweenService = game:GetService("TweenService")
local gunSettings = require(tool.GunSettings)

local bulletPart = game:GetService("ReplicatedStorage"):WaitForChild("Bullet")

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.IgnoreWater = false

local spentBullets = {}
local clearCooldown = false
local clearCooldownTime = 10

remote.OnServerEvent:Connect(function(player, mousePos)	
	--print(tool.Parent)
	
	local origin = barrel.WorldPosition
	local direction = (mousePos - origin).Unit
	
	local result = workspace:Raycast(origin, direction * gunSettings.MaxDistance, raycastParams)

	local intersection = result and result.Position or origin + direction * gunSettings.MaxDistance
	local distance = (origin - intersection).Magnitude
	
	local minSpread = -gunSettings.MinSpread / 100 * distance
	local maxSpread = gunSettings.MaxSpread / 100 * distance
	local spread = Vector3.new(math.random(gunSettings.MinSpread, gunSettings.MaxSpread), math.random(gunSettings.MinSpread, gunSettings.MaxSpread), math.random(gunSettings.MinSpread, gunSettings.MaxSpread))
	intersection += spread
	
	local bullet = bulletPart:Clone()
	bullet.Transparency = 0
	bullet.Size = Vector3.new(0.1,0.1,distance)
	bullet.CFrame = CFrame.new(origin, intersection)*CFrame.new(0,0, -distance/2)
	bullet.Parent = workspace.GunBullets
	
	if result then
		local part = result.Instance
		local humanoid = part.Parent:FindFirstChild("Humanoid")
		
		print(part.Name)
		if humanoid then
			if part.Name == "Left Arm" or part.Name == "Right Arm" or part.Name == "Left Leg" or part.Name == "Right Leg" then
				humanoid:TakeDamage(gunSettings.LimbDamage)
			end
			
			if part.Name == "Torso" or part.Name == "HumanoidRootPart" then
				humanoid:TakeDamage(gunSettings.TorsoDamage)
			end
			
			if part.Name == "Head" then
				humanoid:TakeDamage(gunSettings.HeadDamage)
			end
		end
		
	end

	changeBullet(bullet)
end)

function changeBullet(bullet)
	for count = 1, 10 do
		bullet.Transparency = bullet.Transparency + 0.1
		wait(0.01)
	end
	game:GetService("Debris"):AddItem(bullet, 0)
end


local function onEquipped()
	raycastParams.FilterDescendantsInstances = {tool, tool.Parent, workspace.GunBullets}
end

local function onUnequipped()
	--unequipped function here
end

tool.Equipped:Connect(onEquipped)
tool.Unequipped:Connect(onUnequipped)

Local script:

local tool = script.Parent.Parent
local remote = tool.Main:WaitForChild("GunEvent")
local gunSettings = require(tool.GunSettings)

local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()

local shooting = false
local semiCooldown = false

--[[tool.Activated:Connect(function()
	remote:FireServer(mouse.Hit.Position)
end)]]--

tool.Equipped:Connect(function()
	mouse.Button1Down:Connect(function()
		if gunSettings.FireType == "Full" then
			shooting = true
			while shooting do
				wait(60/gunSettings.FireRate)
				remote:FireServer(mouse.Hit.Position)
			end
		
		elseif gunSettings.FireType == "Semi" and semiCooldown == false then

			semiCooldown = true
			remote:FireServer(mouse.Hit.Position)
				
			wait(60/gunSettings.FireRate)
				
			semiCooldown = false

		end
	end)
end)

mouse.Button1Up:Connect(function()
	shooting = false
end)

It’s because you’re using Mouse.Hit, the old bullet is being detected so it shoots the next bullet to the position of the previous bullet. Use Mouse.TargetFilter to avoid this issue.

1 Like

For the first one the local script mouse position is hitting the bullet ray.

Do

bullet.CanQuery = false

To stop this from happening, or use a mouse filter as @DEVLocalPlayer has said with .TargetFilter or custom mouse raycasting.

The second one the problem and solution is explained in the post below.

1 Like

Hi there; the first solution worked, thank you!

Unfortunately, the second one doesn’t appear to solve my issue. That post talks about how a shotgun spread gets shorter with a longer range, whilst I am trying to stop my spread from going all over the place when I fire at a wall at close range.

Are there any alternative solutions that I could try?

It may not appear so but have you tried it?

The problem should be the same with yours more inaccurate at close range because of your vector + method which is the same as the other post.

local dir = RandomVectorOffset((mouse.Hit.p - ShootPart.CFrame.p).Unit, math.rad(30)) 
local ray = Ray.new(ShootPart.CFrame.p, dir * ShotgunConfigs.Range.Value)

I have tried it. I added it like this to my server script, though it may be incorrect? Anyways, this is how it behaves.

https://gyazo.com/30e07fc65fbef9b62aa9d09c4384584c

local tool = script.Parent
local barrel = tool.Handle.Attachment
local remote = script:WaitForChild("GunEvent")
local tweenService = game:GetService("TweenService")
local gunSettings = require(tool.GunSettings)

local bulletPart = game:GetService("ReplicatedStorage"):WaitForChild("Bullet")

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
raycastParams.IgnoreWater = false

local spentBullets = {}
local clearCooldown = false
local clearCooldownTime = 10

remote.OnServerEvent:Connect(function(player, mousePos)	
	--print(tool.Parent)
	
	local origin = barrel.WorldPosition
	local direction = (mousePos - origin).Unit
	
	local spreadDir = RandomVectorOffset(direction, math.rad(5))

	local result = workspace:Raycast(origin, spreadDir * gunSettings.MaxDistance, raycastParams)

	local intersection = result and result.Position or origin + direction * gunSettings.MaxDistance
	local distance = (origin - intersection).Magnitude

	--local minSpread = -gunSettings.MinSpread / 100 * distance
	--local maxSpread = gunSettings.MaxSpread / 100 * distance
	--local spread = Vector3.new(math.random(gunSettings.MinSpread, gunSettings.MaxSpread), math.random(gunSettings.MinSpread, gunSettings.MaxSpread), math.random(gunSettings.MinSpread, gunSettings.MaxSpread))
	--intersection += spread
	
	local bullet = bulletPart:Clone()
	bullet.Transparency = 0
	bullet.Size = Vector3.new(0.1,0.1,distance)
	bullet.CFrame = CFrame.new(origin, intersection)*CFrame.new(0,0, -distance/2)
	bullet.Parent = workspace.GunBullets
	
	if result then
		local part = result.Instance
		local humanoid = part.Parent:FindFirstChild("Humanoid")
		
		print(part.Name)
		if humanoid then
			if part.Name == "Left Arm" or part.Name == "Right Arm" or part.Name == "Left Leg" or part.Name == "Right Leg" then
				humanoid:TakeDamage(gunSettings.LimbDamage)
			end
			
			if part.Name == "Torso" or part.Name == "HumanoidRootPart" then
				humanoid:TakeDamage(gunSettings.TorsoDamage)
			end
			
			if part.Name == "Head" then
				humanoid:TakeDamage(gunSettings.HeadDamage)
			end
		end
		
	end

	changeBullet(bullet)
end)

local rng_v = Random.new()

function RandomVectorOffset(v, maxAngle) --returns uniformly-distributed random unit vector no more than maxAngle radians away from v
	return (CFrame.new(Vector3.new(), v)*CFrame.Angles(0, 0, rng_v:NextNumber(0, 2*math.pi))*CFrame.Angles(math.acos(rng_v:NextNumber(math.cos(maxAngle), 1)), 0, 0)).LookVector
end 

function changeBullet(bullet)
	for count = 1, 10 do
		bullet.Transparency = bullet.Transparency + 0.1
		wait(0.01)
	end
	game:GetService("Debris"):AddItem(bullet, 0)
end


local function onEquipped()
	raycastParams.FilterDescendantsInstances = {tool, tool.Parent, workspace.GunBullets}
end

local function onUnequipped()
	--unequipped function here
end

tool.Equipped:Connect(onEquipped)
tool.Unequipped:Connect(onUnequipped)

You forgot to replace direction with the spreaded version spreadDir when aiming at the sky

I recommend control F and replace all

Ah, silly me. Thank you for pointing out my mistake. I am happy to report that both issues are solved. Thank you and @DEVLocalPlayer for the help! :smile:

1 Like