Raycasts more accurate with more distance

I am trying to make a weapon system that relies on raycasts, I am trying to make it so raycasts have “spread” to where they aren’t deadshot. but when testing i noticed that raycasts seem to become more accurate the further i am from my target. as the raycasts become closer together and the spread becomes smaller.

In general my method of making the spread is very buggy and unreliable, I would like to find a better method to do this especially the spread system.

here is the code where spread should take place in, also i’m using beams for tracers which tracers are also buggy.

	for i = 0, Settings["BurstAmount"] do
		task.wait(Settings["BurstDelay"])

		local rp = RaycastParams.new()
		rp.IgnoreWater = true
		rp.FilterType = Enum.RaycastFilterType.Blacklist
		rp.FilterDescendantsInstances = {}

		for j = 0, Settings["BulletsPerShot"] - 1 do
			task.spawn(function()
				local spreadX = math.rad(math.random(-spread, spread))
				local spreadY = math.rad(math.random(-spread, spread))
				local Direction = CFrame.new(BarrelAttachment.Position, Hit and Hit.Position or Vector3.new()) * CFrame.Angles(spreadX, spreadY, 0)
				-- Apply spread to the direction vector
				local raycastResult = Ray.new(BarrelAttachment.Position, Direction.LookVector * Settings["Range"], rp)

				if Hit then
					local Hum = Hit.Parent:FindFirstChild("Humanoid")
					if Hum then
						local TracerClone = Tracer:Clone()
						TracerClone.Parent = workspace
						TracerClone.START.WorldPosition = BarrelAttachment.WorldPosition
						TracerClone.END.WorldPosition = raycastResult

						if raycastResult then
							print("Result!")
						else
							print("Neither!")
						end

						TracerClone.Beam.Enabled = true

						task.wait(0.1)
						TracerClone:Destroy()
					else
						print("Miss")
					end
				end
			end)
		end
	end

If I need to share more code let me know.

How is spread determined?
math.random is an integer, so if your spread is only set to 1 then it’ll only be -1, 0 or 1. If it’s a decimal lower than .5 it’s only going to give a result of 0.

Something else you can do is print the spreadX and spreadY values after the calculations to show you what your values are going to be.


this is because you’re creating the spread by randomizing the area around the reticle/crosshair world position, which will make the spread narrower (therefore more accurate) the further away the world position is from the barrel attachment

here is a cool function that can be used to generate a spread pattern

1 Like

I don’t really understand this and how it works.

I attempted to use it within my code however it just came out like this, I likely put it in incorrectly so it just came out like this:

I don’t really understand how it’s supposed to work.
here is the current code

the problem causing it is likely easy to find for you but not me since I am clueless when it comes to this as doing this stuff is new to me.

function:

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

Spread:

	for j = 0, Settings["BulletsPerShot"] - 1 do
					print("SHOT")
				task.spawn(function()
					local spreadX = math.rad(math.random(-spread, spread))
					local spreadY = math.rad(math.random(-spread, spread))
					local Direction = CFrame.new(BarrelAttachment.Position, Hit and Hit.Position or Vector3.new()) * CFrame.Angles(spreadX, spreadY, 0)
					-- Apply spread to the direction vector
					local ray, newraylength
					local Parthit, Parthitpos = game.Workspace:FindPartOnRay(ray)
					local reflectedDirection, newRay

					local TracerClone = Tracer:Clone()
					TracerClone.Parent = workspace
					TracerClone.START.WorldPosition = BarrelAttachment.WorldPosition
					TracerClone.END.WorldPosition = ray.Origin + ray.Direction.magnitude * ray.Direction.Unit
					if Parthit then
						print("HIT")
					reflectedDirection = RandomVectorOffset(-ray.Direction, math.rad(30)) 
						newRay = Ray.new(Parthitpos, reflectedDirection*Settings["Range"])
					end
						

					--if Hit then
					--	local Hum = Hit.Parent:FindFirstChild("Humanoid")
					--	if Hum then
					--		local TracerClone = Tracer:Clone()
					--		TracerClone.Parent = workspace
					--		TracerClone.START.WorldPosition = BarrelAttachment.WorldPosition
					--		TracerClone.END.WorldPosition = raycastResult

					--		if raycastResult then
					--			print("Result!")
					--		else
					--			print("Neither!")
					--		end

					--		TracerClone.Beam.Enabled = true

					--		task.wait(0.1)
					--		TracerClone:Destroy()
					--	else
					--		print("Miss")
					--	end
					--end
				end)
			end
		end
	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

local function GenerateSpread(InitialDirection: Vector3, ShotCount: number, Angle: number): {Vector3}
	local SpreadDirections = {}
	for i = 1, ShotCount do
		table.insert(SpreadDirections, RandomVectorOffset(InitialDirection, math.rad(Angle)))
	end
	return SpreadDirections
end

in your case:

local P0 = BarrelAttachment.WorldPosition
local P1 = ... --[[mouse world position]]
local Spread = ... --[[gun firerate spread]]
local MaxDistance = ... --[[tracer max distance]]
local InitialDirection = (P1 - P0).Unit
local SpreadDirections = GenerateSpread(InitialDirection, Settings["BulletsPerShot"], Spread)

for _, UnitVector in SpreadDirections do
	local TracerClone = Tracer:Clone()
	TracerClone.Parent = workspace
	TracerClone.START.WorldPosition = P0
	TracerClone.END.WorldPosition = P0 + (UnitVector * MaxDistance)
end

Oh thank you it worked, just one last thing to ask. since I am making the weapon rely on raycasts i suppose i’ll just do the raycasts similar to how i did the tracers if I am correct?

yes

ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ

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