Shotgun raycast does a wack

instead of bumping one of my month-old posts, i’ll just make a new one

note that i am very bad at math so don’t expect me to know what to do when you say “apply cos and sin to distance”

anyway, there are 2 known issues, 1 is shown, the other isn’t (and i’m about to explain the other)


issue 1 is uh, the raycasts don’t fire at the same time and instead go 1 by 1:


don’t mind the errors in the output, they’re unrelated to shooting


onto issue 2 (feat. horrible explanation)

The raycasts are incredibly inconsistent, the tracers have spread applied onto them very much properly, but the raycasts themselves for some reason spaz out and don’t actually follow along.

i don’t have a video of this sadly (because i noticed this a long time ago) but i think one of you should be able to notice the issue in my beautiful code (Trademark)

elseif tool.GunType.Value == "SHOTGUN" then
		local gun = tool
		local bullets = tool.Bullets

		local flashPart = gun.FlashPart
		local shootSound = flashPart.ShootSound
		local muzzleFlash = flashPart.Light
		local flashImage = flashPart.FlashImage

		local BODYSHOT_DAMAGE = gunInfo[tool.Name]["BULLET_DAMAGE"]
		local HEADSHOT_DAMAGE = gunInfo[tool.Name]["HEADSHOT_DAMAGE"]
		local FIRERATE = gunInfo[tool.Name]["FIRERATE"]

		if tool.Shooting.Value then player:Kick("You need more power.") return end
		if bullets.Value < 1 then player:Kick("Your manipulation techniques are atrocious.") return end
		if bullets.Value > gunInfo[tool.Name]["MAX_BULLETS"] then player:Kick("The underworld calls to you.") return end

		tool.Shooting.Value = true
		bullets.Value -= 1

		shootSound:Play()

		coroutine.wrap(function() -- coroutine to prevent yielding 
			muzzleFlash.Enabled = true
			flashImage.Enabled = true
			task.wait(0.1) 
			muzzleFlash.Enabled = false
			flashImage.Enabled = false
		end)()

		local PELLETS = 8
		local HORIZONTAL_SPREAD = {min = -0.2, max = 0.2}
		local VERTICAL_SPREAD = {min = -0.2, max = 0.2}
		local MAX_DISTANCE = gunInfo[tool.Name]["MAX_DISTANCE"]

		for pellet=1, PELLETS do
			local ran = Random.new()

			local spreadX = ran:NextNumber(HORIZONTAL_SPREAD.min, HORIZONTAL_SPREAD.max)
			local spreadY = ran:NextNumber(VERTICAL_SPREAD.min, VERTICAL_SPREAD.max)

			local direction = (hitPos + Vector3.new(spreadX, spreadY)).Unit
			local displacement = direction * MAX_DISTANCE

			local origin = flashPart.Position 
			local raycastParams = RaycastParams.new()
			
			raycastParams.FilterDescendantsInstances = {player.Character}
			raycastParams.IgnoreWater = true
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			
			local direction = (hitPos - origin).Unit
			local displacement = direction * gunInfo[tool.Name]["MAX_DISTANCE"]
			local result = workspace:Raycast(
				origin,
				displacement,
				raycastParams
			)
			
			local endPos = nil
			
			if result then
				endPos = result.Position
				local character = result.Instance.Parent
				local humanoid = character:FindFirstChild("Humanoid")

				if humanoid ~= players:GetPlayerFromCharacter(character) then 
					if result.Instance == character.Head then
						humanoid:TakeDamage(HEADSHOT_DAMAGE)
						DamageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
					else
						humanoid:TakeDamage(BODYSHOT_DAMAGE)
						DamageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
					end
				end

			else
				endPos = origin + displacement
			end
			
			-- // TRACERS

			local tracer = Instance.new("Part")
			tracer.Parent = workspace
			tracer.Anchored = true
			tracer.CanCollide = false
			tracer.CanQuery = false
			tracer.Transparency = 0.6
			tracer.Color = Color3.new(0.960784, 0.666667, 0.156863)
			dService:AddItem(tracer, 0.2)

			local tracerLength = (endPos - origin).Magnitude
			tracer.Size = Vector3.new(0.07, 0.07, tracerLength)
			tracer.CFrame = CFrame.lookAt(origin + (endPos - origin) / 2, endPos)

			task.wait(FIRERATE)
			tool.Shooting.Value = false

		end

unsolved?!

do not worry

bump :point_right:

1 Like

try removing the wait inside this loop

			tracer.CFrame = CFrame.lookAt(origin + (endPos - origin) / 2, endPos)

			task.wait(FIRERATE) -- remove this
			tool.Shooting.Value = false

can you explain what the second issue is a bit more?

eep sorry i got distracted

that wait will prevent the raycasts firing 1 by 1, but it is needed to check for firerate on the server (anti-exploit)

i managed to fire all the raycasts at once before, but that was months ago and i do not remember how i did it at all

regarding that second issue, i’ll try to dig up a video, hold on

1 Like

i think u just need to move the wait outside of the for loop like this i move it to the bottm:

for pellet=1, PELLETS do
	local ran = Random.new()

	local spreadX = ran:NextNumber(HORIZONTAL_SPREAD.min, HORIZONTAL_SPREAD.max)
	local spreadY = ran:NextNumber(VERTICAL_SPREAD.min, VERTICAL_SPREAD.max)

	local direction = (hitPos + Vector3.new(spreadX, spreadY)).Unit
	local displacement = direction * MAX_DISTANCE

	local origin = flashPart.Position 
	local raycastParams = RaycastParams.new()

	raycastParams.FilterDescendantsInstances = {player.Character}
	raycastParams.IgnoreWater = true
	raycastParams.FilterType = Enum.RaycastFilterType.Exclude

	local direction = (hitPos - origin).Unit
	local displacement = direction * gunInfo[tool.Name]["MAX_DISTANCE"]
	local result = workspace:Raycast(
		origin,
		displacement,
		raycastParams
	)

	local endPos = nil

	if result then
		endPos = result.Position
		local character = result.Instance.Parent
		local humanoid = character:FindFirstChild("Humanoid")

		if humanoid ~= players:GetPlayerFromCharacter(character) then 
			if result.Instance == character.Head then
				humanoid:TakeDamage(HEADSHOT_DAMAGE)
				DamageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
			else
				humanoid:TakeDamage(BODYSHOT_DAMAGE)
				DamageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
			end
		end

	else
		endPos = origin + displacement
	end

	-- // TRACERS

	local tracer = Instance.new("Part")
	tracer.Parent = workspace
	tracer.Anchored = true
	tracer.CanCollide = false
	tracer.CanQuery = false
	tracer.Transparency = 0.6
	tracer.Color = Color3.new(0.960784, 0.666667, 0.156863)
	dService:AddItem(tracer, 0.2)

	local tracerLength = (endPos - origin).Magnitude
	tracer.Size = Vector3.new(0.07, 0.07, tracerLength)
	tracer.CFrame = CFrame.lookAt(origin + (endPos - origin) / 2, endPos)

end

task.wait(FIRERATE)
tool.Shooting.Value = false
1 Like

I FOUND IT

can you send the part of the code where the error is happening (lines 40-60)

oh wow, i love overlooking the most visible issues, thanks

1 Like

oh no i forgot to mention that’s also outdated script (well not really)

i’ve moved the entire shotgun raycasting code to a different place because i was rewriting the weapon system

but i suspect the issue happens in this section:

local ran = Random.new()

			local spreadX = ran:NextNumber(HORIZONTAL_SPREAD.min, HORIZONTAL_SPREAD.max)
			local spreadY = ran:NextNumber(VERTICAL_SPREAD.min, VERTICAL_SPREAD.max)

			local direction = (hitPos + Vector3.new(spreadX, spreadY)).Unit
			local displacement = direction * MAX_DISTANCE

			local origin = flashPart.Position 
			local raycastParams = RaycastParams.new()
			
			raycastParams.FilterDescendantsInstances = {player.Character}
			raycastParams.IgnoreWater = true
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			
			local direction = (hitPos - origin).Unit
			local displacement = direction * gunInfo[tool.Name]["MAX_DISTANCE"]
			local result = workspace:Raycast(
				origin,
				displacement,
				raycastParams
			)

i dont see any prints in that part of the code. are you even getting the error anymore? also, the error is old because it was a studio bug according to this post

if you arent getting it anymore that means roblox fixed it.

nooo that error is related to the NPC

the video shows how inconsistent the raycasts are (printing nil and stuff)

and the script is the exact same, which is what i sent above

ohhhh sorry i didnt even notice that :sweat_smile:

do you mind sending the ENTIRE script that handles the pellets?

i mean, i already have, but i’ll resend it to prevent scrolling

i should probably elaborate;
this is inside of 1 server script, and that 1 server script handles shooting and decides what to do based on the guntype (automatic, semi, shotgun)

every piece of gun has its own seperate raycasting code, if that makes sense

this is the entire shotgun section:

elseif tool.GunType.Value == "SHOTGUN" then
		local gun = tool
		local bullets = tool.Bullets

		local flashPart = gun.FlashPart
		local shootSound = flashPart.ShootSound
		local muzzleFlash = flashPart.Light
		local flashImage = flashPart.FlashImage

		local BODYSHOT_DAMAGE = gunInfo[tool.Name]["BULLET_DAMAGE"]
		local HEADSHOT_DAMAGE = gunInfo[tool.Name]["HEADSHOT_DAMAGE"]
		local FIRERATE = gunInfo[tool.Name]["FIRERATE"]

		if tool.Shooting.Value then player:Kick("You need more power.") return end
		if bullets.Value < 1 then player:Kick("Your manipulation techniques are atrocious.") return end
		if bullets.Value > gunInfo[tool.Name]["MAX_BULLETS"] then player:Kick("The underworld calls to you.") return end

		tool.Shooting.Value = true
		bullets.Value -= 1

		shootSound:Play()

		coroutine.wrap(function() -- coroutine to prevent yielding 
			muzzleFlash.Enabled = true
			flashImage.Enabled = true
			task.wait(0.1) 
			muzzleFlash.Enabled = false
			flashImage.Enabled = false
		end)()

		local PELLETS = 8
		local HORIZONTAL_SPREAD = {min = -0.2, max = 0.2}
		local VERTICAL_SPREAD = {min = -0.2, max = 0.2}
		local MAX_DISTANCE = gunInfo[tool.Name]["MAX_DISTANCE"]

		for pellet=1, PELLETS do
			local ran = Random.new()

			local spreadX = ran:NextNumber(HORIZONTAL_SPREAD.min, HORIZONTAL_SPREAD.max)
			local spreadY = ran:NextNumber(VERTICAL_SPREAD.min, VERTICAL_SPREAD.max)

			local direction = (hitPos + Vector3.new(spreadX, spreadY)).Unit
			local displacement = direction * MAX_DISTANCE

			local origin = flashPart.Position 
			local raycastParams = RaycastParams.new()
			
			raycastParams.FilterDescendantsInstances = {player.Character}
			raycastParams.IgnoreWater = true
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			
			local direction = (hitPos - origin).Unit
			local displacement = direction * gunInfo[tool.Name]["MAX_DISTANCE"]
			local result = workspace:Raycast(
				origin,
				displacement,
				raycastParams
			)
			
			local endPos = nil
			
			if result then
				endPos = result.Position
				local character = result.Instance.Parent
				local humanoid = character:FindFirstChild("Humanoid")

				if humanoid ~= players:GetPlayerFromCharacter(character) then 
					if result.Instance == character.Head then
						humanoid:TakeDamage(HEADSHOT_DAMAGE)
						DamageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
					else
						humanoid:TakeDamage(BODYSHOT_DAMAGE)
						DamageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
					end
				end

			else
				endPos = origin + displacement
			end
			
			-- // TRACERS

			local tracer = Instance.new("Part")
			tracer.Parent = workspace
			tracer.Anchored = true
			tracer.CanCollide = false
			tracer.CanQuery = false
			tracer.Transparency = 0.6
			tracer.Color = Color3.new(0.960784, 0.666667, 0.156863)
			dService:AddItem(tracer, 0.2)

			local tracerLength = (endPos - origin).Magnitude
			tracer.Size = Vector3.new(0.07, 0.07, tracerLength)
			tracer.CFrame = CFrame.lookAt(origin + (endPos - origin) / 2, endPos)
		end
		
		task.wait(FIRERATE)
		tool.Shooting.Value = false
	end

alright so this part of the code just seems weird to me:

			if humanoid ~= players:GetPlayerFromCharacter(character) then 
				if result.Instance == character.Head then
					humanoid:TakeDamage(HEADSHOT_DAMAGE)
					DamageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
				else
					humanoid:TakeDamage(BODYSHOT_DAMAGE)
					DamageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
				end
			end

i would change it to this:

			if humanoid then 
				if result.Instance == character.Head then
					humanoid:TakeDamage(HEADSHOT_DAMAGE)
					DamageIndicatorEvent:FireClient(player, HEADSHOT_DAMAGE, humanoid)
				else
					humanoid:TakeDamage(BODYSHOT_DAMAGE)
					DamageIndicatorEvent:FireClient(player, BODYSHOT_DAMAGE, humanoid)
				end
			end

if that does not work can you tell me what the code was trying to print on line 49 like in the video when you got nil

OH WAIT SORRY. OK SO CHANGE THE CODE I SAID IN THE MESSAGE I JUST SENT. BUT. I KNOW WHY ITS INCONSISTENt. so basically, the raycasts are hitting the tracer parts you made. to fix it. make a folder in workspace and name it “ignore”. when you clone the tracer, set its parent to that folder. and inside the raycast params do this:

raycastParams.FilterDescendantsInstances = {player.Character, workspace.ignore}

but the tracers have can query set to false, shouldn’t that disable raycast collision?

oh uhh i dont know, well, can you try the two changes and see what happens? if it still does not work can you tell me what the code was trying to print on line 49 like in the video when you got nil

nah nah it’s fine

i did a bunch of toying around with the spread

and did this:

raycastParams.FilterDescendantsInstances = {player.Character}
			raycastParams.IgnoreWater = true
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude
			
			local direction = (hitPos - flashPart.Position).Unit
			local spreadDirection = (direction + Vector3.new(spreadX, spreadY, 0)).Unit -- this!!!
			local displacement = spreadDirection * MAX_DISTANCE
			
			local result = workspace:Raycast(
				origin,
				displacement,
				raycastParams
			)

i have no idea how it works but it seems to fix the issue

Oh good job. I have no idea why that worked either :sob:

1 Like

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