How would I do shotgun raycasts?

this is gonna be a hefty topic, i think

i’ve made the decision to hop from a pistol to a shotgun (bad idea), and my main obstacle is raycasting

i’ve worked with raycasts once (and it went horribly), so i really don’t know how to approach this

other than raycasting on the server, i have 2 questions:

  1. how would i make multiple raycasts fire in a cone shape?
  2. will multiple raycasts at once cause framerate issues?

i’ve made a very barebones shotgun to try(?) and give you an idea

you can repeat raycasting 8 times and give it a high spread

1 Like
  1. The raycast origin is your camera position / gun barrel, depending on where you want to shoot from
  2. The direction is the camera look vector / gun barrel look vector
  3. Add spread by adding a Vector3 to the direction. The X value is the horizontal spread, and the Y value is the vertical spread. You can ignore the Z value.
  4. Use RaycastParams to ignore the player’s character, so it’s not detected by the raycast
  5. Do a new raycast for each pellet

No, raycasts don’t cause much lag


Code example:

local PELLETS = 8
local HORIZONTAL_SPREAD = NumberSequence.new(-0.2, 0.2) -- Change these to 1 variable if you want
local VERTICAL_SPREAD = NumberSequence.new(-0.2, 0.2)

local origin = '...' -- Get the origin from the player's character / elsewhere

local raycastParams = RaycastParams.new()
raycastParams.FilterDescendantsInstances = { character } --TODO Get the character from the player
raycastParams.IgnoreWater = true
raycastParams.FilterType = Enum.RaycastFilterType.Exclude


local function shoot()
	-- Whatever else you're doing

	for pellet=1, PELLETS do
		local spreadX = HORIZONTAL_SPREAD.Min + (math.random() * (HORIZONTAL_SPREAD.Max - HORIZONTAL_SPREAD.Min))
		local spreadY = VERTICAL_SPREAD.Min + (math.random() * (VERTICAL_SPREAD.Max - VERTICAL_SPREAD.Min))
		
		local result = workspace:Raycast(
			origin, -- Raycast origin (gun barrel)
			origin.CFrame.LookVector + Vector3.new(spreadX, spreadY),
			raycastParams
		)
	end
end
1 Like

Here is a simple script I whipped up. There is most likely more sophistacted methods you can do to achieve the spread, but here it is:

local player = game.Players.LocalPlayer
local character = player.Character or player.CharacterAdded:Wait()

local raycastParams = RaycastParams.new()
raycastParams.FilterType = Enum.RaycastFilterType.Exclude
raycastParams.FilterDescendantsInstances = {character} -- don't detect the shooter's character, it might get in the way
raycastParams.IgnoreWater = true

local mouse = player:GetMouse()

local MAX_DISTANCE = 50
local BULLETS = 8
local SPREAD = 10

local function shoot(targetCF)
	local origin = character.RightHand.CFrame * CFrame.new(0,0,-character.RightHand.Size.Z/2) -- Position at the edge of right hand

	for i = 1, BULLETS do
		local targetCF = targetCF * CFrame.new(math.random(-SPREAD, SPREAD), math.random(-SPREAD, SPREAD), 0)
		local direction = -(origin.Position - targetCF.Position).Unit * MAX_DISTANCE
		
		local part = script.Part:Clone()
		part.Size = Vector3.new(.2,.2, direction.Magnitude)
		part.CFrame = CFrame.lookAt(origin.Position, targetCF.Position) * CFrame.new(0,0,-part.Size.Z/2)
		part.Parent = workspace
		game.Debris:AddItem(part, 3)
		
		local raycastResult = workspace:Raycast(origin.Position, direction, raycastParams)
		if raycastResult then
			print("Hit!")
		end
	end
end


game:GetService("UserInputService").InputBegan:Connect(function(input, gpe)
	
	if not gpe and input.UserInputType == Enum.UserInputType.MouseButton1 then
		shoot(mouse.Hit)
	end
end)

2 Likes

hey,

sorry to bother, but how would i get direction and make that direction have a distance?

my current code looks like this

-- this is inside an OnServerEvent event
local mossberg = player.Character:FindFirstChildOfClass("Tool")
	local damage = 11
	
	local PELLETS = 8
	local HORIZONTAL_SPREAD = {min = -0.2, max = 0.2}
	local VERTICAL_SPREAD = {min = -0.2, max = 0.2}
	local MAX_DISTANCE = 50

	local origin = mossberg:WaitForChild("Flash")

	local raycastParams = RaycastParams.new()
	raycastParams.FilterDescendantsInstances = { player.Character }
	raycastParams.IgnoreWater = true
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude


	local function shoot()
		for pellet=1, PELLETS do
			local spreadX = HORIZONTAL_SPREAD.min + (math.random() * (HORIZONTAL_SPREAD.max - HORIZONTAL_SPREAD.min))
			local spreadY = VERTICAL_SPREAD.min + (math.random() * (VERTICAL_SPREAD.max - VERTICAL_SPREAD.min))

			local result = workspace:Raycast(
				origin.Position, 
				origin.CFrame.LookVector + Vector3.new(spreadX, spreadY),
				raycastParams
			)
			
			local tracer = Instance.new("Part")
			tracer.Size = Vector3.new(0.1,0.1, origin.CFrame.LookVector)
			tracer.Parent = workspace
			tracer.CFrame = CFrame.lookAt(origin.Position, origin.Position, origin.CFrame.LookVector)
			tracer.Anchored = true
			tracer.Color = Color3.new(0.960784, 0.666667, 0.156863)
			
			
			
			if result ~= nil then
				print(result)
				local enemy = result.Instance.Parent:FindFirstChild("Humanoid")
				
				if enemy == game:GetService("Players"):GetPlayerFromCharacter(result.Instance.Parent) then continue end
				
				enemy:TakeDamage(damage)
				DamageGUIEvent:FireClient(player, damage, enemy)
			end
		end
	end

Put your current direction in brackets and multiply it by MAX_DISTANCE like this:

local result = workspace:Raycast(
	origin.Position, 
	(origin.CFrame.LookVector + Vector3.new(spreadX, spreadY)) * MAX_DISTANCE,
	raycastParams
)
1 Like

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