You can simulate spread by ‘rotating’ the direction of the weapon ray. This will mean that there will be much more spread from further away, while weapons will be more accurate the closer they are to the target.
- Set up a ‘spread’ value for your weapon. For example, my AK47 has a spread of 2.
When the player fires their gun:
-
Generate two random number ranging from -spread to spread; one for ‘x’ and one for ‘y’. The best way to do this is using the Random datatype
-
Generate a starting CFrame, with the weapon’s muzzle as its position, which faces the mouse’s position. You can do this easily using the CFrame.new(position, look_towards) constructor. This CFrame will only be used for the direction of the ray.
-
Rotate the target CFrame by your random ‘x’ and random ‘y’ values, in radians.
-
Now you can generate the ray for the weapon, which now has spread added to it. The direction of the ray you want to cast is the LookVector of the CFrame you constructed; this is simply a CFrame that faces the mouse’s position, but with rotation to emulate spread.
-
Construct a new ray. The origin of the ray should be the actual muzzle position of the gun; i.e. where bullets originate from. The direction of the ray should be the LookVector above, multiplied by the range of the weapon (e.g. 500 studs). This is because the LookVector is a unit vector with a magnitude of 1, so multiplying it by the weapon range, 500, creates a ray that starts from the muzzle, and projects in the direction of the mouse (with spread) for 500 studs only.
Here is an example of my code.
local muzzle_pos = weapon_muzzle.Position
local spread = weapon_data.Spread
local random_generator = Random.new()
local spread_x = random_generator:NextNumber(-spread, spread)
local spread_y = random_generator:NextNumber(-spread, spread)
local target_cframe = CFrame.new(muzzle_pos, Mouse.Hit)
local spread_rotation = CFrame.fromEulerAnglesXYZ(
math.rad(spread_x),
math.rad(spread_y),
0
)
target_cframe = target_cframe * spread_rotation
local direction = target_cframe.LookVector
local ray = Ray.new(
muzzle_pos,
direction * weapon_data.Range
)
Further resources
-
Battle Royale – good open sourced shooter for learning.
-
Making a raycasting gun - very very simple tutorial, which you could use to clarify any confusions about raycasting.
-
[Understanding CFrames](CFrames | Documentation - Roblox Creator Hub