Raycasts are hitting the air

I am making a simple projectile module. Every frame, a ray will the cast from the current position to the previous bullet position from the last game.
All bullets are in a folder used by RaycastParams to stop them from hitting each other.
For some odd reason, the bullets will either not fire at all and no rays will be cast, or they will be cast and show a result hitting a part despite being nowhere near it.

Here is a recording. Every ray is a random color, and the white dots are the bullets. they stop after hitting something.

3 Likes

Hello! Can we see your raycasting code to better diagnose the issue? Thank you!

	connection = runservice.Stepped:Connect(function(time,delta)
		t += delta -- for a stepped or heartbeat event
		
		local newPos = Vector3.new(
			start.X + newforce.X * t + (acceleration.X * t^2)/2,
			start.Y + newforce.Y * t + (acceleration.Y * t^2)/2,
			start.Z + newforce.Z * t + (acceleration.Z * t^2)/2
		)
		bullet.Position = newPos
			
		local result = workspace:Raycast(newPos,prevPos,params)	
		
		-- RAY VISUALISATION IGNORE THIS

		local distance = (newPos - prevPos).Magnitude
		local position = (newPos + prevPos)/2
		local p = Instance.new("Part",workspace.Bullets)
		p.Anchored = true
		p.CanCollide = false
		p.Size = Vector3.new(0.1, 0.1, distance)
		p.Color = Color3.fromRGB(math.random(0,255),math.random(0,255),math.random(0,255))
		p.Transparency = math.random(0,600)/1000
		p.Material = Enum.Material.Neon
		p.CFrame = CFrame.lookAt(newPos, position)*CFrame.new(0, 0, -distance/2)
	
-- more code down here	
		if result and result.Position then
			connection:Disconnect()
			print(result.Instance)
		else
			print("failed")
			
		end
		
		
		prevPos = newPos
		
		if t >= cleanupTime then
		--	connection:Disconnect()
			bullet:Destroy()
		end
		
	end)
end

What happens if you add a line which sets CanQuery to false? It might be querying with the visualizer.

1 Like

I’m not really sure what is prevPos, I’m just assumming that by its name it refers to the last position it raycasted from/ last updated position, if that is the case it shouldn’t be that, since :Raycast should take a Starting position and a direction of where it raycasts, not a position of where it will raycast to.

So if prevPos IS a position it’s gonna raycast towards newPos+prevPos in world position

So if that is the case try changing that to this:

local result = workspace:Raycast(newPos,     prevPos-newPos         ,params)
----------------------------------------Now this is a direction

EDIT: Yeah after reading the code I know now that prevPos IS just the last updated position and youre just passing that through the raycast, so that is definetly the issue here.

1 Like

This and the CanQuery thing solved some of the issue, but not all of it. Some of the rays still go through and others fall short. Still very much a significant improvement though


Maybe consider starting the raycast from prevPos instead? like:

local result = workspace:Raycast(prevPos,newPos-prevPos,params)
--changing both raycast origin and direction accordingly to start from prevPos and towards newPos	

Also, could you show us the rest of your script? I might have some theories on what it could be, like how you’re handling some variables but id like to know more.

you can have the whole module tbh i don’t avre

local runservice = game:GetService("RunService")

local module = {}


function module.Launch(start, target, force, acceleration, cleanupTime)
	
	local bullet = game.ServerStorage.Bullet:Clone()
	bullet.Parent = game.Workspace.Bullets
	
	local t = 0
	local prevPos = Vector3.new(0,1e36,0)
	
	local newforce = (target - start).Unit * force
	
	local params = RaycastParams.new()
	local exTable = {}
	
	for i,v in ipairs(workspace.Bullets:GetChildren()) do
		if v:IsA("BasePart") then
			table.insert(exTable,v)
		end
	end
	
--	acceleration *= Vector3.new(2,1,2)
--	force *= (Vector3.one*2) 
	
	params.FilterDescendantsInstances = exTable
	
	connection = runservice.Stepped:Connect(function(time,delta)
		t += delta -- for a stepped or heartbeat event
		
		local newPos = Vector3.new(
			start.X + newforce.X * t + (acceleration.X * t^2)/2,
			start.Y + newforce.Y * t + (acceleration.Y * t^2)/2,
			start.Z + newforce.Z * t + (acceleration.Z * t^2)/2
		)
		bullet.Position = newPos
			
		local result = workspace:Raycast(newPos,prevPos-newPos,params)	
		
		-- RAY VISUALISATION IGNORE THIS

		local distance = (newPos - prevPos).Magnitude
		local position = (newPos + prevPos)/2
		local p = Instance.new("Part",workspace.Bullets)
		p.Anchored = true
		p.CanCollide = false
		p.CanQuery = false
		p.Size = Vector3.new(0.1, 0.1, distance)
		p.Color = Color3.fromRGB(math.random(0,255),math.random(0,255),math.random(0,255))
		p.Transparency = math.random(0,600)/1000
		p.Material = Enum.Material.Neon
		p.CFrame = CFrame.lookAt(newPos, position)*CFrame.new(0, 0, -distance/2)
	
-- more code down here	
		if result and result.Position then
			connection:Disconnect()
			print(result.Instance)
		else
			print("failed")
			
		end
		
		
		prevPos = newPos
		
		if t >= cleanupTime then
		--	connection:Disconnect()
			bullet:Destroy()
		end
		
	end)
end


return module

and this is an example of how i used it

tool.Activated:Connect(function()
--	print(character,player,humanoid,root)
	local originPos = root.Position + root.CFrame.LookVector * 1.5
	local hitPos = mousehit:InvokeClient(player)
	
	module.Launch(
		originPosition, --start
		hitPos, --target
		100, --force (number, just put ur speed there)
		CFrame.new(0,0,0), -- acceleration (gravity)
		4 --cleanup time (seconds until bullet destroys itself if it does not find a target)
	)

	
end)

Do you run the tool.Activated on a LocalScript?

edit: Nvm

1 Like
connection = runservice.Stepped:Connect(function(_,dt)
		t += dt -- for a stepped or heartbeat event

		local newPos = Vector3.new(
			start.X + newforce.X * t + (acceleration.X * t^2)/2,
			start.Y + newforce.Y * t + (acceleration.Y * t^2)/2,
			start.Z + newforce.Z * t + (acceleration.Z * t^2)/2
		)
		bullet.Position = newPos

		local result = workspace:Raycast(newPos,CFrame.lookAt(prevPos,newPos).LookVector * dt,params)	

		-- RAY VISUALISATION IGNORE THIS

		local distance = (newPos - prevPos).Magnitude
		local position = (newPos + prevPos)/2
		local p = Instance.new("Part",workspace.Bullets)
		p.Anchored = true
		p.CanCollide = false
		p.CanQuery = false
		p.Size = Vector3.new(0.1, 0.1, distance)
		p.Color = Color3.fromRGB(math.random(0,255),math.random(0,255),math.random(0,255))
		p.Transparency = math.random(0,600)/1000
		p.Material = Enum.Material.Neon
		p.CFrame = CFrame.lookAt(newPos, position)*CFrame.new(0, 0, -distance/2)

		-- more code down here	
		if result and result.Position then
			connection:Disconnect()
			print(result.Instance)
		else
			print("failed")

		end


		prevPos = newPos

		if t >= cleanupTime then
			--	connection:Disconnect()
			bullet:Destroy()
		end

	end)

Can you try this?

can i know what you changed first?

Meanwhile setting the direction for the RayCast, you’re not adding a length to it, which causes it’s length to be randomly resulting in randomly stopped raycasts
basically this line:

local result = workspace:Raycast(newPos,CFrame.lookAt(prevPos,newPos).LookVector * dt,params)

Other than that it’s a rewrite of your script

for some reason now nothing is hitting at all??? it’s all just going straight into the abyss

Are you sure that your original code is not causing the ray to sometimes collide with the player character? I don’t see any lines that add the player’s character to the ignore list in your code.

2 Likes

Could you try this now?

local runservice = game:GetService("RunService")

local module = {}


function module.Launch(start, target, force, acceleration, cleanupTime)

	local bullet = game.ServerStorage.Bullet:Clone()
	bullet.Parent = game.Workspace.Bullets

	local t = 0
	local prevPos = Vector3.new(0,1e36,0)

	local newforce = (target - start).Unit * force

	local params = RaycastParams.new()
	local exTable = {}

	for i,v in ipairs(workspace.Bullets:GetChildren()) do
		if v:IsA("BasePart") then
			table.insert(exTable,v)
		end
	end

	--	acceleration *= Vector3.new(2,1,2)
	--	force *= (Vector3.one*2) 

	params.FilterDescendantsInstances = exTable
	
	local connection

	connection = runservice.Stepped:Connect(function(_,dt)
		t += dt -- for a stepped or heartbeat event

		local newPos = Vector3.new(
			start.X + newforce.X * t + (acceleration.X * t^2)/2,
			start.Y + newforce.Y * t + (acceleration.Y * t^2)/2,
			start.Z + newforce.Z * t + (acceleration.Z * t^2)/2
		)
		bullet.Position = newPos

		local result = workspace:Raycast(prevPos,CFrame.lookAt(prevPos,newPos).LookVector.Unit * dt * force,params)	

		-- RAY VISUALISATION IGNORE THIS

		local distance = (newPos - prevPos).Magnitude
		local position = (newPos + prevPos)/2
		local p = Instance.new("Part")
		p.Anchored = true
		p.CanCollide = false
		p.CanQuery = false
		p.CanTouch = false
		p.Size = Vector3.new(0.1, 0.1, distance)
		p.Color = Color3.fromRGB(math.random(0,255),math.random(0,255),math.random(0,255))
		p.Transparency = math.random(0,600)/1000
		p.Material = Enum.Material.Neon
		p.CFrame = CFrame.lookAt(newPos, position)*CFrame.new(0, 0, -distance/2)
		p.Parent = workspace.Bullets

		-- more code down here	
		if result and result.Position then
			connection:Disconnect()
			print(result.Instance)
		else
			print("failed")

		end


		prevPos = newPos

		if t >= cleanupTime then
			--	connection:Disconnect()
			bullet:Destroy()
		end

	end)
end


return module

For some reason setting a local for the Connection worked along with some adjustments to the RayCast direction, also what @TheGrimDeathZombie said is true, it sometimes could hit the character

1 Like

sorry for the late response, i had to go somewhere for a bit

i updated the raycast visualising to match with what you’re doing (i forgot to mention that) and it turns out the way you raycast it is actually kinda odd??? just making sure this is intentional behaviour

ignoring the odd raycasting, the hit detection actually seems to work pretty well but i will still leave the post open just in case

Could be the Visualization section