Raycast not working as expected

Hello, I am currently making a gun system but the problem is it’s not working as expected. The main issue is the bullet either fires above the mouse, or fires accurately on the mouse. Here’s a video of the problem

I’ve tried multiple solutions like removing gun spread, messing with .unit vector but I still can’t seem to figure it out. Any help would be much appreciated.

Here is the client code for a gun

Gun client
local require = require(game:GetService('ReplicatedStorage'):WaitForChild('library').require)
local index = require('utility', 'index')
local service = require('utility', 'service')

--// services
local input = service.UserInputService
local run = service.RunService

--// remotes
local gun_ev = require('events', 'gun')

--// tool
local tool = script.Parent
local handle = tool:WaitForChild('Handle')
local point = handle.point
local set = require(tool.settings)

--// player
local player = service.Players.LocalPlayer
local mouse = player:GetMouse()

--// objects
local site = workspace:WaitForChild('site')
local outside = site.outside
local library = service.ReplicatedStorage.library
local assets = library.assets
local gun_folder = assets.gun
local ui = gun_folder.gun_ui

--// variables
local rates = {}
local current_ammo = set.gun.ammo
local last_shot, reloading = nil, false

--// indexing
local ray = Ray.new
local fpor = workspace.FindPartOnRayWithIgnoreList

local function update_ui(custom, dir, ignore)
	local new = player.PlayerGui:FindFirstChild(ui.Name)

	if new then
		local crosshair = new.crosshair
		local frame = new.right
		local ammo = frame.ammo
		local counter = ammo.counter
		local name = frame.name
		
		if custom == 'mouse' then 
			local pos = input:GetMouseLocation()
			
			crosshair.Position = UDim2.new(0, pos.X, 0, pos.Y - 36)
		else
			name.Text = custom and custom ~= 'gun' and custom or tool.Name
			name.shadow.Text = name.Text
			counter.Text = current_ammo
			counter.shadow.Text = counter.Text
		end
	end
end

local function fire(dir, ignore)
	if not rates[tool] or tick() - rates[tool] >= (60 / set.gun.rpm) then rates[tool] = tick() else return end
	if current_ammo <= 0 or reloading then return end
	
	last_shot = tick()
	
	gun_ev:FireServer('fire', tool, dir, ignore)
	
	current_ammo = current_ammo - 1
	
	update_ui('gun', dir, ignore)
end

input.InputBegan:connect(function(key, processed)
	if tool.Parent:IsA('Backpack') then return end
	
	if key.UserInputType == Enum.UserInputType.MouseButton1 and not processed then
		local dir = (mouse.Hit.p - point.WorldPosition).unit
		
		if set.gun.type:lower() == 'semi' or set.gun.type:lower() == 'shotgun' then
			fire(dir, {tool.Parent, outside.vegetation})
		elseif set.gun.type:lower() == 'auto' then
			while input:IsMouseButtonPressed(Enum.UserInputType.MouseButton1) do
				dir = (mouse.Hit.p - point.WorldPosition) do
					fire(dir, {tool.Parent, outside.vegetation})
				end
				run.Heartbeat:wait()
			end
		end
	end
	if key.KeyCode == Enum.KeyCode.R then
		if not last_shot or tick() - last_shot >= 1 and current_ammo < set.gun.ammo then
			reloading = true
			
			update_ui('RELOADING')
		
			delay(1, function()
				reloading = false
				
				current_ammo = set.gun.ammo
				
				update_ui()
			end)
		end
	end
end)

tool.Equipped:connect(function(m)
	input.MouseIconEnabled = false
	
	if player.PlayerGui:FindFirstChild(ui.Name) then
		player.PlayerGui:FindFirstChild(ui.Name):Destroy()
	end
	
	ui:Clone().Parent = player.PlayerGui do
		update_ui()
	end
	
	update_ui('mouse')
	
	m.Move:connect(function()
		update_ui('mouse')
	end)
end)

tool.Unequipped:connect(function()
	input.MouseIconEnabled = true
	
	if player.PlayerGui:FindFirstChild(ui.Name) then
		player.PlayerGui:FindFirstChild(ui.Name):Destroy()
	end
end)

Here is the bullet visualizer

Visualizer
local req = require(game:GetService('ReplicatedStorage'):WaitForChild('library').require)
local index = req('utility', 'index')
local service = req('utility', 'service')

--// remotes
local gun_ev = req('events', 'gun')

--// indexing
local ray = Ray.new
local fpor = workspace.FindPartOnRayWithIgnoreList

--// player
local player = service.Players.LocalPlayer

gun_ev.OnClientEvent:connect(function(...)
	local args = {...}
	if args[1] == 'replicate_bullet' then
		local tool, set, dir, ignore, ray = args[2], args[3], args[4], args[5], args[6]
		local handle = tool.Handle
		local point = handle.point
		
		local hit, pos = fpor(workspace, ray, ignore, false, true)
		local new_length = (point.WorldPosition - pos).magnitude
		
		local sizer = Instance.new('Part') 
		sizer.Parent = point
		sizer.CFrame = CFrame.new(point.WorldPosition, pos)
		sizer.Transparency = 1
		sizer.Size = Vector3.new(.05, .05, .05)
		sizer.Anchored = true
		
		local bullet = Instance.new('LineHandleAdornment')
		bullet.Parent = sizer
		bullet.Adornee = sizer
		bullet.Length = new_length
		bullet.Transparency = .5
		bullet.Color3 = Color3.fromRGB(255, 255, 130)
		
		delay(.15, function()
			sizer:Destroy()
		end)
	end
	if args[1] == 'update_crosshair' then
		local new = player.PlayerGui:FindFirstChild('gun_ui')

		if new then
			local crosshair = new.crosshair
			
			crosshair.Image = args[2]
		end
	end
end)

3 Likes

I can’t be sure, but maybe the crosshair isn’t accurately in the same position as the mouse, so it might give the effect of that the bullets aren’t firing in the right position.

I’m not quiet sure about that, as I stated in my post, it’s sometimes accurate and sometimes inaccurate. Here’s a gif of it working properly, but it’s very inconsistent and it took me about 3 restarts to get this to correctly happen.

I’m just hoping to find a solution by today.

Have you tried

bullet.CanCollide = false

If that is not your issue then something with the new length variable.

I am not using a part, so I cannot collide the adornment, it happens when I use a part anyway.

I assume you did this to offset the topbar, but I don’t believe it is necessary. If so then the crosshair itself is not in the right position.

If I do not add the - 36, then the crosshair will have an offset that isn’t centered with the mouse.

also, forgot to note: it’s fine when I am firing at longer ranges, otherwise, it messes up.

This is why ScreenGui.IgnoreGuiInset exists, so you don’t have to code in that subtraction.

Your rays are looking fairly accurate from what I see in the video, anyhow. Is there an inaccuracy that I can’t see? The rays are fairly small so I can’t tell excatly, but it seems to hit the point every time.

Like I said before, there is inconsistency whether or not it actually is accurate, whereas in some play-tests, its accurate. Here’s a video showing the inaccuracy, and I posted a video above showing what it should look like. (yes, I am now using IgnoreGuiInset)

Oh yeah, I can see the inaccuracy. I thought I might be missing something but I found a few places where you can start to get debugging or working towards a solution.

The main points of code to look at are as follows:

-- From gun client
local dir = (mouse.Hit.p - point.WorldPosition).unit

-- From bullet visualiser
local tool, set, dir, ignore, ray = args[2], args[3], args[4], args[5], args[6]

-- From bullet visualiser
local hit, pos = fpor(workspace, ray, ignore, false, true)

Seeing as your first Gif produced damage upon accurate hits while your second does not, I don’t quite know if what I’m saying has any relevance but at the very least it should decrease inaccuracies and help you pinpoint where exactly the issues are occurring. There are also some errors to correct.

First off: your visualiser. I don’t think you’ve caught on yet, but you parent the sizer right after instancing it. This has the same performance implications as using the parent argument of Instance.new. In addition to this, the bullet becomes physically simulated for a brief moment before being set in place.

local sizer = Instance.new('Part') 
sizer.CFrame = CFrame.new(point.WorldPosition, pos)
sizer.Transparency = 1
sizer.Size = Vector3.new(.05, .05, .05)
sizer.Anchored = true
sizer.Parent = point
Code Differences
local sizer = Instance.new('Part') 
- sizer.Parent = point
sizer.CFrame = CFrame.new(point.WorldPosition, pos)
sizer.Transparency = 1
sizer.Size = Vector3.new(.05, .05, .05)
sizer.Anchored = true
+ sizer.Parent = point

The same goes with your actual visual.

local bullet = Instance.new('LineHandleAdornment')
bullet.Adornee = sizer
bullet.Length = new_length
bullet.Transparency = .5
bullet.Color3 = Color3.fromRGB(255, 255, 130)
bullet.Parent = sizer
Code Differences
local bullet = Instance.new('LineHandleAdornment')
- bullet.Parent = sizer
bullet.Adornee = sizer
bullet.Length = new_length
bullet.Transparency = .5
bullet.Color3 = Color3.fromRGB(255, 255, 130)
+ bullet.Parent = sizer

This may not mean much in the grand scheme of things, but considering you’re creating these on the server, there’s always the potential that its not showing an accurate representation of where your bullet is going. Servers using more memory could show delays in visualisation as well.

As for the actual bullet and its supposed inaccuracies, that’s caused by one of the calculations you’re passing to your ray methods. To get a better sense of what your bullets are doing, I would recommend visualising on the shooting client itself and not having the server create bullets for the client. What the client and server see are very different.

Once you’ve visualised within the gun client itself, you may be able to determine the source of error or wrong calculation. Considering that both the client and server are fairly dependent on the dir calculation in the gun client, that’s something you’ll want to review closely.

2 Likes

Thank’s for the tips, but I have seemed to fix this problem myself by removing the direction on the client and letting the server calculate the unit vector, seems to work pretty well.

2 Likes