Help needed figuring out where to have gun bullets fly to

I can imagine this is extremely easy but it’s just one of those things that’s slipping my mind. I’m trying to get bullets to fly to where I click on the screen, but whenever I fire, it heads for the spawn in the picture (most likely the origin point of the whole map).

Below is the local script I use to send the positional data to the server in order to generate the bullet. I’ve tried setting the direction to multiple different things, such as using a raycast to get the instance/position it hits, but nothing has worked 100%.

local UIS = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")

local events = RS:WaitForChild("Events")
local gunFired = events:WaitForChild("GunFired")

local gun = script.Parent
local handle = gun:WaitForChild("Handle")
local config = gun:WaitForChild("Config")
local fireRate = config:WaitForChild("FireRate")

local character = gun.Parent

local fireBegin = nil
local fireStop = nil
local firing = false

gun.Equipped:Connect(function()
	if fireBegin or fireStop or firing then
		fireBegin:Disconnect()
		fireStop:Disconnect()
		fireBegin = nil
		fireStop = nil
		firing = false
	end
	fireBegin = UIS.InputBegan:Connect(function(input, GPE)
		if GPE then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			firing = true
			while firing do
				local direction
				local mousePosition = UIS:GetMouseLocation()
				local mouseRay = workspace.Camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
				local raycastParams = RaycastParams.new()
				raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
				raycastParams.FilterDescendantsInstances = {gun, character}
				local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
				direction = mouseRay.Direction - handle.Barrel.WorldPosition
				gunFired:FireServer(gun, direction)
				task.wait()
			end
		end
	end)

	fireStop = UIS.InputEnded:Connect(function(input, GPE)
		if GPE then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			firing = false
		end
	end)
end)

gun.Unequipped:Connect(function()
	if fireBegin or fireStop or firing then
		fireBegin:Disconnect()
		fireStop:Disconnect()
		fireBegin = nil
		fireStop = nil
		firing = false
	end
end)

Please let me know if I need to provide more info (I don’t think I do but I couldn’t possibly know).

For your raycast position subtract the position of handle from mouse position.

local raycastResult = workspace:RayCast(handle.Position, (mousePosition - handle.Position) * 1000, raycastParams)

This broke the script. “mousePosition” is a Vector2 and you’re subtracting a Vector3 from it.

Try this:

direction= (raycastResult.Position - handle.Barrel.WorldPosition).unit

You should use local mouse = game:GetService("Players").LocalPlayer:GetMouse() and then in your raycast you should do (mouse.Hit.Position - handle.Position) * 1000.

Both of these made the script error when shooting at the sky (raycastresult didn’t exist) or didn’t line up with the cursor on screen:

local UIS = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")

local events = RS:WaitForChild("Events")
local gunFired = events:WaitForChild("GunFired")

local gun = script.Parent
local handle = gun:WaitForChild("Handle")
local config = gun:WaitForChild("Config")
local fireRate = config:WaitForChild("FireRate")

local character = gun.Parent

local fireBegin = nil
local fireStop = nil
local firing = false

gun.Equipped:Connect(function()
	if fireBegin or fireStop or firing then
		fireBegin:Disconnect()
		fireStop:Disconnect()
		fireBegin = nil
		fireStop = nil
		firing = false
	end
	fireBegin = UIS.InputBegan:Connect(function(input, GPE)
		if GPE then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			firing = true
			while firing do
				local direction
				local mousePosition = UIS:GetMouseLocation()
				local mouseRay = workspace.Camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
				local raycastParams = RaycastParams.new()
				raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
				raycastParams.FilterDescendantsInstances = {gun, character}
				local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
				direction= (raycastResult.Position - handle.Barrel.WorldPosition).unit
				gunFired:FireServer(gun, direction)
				task.wait()
			end
		end
	end)

	fireStop = UIS.InputEnded:Connect(function(input, GPE)
		if GPE then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			firing = false
		end
	end)
end)

Yes, I just tried this. It returned the same problems as my original script except it gives an error whenever I shoot at the sky because there is no raycast result.

local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
if raycastResult then
	direction = handle.Barrel.WorldPosition + (raycastResult.Position - handle.Barrel.WorldPosition).unit
end

I think it should actually be mouseRay.Origin instead of handle.Position now that I think about it.

This makes it shoot accurately, but I still can’t shoot above the horizon line.

When you mean you can’t shoot above the horizon line I’m assuming you want to see a raycast but none is shooting since there is no raycast result.

You would need to create a separate ray that shoots

1 Like

What would that ray be, and why wouldn’t I just apply it to all directions instead of just up?

I don’t recommend check GPE for InputEnded but only for InputBegan and InputChanged

If only pass a vector, you cannot determine the path of a bullet. You need a start/stop vector, or ray, or something else. CFrame even.

To make a ray for the horizon it’s pretty simple
First just detect whether or not there is a raycast.

if raycastResult then
   direction = handle.Barrel.WorldPosition + (raycastResult.Position - handle.Barrel.WorldPosition).unit
end

You would just create a ray after that from the tip of the gun that goes on for however long you want.

Theoretically, you could but it would be very hard to implement. Your current system is fine

I accidentally made some progress:

local UIS = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")
local players = game:GetService("Players")

local events = RS:WaitForChild("Events")
local gunFired = events:WaitForChild("GunFired")

local gun = script.Parent
local handle = gun:WaitForChild("Handle")
local config = gun:WaitForChild("Config")
local fireRate = config:WaitForChild("FireRate")

local character = gun.Parent
local mouse = players.LocalPlayer:GetMouse()

local fireBegin = nil
local fireStop = nil
local firing = false

gun.Equipped:Connect(function()
	if fireBegin or fireStop or firing then
		fireBegin:Disconnect()
		fireStop:Disconnect()
		fireBegin = nil
		fireStop = nil
		firing = false
	end
	fireBegin = UIS.InputBegan:Connect(function(input, GPE)
		if GPE then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			firing = true
			while firing do
				local direction
				local mousePosition = UIS:GetMouseLocation()
				local mouseRay = workspace.Camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
				local raycastParams = RaycastParams.new()
				raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
				raycastParams.FilterDescendantsInstances = {gun, character}
				local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
				if raycastResult then
					direction = handle.Barrel.WorldPosition + (raycastResult.Position - handle.Barrel.WorldPosition)
				else
					direction = handle.Barrel.WorldPosition + mouseRay.Direction
				end
				gunFired:FireServer(gun, direction)
				task.wait()
			end
		end
	end)

	fireStop = UIS.InputEnded:Connect(function(input, GPE)
		if GPE then return end
		if input.UserInputType == Enum.UserInputType.MouseButton1 then
			firing = false
		end
	end)
end)

gun.Unequipped:Connect(function()
	if fireBegin or fireStop or firing then
		fireBegin:Disconnect()
		fireStop:Disconnect()
		fireBegin = nil
		fireStop = nil
		firing = false
	end
end)

This code will make the bullets fire perfectly in the direction that I aim (even above the horizon line) but it will shoot in the direction that my gun is pointing in 3D space if I shoot above the horizon line while moving. I also can’t zoom all the way into 1st person and shoot, and I can’t aim through my character and then shoot accurately (I think these two problems are linked to my ray filter but idk the solution).

There are a few issues with the script:

  1. task.wait() is not a valid function, it should be wait() .
  2. The script has an infinite loop in the while firing do block that is not being stopped, this could cause performance issues.
  3. The script is using the local variable direction without assigning a value before using it. This could cause a nil value to be passed as an argument to gunFired:FireServer(gun, direction) .

Here is an example of how you can rewrite the script:

local UIS = game:GetService("UserInputService")
local RS = game:GetService("ReplicatedStorage")

local events = RS:WaitForChild("Events")
local gunFired = events:WaitForChild("GunFired")

local gun = script.Parent
local handle = gun:WaitForChild("Handle")
local config = gun:WaitForChild("Config")
local fireRate = config:WaitForChild("FireRate")

local character = gun.Parent

local fireBegin = nil
local fireStop = nil

gun.Equipped:Connect(function()
    if fireBegin or fireStop then
        fireBegin:Disconnect()
        fireStop:Disconnect()
        fireBegin = nil
        fireStop = nil
    end
    fireBegin = UIS.InputBegan:Connect(function(input, GPE)
        if GPE then return end
        if input.UserInputType == Enum.UserInputType.MouseButton1 then
            local mousePosition = UIS:GetMouseLocation()
            local mouseRay = workspace.Camera:ViewportPointToRay(mousePosition.X, mousePosition.Y)
            local raycastParams = RaycastParams.new()
            raycastParams.FilterType = Enum.RaycastFilterType.Blacklist
            raycastParams.FilterDescendantsInstances = {gun, character}
            local raycastResult = workspace:Raycast(mouseRay.Origin, mouseRay.Direction * 1000, raycastParams)
            local direction =

im pretty sure you can use mouse.Hit

since that tells you the CFrame of the mouse, and you can even get the position of it im pretty sure. to make the bullet fly smoothly, you could probably just use lerping or tweens.

let me know if this worked, note that mouse.Hit works in localscripts only im positive, correct me if im wrong.

I see you haven’t been here since mid-2022.

This loop appears to be handled in a separate thread. It seems to make the gun automatic.

This variable is clearly assigned here, one way or another.


I think Mouse.Hit, once again, is the only way (or you can do the raycasting yourself with Camera)

1 Like