input.Position isn't returning an accurate screen position?

After a couple of tests, I kept having bullets go to a different place than the one I clicked, so I created this script where I tried a bunch of things to try and get the bullet to be accurate.

I tried both input.Position and UserInputService:GetMouseLocation(), ScreenPointToRay instead of ViewportPointToRay (I also tried changing the depth for both of those to various different numbers), and I tried subtracting the gui inset for all combinations. For the gui inset, I tested both, 36) and GuiService:GetGuiInset().

I’m pretty sure it’s not because of the way I’m moving the bullets, because I tried a couple of different methods for that as well.

It’s not because of workspace:Raycast() either, because I tried everything that I mentioned before with and they had the same results.

tl;dr: I tried literally every combination of everything that I could think of that related to this, and I still don’t know why my bullets are offsetted. The name of this post is what I think is happening at this point.

local UserInputService = game:GetService("UserInputService")
local RunService = game:GetService("RunService")

local Player = game:GetService("Players").LocalPlayer
local Character = Player.Character or Player.CharacterAdded:Wait()
local Camera = workspace.CurrentCamera

local zeroVector =
local random =
local tau = math.pi * 2

local bullet = script:WaitForChild("Bullet")
local bulletSpread = 0
local bulletSpeed = 90
local bulletCount = 0
local range = 400

local raycastParams =
raycastParams.IgnoreWater = false
raycastParams.FilterDescendantsInstances = {Character}
raycastParams.FilterType = Enum.RaycastFilterType.Blacklist

function randomOffset(vector, maxAngle)
	local rand1 = random:NextNumber(0, tau)
	local rand2 = random:NextNumber(math.cos(maxAngle / 1e3), 1)

	local res = (, vector)
		* CFrame.Angles(0, 0, rand1)
		* CFrame.Angles(math.acos(rand2), 0, 0)

	print(res:Dot(vector.Unit)) -- this always prints 1
	-- since i set maxAngle to 0 for testing
	return res

local function get3DPosition(screenPosition)
	local oray = Camera:ViewportPointToRay(screenPosition.X, screenPosition.Y, 0)
	return workspace:Raycast(oray.Origin, randomOffset(oray.Direction, bulletSpread) * range, raycastParams)

UserInputService.InputBegan:Connect(function(input, ignore)
	if ignore then return end
	if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
	local raycastResult = get3DPosition(input.Position)
	if not raycastResult then return end
	local position = raycastResult.Position
	local bullet = bullet:Clone()
	bullet.Position = Character:GetPrimaryPartCFrame().Position +, 0, 4)
	bullet.CFrame =, position)
	bullet.Anchored = true
	bullet.Parent = workspace
	bulletCount = bulletCount + 1
	local startPosition = bullet.Position
	local distance = (startPosition - position).Magnitude
	local name = "RenderBullet" .. tostring(bulletCount)
	RunService:BindToRenderStep(name, Enum.RenderPriority.Input.Value, function(deltaTime)
		bullet.CFrame = bullet.CFrame + (bullet.CFrame.LookVector * deltaTime * bulletSpeed)
		if (bullet.Position - startPosition).Magnitude >= distance then

	Character = newCharacter
--// thanks for stopping by

btw the offset function isn’t related, I temporarily disabled that for testing.

1 Like

Update: for some reason mouse.Hit.Position is perfectly accurate.