Raycasting Gun Spread

Hello im working on a spread system but this is good for shotguns , i want to make it for weapons like AK-47and pistols and etc, for when you move so something like CSGO’s spread and Valorant’s Spread.
But I am bad at math and don’t know how to make a spread

Can someone help, please?

This is on a different studio using a tool but when i finish and see the spread works the way i want it to be il be importing it to my framework.

Using fastcast btw

This is the source


local fireEvent = tool.FireEvent

local FastCast = require(game.ReplicatedStorage.FastCastRedux)

local PartCache = require(game.ReplicatedStorage.PartCache)

local pellet = game:GetService("ServerStorage").Pellet

local pelletsFolder = workspace.PelletsFolder

local firePoint = tool.Handle.FirePoint

FastCast.VisualizeCasts = true

local MAX_DIST = 500

local MIN_SPREAD = 1

local MAX_SPREAD = 4

local caster = FastCast.new()

local provider = PartCache.new(pellet, 100, pelletsFolder)

local castParams = RaycastParams.new()

castParams.FilterType = Enum.RaycastFilterType.Blacklist

local behavior = FastCast.newBehavior()

behavior.RaycastParams = castParams

behavior.CosmeticBulletProvider = provider

behavior.CosmeticBulletContainer = pelletsFolder

behavior.Acceleration = Vector3.new(0, -workspace.Gravity, 0)

local function GetMousePos(unitRay)

local ori, dir = unitRay.Origin, unitRay.Direction * MAX_DIST

local result = workspace:Raycast(ori, dir, castParams)

return result and result.Position or ori + dir

end

local function Fire(direction)

local directionCF = CFrame.new(Vector3.new(), direction)

local spreadDirection = CFrame.fromOrientation(0, 0, math.random(0, math.pi * 2))

local spreadAngle = CFrame.fromOrientation(math.rad(math.random(MIN_SPREAD, MAX_SPREAD)), 0, 0)

local direction = (directionCF * spreadDirection * spreadAngle).LookVector

caster:Fire(firePoint.WorldPosition, direction, 1000, behavior)

end

tool.Equipped:Connect(function()

castParams.FilterDescendantsInstances = {tool.Parent}

end)

fireEvent.OnServerEvent:Connect(function(player, ray)

if tool.Parent:IsA("Backpack") then return end

local pos = GetMousePos(ray)

local direction = (pos - firePoint.WorldPosition).Unit

for i = 1, 10 do

Fire(direction)

end

end)

caster.LengthChanged:Connect(function(cast, lastPoint, dir, displacment, segVel, pellet)

pellet.CFrame = CFrame.lookAt(lastPoint + (dir * displacment), lastPoint)

end)

caster.RayHit:Connect(function(cast, result, segVel, pellet)

if result.Instance then

local character = result.Instance:FindFirstAncestorWhichIsA("Model")

local humanoid = character and character:FindFirstChild("Humanoid")

if humanoid then

humanoid:TakeDamage(10)

provider:ReturnPart(pellet)

else

wait(1)

provider:ReturnPart(pellet)

end

end

end)```
1 Like

It looks like you already have some code to spread the gun. What does it currently do?

1 Like

It suits a shotgun tho i want the spread to be like Valorant or CSGO’s like when you move and spread changes unless you stand still or spread changes when you shoot alot without stopping and waiting for gun to cool down

1 Like

Your gonna want to use this formula.

merge some of this with your existing code.

local RNG = Random.new() -- Put these two variables on top, somewhere near all your other variables.
local TAU = math.pi * 2

local directionalCF = CFrame.new(Vector3.new(), dir) -- put your dir variable here
local direction = (directionalCF * CFrame.fromOrientation(0, 0, RNG:NextNumber(0, TAU)) * CFrame.fromOrientation(math.rad(RNG:NextNumber(MIN_SPREAD, MAX_SPREAD)), 0, 0)).LookVector

You may have to do some tweaking to make this work with your code.
Dont worry it shouldnt be any math tweaks, just how you structure the code.

1 Like

thank you bro let me try it out rq

	local RNG = Random.new() -- Put these two variables on top, somewhere near all your other variables.
	local TAU = math.pi * 2

	local directionalCF = CFrame.new(Vector3.new(), direction) -- put your dir variable here
	local spreadDirection = CFrame.fromOrientation(0, 0, math.random(0, math.pi * 2))
	local spreadAngle = CFrame.fromOrientation(math.rad(math.random(MIN_SPREAD, MAX_SPREAD)), 0, 0)
	local direction = (directionalCF * CFrame.fromOrientation(0, 0, RNG:NextNumber(0, TAU)) * CFrame.fromOrientation(math.rad(RNG:NextNumber(MIN_SPREAD, MAX_SPREAD)), 0, 0)).LookVector

	caster:Fire(firePoint.WorldPosition, direction, 1000, behavior)
end```

make sure to multiply the direction by some speed.
if you have a bullet speed do that

direction * bulletSpeed

2 Likes

how could i make the spread like valorant / csgo tho? have any idea?

oh i forgot to mention the min and max have to be positive.

it will make min negative
and max positive automatically

so instead of

local MIN_SPREAD = 1
local MAX_SPREAD = 4

it should be

local MIN_SPREAD = -1
local MAX_SPREAD = 4```
local MIN_SPREAD = 0
local MAX_SPREAD = 4

-- keep BOTH positive.
-- 0 minspread = it has a chance to be 100% accurate. its like a range.
-- if you NEVER want it to be 100% accurate change it to 1 or some number above 0

Yeah but in my code min_spread and max_Spread is like bullets so it has a chance of being 0 bullets shot or 4 bullets shot tho i want it to be like you can change the spread of how much it moves.

oh nvm im dumb its not lmao mb

okay so i have a idea of how to make the spread like valorant
I could make it so whenever the script detects the player move the max_Spread will increase by +1 and if the player stops movin then the max_Spreads goes back to 4?
But how would i detect the player moving

to detect player moving you would use humanoid.MoveDirection

sorta like this

humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
if humanoid.MoveDirection.Magnitude > .1 then
-- player is moving
end
end)
		if HRP.MoveDirection.Magnitude > .1 then
			MAX_SPREAD = MAX_SPREAD + 1-- player is moving
		else
			MAX_SPREAD = 4
		end
	end)```

yeah that should work.

but if the movedirection keeps changing its going to get bigger and bigger (very big).

so your gonna want too maybe make a bool that says if its moving or not.

also make sure ur using humanoid and not humanoidrootpart

local isMoving = false

humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
 if humanoid.MoveDirection.Magnitude > .1 then
  if isMoving == false then
   isMoving = true
   MAX_SPREAD = MAX_SPREAD + 1-- player is moving
  end
 else
  MAX_SPREAD = 4
 end
end)

or a simpler way is just to set MAX_SPREAD to 5 or 6 or something without adding it

yeah it says nil when its trying to find character

local player = game:GetService("Players").LocalPlayer
local Character = player.Character or player.CharacterAdded:Wait()
local Humanoid = Character:WaitForChild("Humanoid")```

where are u putting the script.
is it localscript or serverscript?

if its not localscript then it wont work. you need to use a remote event or something to find the player.

yh i did this

	game.Players.ChildAdded:Connect(function(player)
		player.CharacterAdded:Wait()
		local humanoid = player.Character:WaitForChild("Humanoid")
		humanoid:GetPropertyChangedSignal("MoveDirection"):Connect(function()
			if humanoid.MoveDirection.Magnitude > .1 then
				if isMoving == false then
					isMoving = true
					MAX_SPREAD = MAX_SPREAD + 1-- player is moving
				end
			else
				MAX_SPREAD = 4
			end
		end)

but when i test it out the spread doesnt seem to increase hmm but theres no error tho