Is this Raycasting circular spread code ok?

After researching and combining trying many sources from the dev forums about raycast spread, I need an opinion about this raycasting spread script:

local up = Vector3.new(0, 1, 0)
gmod.Spread = 10

local function newDirection(direction)
	direction = direction.Unit

	local angle = math.random()*math.pi*2
	local axisA = up:Cross(direction)
	local axisB = direction:Cross(axisA)
	
	return direction + gmod.Spread/1000*(math.cos(angle)*axisA + math.sin(angle)*axisB)
end

-- The gun itself:
local spreadInterval = (mousepos - self.Handle.Position).magnitude
				
local sprd = spreadInterval * gmod.Spread
local startPos	= chr:WaitForChild('Head').CFrame.p
				
-- Create Ray
local direction = newDirection(mousepos - startPos) * gmod.Distance
local ray = Ray.new(startPos, direction)
local part, position = workspace:FindPartOnRayWithIgnoreList(ray, {chr,workspace.IgnoreTable}, false, true)
local distance = (self.Handle.CFrame.p - position).magnitude
local B_CFrame = CFrame.new(self.Handle.Position,position) * CFrame.new(0,0,-distance/2) -- Final position
--

I end up having something like this (which is a circular spread):
https://gyazo.com/9f5455f3df55a9c30527a2ab87ab22bb.gif

2 Likes

Here’s my favorite way to format a spread function:

local function RandomSpreadAt100Studs(cf, spread_diameter)
   local target_cf = cf * CFrame.new(0, 0, -100) -- Get a CFrame 100 studs ahead
      * CFrame.Angles(0, 0, math.pi * 2 * math.random()) -- Turn CFrame randomly on  the Z axis
      * CFrame.new(0, math.random() * spread_diameter / 2, 0) -- Pick a random spread distance from the origin ray
   return CFrame.new(cf.Position, target_cf.Position)
end
8 Likes

What is cf exactly in the parameters?

1 Like

I meant it refers to, it’s really frustrating right now that I can’t understand…

1 Like

cf refers to the projectile origin CFrame (fire position + fire direction)

One way to construct an origin CFrame is through the CFrame.new(Vector3, Vector3) constructor:

local origin_cf = CFrame.new(Head.Position, mousepos)

My function outputs a CFrame which can then be deconstructed into a direction vector by reading its LookVector:

print(RandomSpreadAt100Studs(origin_cf, 5).LookVector) --> eg. 0.5,-0.1,0.8 (Vector3)

You can read more about CFrames on the developer hub - CFrame API can help you solve 3D math problems with way less effort

2 Likes

Thanks a lot for sharing your method with me! :slight_smile:

1 Like

Sorry for another bump, but i’ll appreciate if you could explain to me how/why this works exactly, why do you get a cframe 100 studs ahead, and turn it randomly on the Z axis and not any other axis, and why do you pick the random spread in the Y axis.

Brings up my question of: Does the spread depend on Y/Z and not X?

1 Like

CFrame.new(pos, look) is actually deprecated.

Instead, you should use CFrame.fromMatrix():

function lookAt(pos, look)
    local forwardVector = (pos - look).Unit
    local rightVector = forwardVector:Cross(Vector3.new(0, 1, 0))
    local upVector = rightVector:Cross(forwardVector)
    return CFrame.fromMatrix(pos, rightVector, upVector)
end
6 Likes

I never heard about this term/that it’s deprecated, quite surprising!

1 Like