How to add slight inaccuracy to a raycasting gun

Hi, Tophat here.

I’m working on a weapon system involving some guns. I’ve been wondering on how to cast rays that would be slightly inaccurate to it’s target (like a few degrees off)

Here is an crude diagram explaining what I’m trying to achieve:

A marks the gun’s end where the ray’s origin will be cast.
B marks the target where the player is aiming with the gun.
C would be the “range of inaccuracy” where the ray’s direction may be altered by a few degrees (such as by 5 degrees) The ray can be casted so its direction will be anywhere from the top of the line on the left side of the diagram to the bottom.

So, I ask, how would I do such a thing? I am not that confident with casting rays and certain aspects of CFrames in general.

Below is the code I am using:

script.Parent.WeaponEvent.OnServerEvent:Connect(function(Player, FromP, ToP, Attachment0)
	-- Casts the ray
	local RayCast = Ray.new(FromP,(ToP-FromP).unit*100)
	
	-- Finds the part and position of any object hit by the ray
	local Part,Position,Normal = game.Workspace:FindPartOnRay(RayCast,Player.Character, false,true)
	
	-- Distance value for part that will be created
	local Dist = 300
	
	-- Part that represents ray to do damage to humanoids, with a distance cap
	local RaycastPart = Instance.new("Part")
	RaycastPart.Parent = game.Workspace
	RaycastPart.Anchored = true
	RaycastPart.CanCollide = false
	RaycastPart.Material = Enum.Material.Neon
	RaycastPart.Color = Color3.fromRGB(255, 251, 203)
	RaycastPart.Transparency = 1
	RaycastPart.Size = Vector3.new(0.1,0.1,Dist)
	RaycastPart.CFrame = CFrame.new(FromP,Position)*CFrame.new(0,0,-Dist/2)
	
	game.Debris:AddItem(RaycastPart, 0.025)
	script.Fire:Play()
	
	-- Damages humanoid if part was a humanoid
	if Part and Part.Parent:FindFirstChild("Humanoid") then
		Part.Parent.Humanoid:TakeDamage(script:GetAttribute("Damage"))
	end
	
	-- Creates an attachment to create beam, which visualizes ray
	local Attachment1 = Instance.new("Attachment", workspace.BeamAttacher)
	Attachment1.Name = "RaycastBeamAttachment"
	Attachment1.WorldPosition = Position
	
	-- Creates the beam
	local Beam = Instance.new("Beam", workspace.BeamAttacher)
	Beam.Color = ColorSequence.new{ColorSequenceKeypoint.new(0, Color3.fromRGB(255, 251, 203)), ColorSequenceKeypoint.new(1, Color3.fromRGB(255, 251, 203))}
	Beam.FaceCamera = true
	Beam.Width0 = 0.1
	Beam.Width1 = 0.1
	Beam.LightEmission = 1
	Beam.Transparency = NumberSequence.new{NumberSequenceKeypoint.new(0, 0.3), NumberSequenceKeypoint.new(1, 0.3)}
	
	Beam.Attachment0 = Attachment0
	Beam.Attachment1 = Attachment1
	
	game.Debris:AddItem(Beam, 0.025)
	game.Debris:AddItem(Attachment1, 0.025)
end)

Help is very much appreciated!

1 Like

I suggest you use workspace:Raycast instead of the now deprecated FindPartOnRay. Here is the documentation.

local raycast = workspace:Raycast(FromP, (FromP - ToP).Magnitude)
-- .Magnitude will get the DIRECTION vector between the two positions

local part = raycast.Instance
local position = raycast.Position

And to add a spread, you can just add a Vector3 value to the direction

local spread = Vector3.new(math.random(-5, 5), math.random(-5, 5), math.random(-5, 5))
--this will make the ray be off by a random from 0 to 5 studs
local raycast = workspace:Raycast(FromP, (FromP - ToP).Magnitude + spread)

Hope this helps

EDIT Thanks @NotFaykingBtw, I forgot

4 Likes

I suggest doing math.random(-5, 5).

1 Like

The last line, adding spread to the magnitude, value just gives an error saying “attempt to perform arithmetic on number and vector3”. Thanks for telling me the updated method for raycasting though.

Sorry, my mind is probably working on another project even while answering. It’s just (FromP - ToP) or
(FromP - ToP).Unit * Lenght. The first connects the two parts and the second goes from the FromP towards the ToP, but only for a specified Lenght

1 Like

Yeah, I understand that part. I’m more concerned on how to apply the Vector3 for spread into the ray’s direction, for I’m still quite stumped.

The error you got earlier was that I used .Magnitude, which gets the length of a vector, meaning it’s a simple number. And I tried adding a Vector3 to that, which is not possible. If you remove the Magnitude, so it stays as a Vector3, you can add another Vector3 to it and it shouldn’t print an error (= it should add the spread)

1 Like

After a little bit of reading, this should be the correct code;

local spread = Vector3.new(math.random(-5, 5), math.random(-5, 5), math.random(-5, 5))
--this will make the ray be off by a random from -5 to 5 studs

local raycast = workspace:Raycast(FromP, ((ToP - FromP).Unit + spread)*100)

Tell me if theres any errors, i’m currently going to open Roblox Studio…

1 Like

The spread works now! But for some reason, the line “local part = raycast.Instance” gives the error “attempt to index nil with instance”. The weird part is that the script works part of the time. Maybe it’s because I’m firing this event at a fast rate (I’m testing on an gun with a high firerate)

FromP, ((ToP - FromP).Unit + spread)*100
If you do it like this, first its going to apply the spread and then multiply it, meaning the spread is going to be bigger (I mean a LOT bigger)

If you do
FromP, (ToP - FromP).Unit * 100 + spread
It will first make the length 100 studs and then add 5 studs of spread (well actually 10 cuz 5 in both direction) to it. Use the one you need

You get that error because the spread is huge, and the raycast didn’t hit anything. To check if there’s a hit, you can do this

if raycast.Instace then -- check if the Instance exists = if the ray hit anything
	local part = raycast.Instance
end

This shouldn’t throw an error

3 Likes

Yes, i was thinking about that, after a bit of testing; yes, my solution didn’t work.

EDIT:
@WoTrox Solution was correct;


local spread= 0 -- lower is more accurate
local distance = (ToP-FromP).Magnitude

local offset = Vector3.new(
	math.random(-100,100),
	math.random(-100,100),
	math.random(-100,100)
) / 1000 * distance * spread

local RayCast = Ray.new(FromP, (ToP-FromP+offset).Unit*100)

you need to multiply by the ray distance for the spread to be consistent across multiple ranges.

2 Likes