Plz help me fix this bug with mobile weapon raycast

basically, i have a gun system, it works gud for pc but has no mobile support, i was started to add mobile support, added shiftlock when u equip the gun, added fire button, done all the scripts, but there’s one grr hateful bug that i can’t fix. when i press fire button, raycast is doing his job, bullet fires, everything works, but the raycast is a little bit offset, what do i mean: when i shoot at the long distances, the raycast is working exactly as it should working, but when i shoot at the short distances, the raycast is a little bit offset to the left and to the bottom of my screen. i’ve been planning to change the way how my raycast work, so it’d not be from gun start to the crosshair, but take a position starting from my camera to my crosshair, and then use this position to run a raycast starting from my gun start to that position that i took earlier, but i have literally no idea how to make it cuz i’m not that experienced enough (i’m only like 1.5 years in all that development stuff and 1 year in scripting)

a link to the video where the bug is happening:

https://www.youtube.com/watch?v=Gj4MIEAxSD0

as you can see on the video, when i shoot at the long distances the raycast is gud and working gud
but when i shoot at the short distances, then raycast tweaks to the left and to the bottom as i said earlier

the snippet of my local script on the gun:

plr.PlayerGui.MobileReloadButton.ImageButton3.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.Touch then
		mousewhiledb = true

		while mousewhiledb == true do
			if script.Parent.Bullets.Value == 0 then
				if reloaddb == true then return end
				script.Parent.ThingsForSound.EmptyEvent:FireServer()
				return
			end

			if firedb == true or reloaddb == true then return end
			firedb = true

			local screenCenter = Vector2.new(cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2)
			local ray = cam:ViewportPointToRay(screenCenter.X, screenCenter.Y)

			script.Parent.RaycastEvent:FireServer(ray.Origin + ray.Direction * 1000)

			script.Parent.OtherEvents.EffectEvent:FireServer()
			script.Parent.Bullets.Value -= 1
			script.Parent.Name = script.Parent.Bullets.Value

			local thing = game.ReplicatedStorage.ThingAfterShotCool:Clone()
			thing.Parent = workspace
			thing.Position = ray.Origin + ray.Direction * 100

			local raycast = workspace:Raycast(ray.Origin, ray.Direction * 1000)
			local target = raycast and raycast.Instance
			pcall(function()
				if target and (target:IsA("BasePart") or target:IsA("MeshPart") or target:IsA("UnionOperation")) then
					thing.ThingAfterGunshotThingCool.Color = ColorSequence.new(target.Color)
				end
			end)

			thing.ThingAfterGunshotThingCool:Emit(5)

			idletrack:Play()
			firetrack:Play()
			holdtrack:Stop()
			game:GetService("Debris"):AddItem(thing, 0.3)
			task.wait(0.3)

			firedb = false
		end
	end
end)

server script on the gun:

local startpoint = script.Parent.Handle.RaycastStart
local bulletcolbol = game.ReplicatedStorage.Bullet

script.Parent.RaycastEvent.OnServerEvent:Connect(function(plr, mousehit)

	script.Parent.Handle.Fire:Play()

	local filter = RaycastParams.new()
	filter.FilterType = Enum.RaycastFilterType.Exclude
	filter.FilterDescendantsInstances = {script.Parent.Parent, workspace:FindFirstChild(plr.Name)}

	local distance = (mousehit - startpoint.Position).Magnitude
	local direction = (mousehit - startpoint.Position).Unit

	local raycast = workspace:Raycast(startpoint.Position, direction * 1000, filter)
	local hitpart = raycast and raycast.Instance

	local bullet = bulletcolbol:Clone()
	bullet.CFrame = CFrame.lookAt(startpoint.Position, mousehit) * CFrame.new(0, 0, -distance / 2)
	bullet.Size = Vector3.new(0.15, 0.15, distance)
	bullet.Parent = workspace

	local ts = game:GetService("TweenService")
	local ti = TweenInfo.new(0.2)
	local tg = {Transparency = 1}

	ts:Create(bullet, ti, tg):Play()

	game.Debris:AddItem(bullet, 0.2)

	if hitpart then
		local char = hitpart.Parent
		local hum = char and char:FindFirstChildOfClass("Humanoid")

		if hum then
			if hitpart.Name == "Head" then
				hum:TakeDamage(40)
			elseif hitpart.Name ~= "Head" then
				hum:TakeDamage(20)
			end
		end
	end
end)

local motor6d = Instance.new("Motor6D")
motor6d.Name = "MEOWINGrip"

script.Parent.Equipped:Connect(function()
	pcall(function()
		if script.Parent.Parent:FindFirstChild("Right Arm").RightGrip then
			script.Parent.Parent:FindFirstChild("Right Arm").RightGrip:Destroy()
		end
	end)
	
	motor6d.C0 = CFrame.new(-0.25, -0.7, -0.2) * CFrame.Angles(0, math.rad(0), math.rad(90))

	motor6d.Parent = script.Parent.Parent:FindFirstChild("Right Arm")

	motor6d.Part0 = script.Parent.Parent:FindFirstChild("Right Arm")
	motor6d.Part1 = script.Parent.Handle

	motor6d.Enabled = true
end)

script.Parent.Unequipped:Connect(function()
	motor6d.Enabled = false
end)

i hope anyone will help

Cant you just raycast straight from Camera’s CFrame?

this way people could shoot from corners without them being visible to the opponents, i don’t really want this to happen

You can maybe trying doing what this player did for their game:

i don’t think that would help me, but ty anyways for trying

Could you try using WorldToViewportPoint() function with the actual raycast you have made on the client and create a frame depicting where this position of the recast lands on the screen to see how much it is deferring from the center frame of the viewport?

This wouldn’t solve the problem I just want to understand and see where the problem is underlined if it is not a specific server issue and if it just an issue with solely raycasting and perspective

okay, i’m just too tired for trying to fix the bug. i’ve tweaked shiftlock to CFrame.new(1.5, 1, 0) instead of CFrame.new(2.5, 0, 0) and i guess that’ll go. the raycast is still isn’t the best but i just dont care anymore, thank you for trying to help, i’ll keep the post unsolved for situation where someone would magically tell me what i was doing wrong, yea

edit: actually, i think i can make the raycast go from the camera, since with me tweaking the shiftlock, the player can’t shoot from the wall, or even if he can, it wouldn’t give much advantage over the others, so i can call this a solution i think

ONE MORE EDIT: chat gpt somehow actually helped me, he made me a thing so the raycast will go from the raycaststart to the where the camera position is, i think you can somehow implement this if you’re the same guy as me looking for how to fix it, here’s both changed scripts if you’d need them

local script:

plr.PlayerGui.MobileReloadButton.ImageButton3.InputBegan:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.Touch then
		mousewhiledb = true

		while mousewhiledb == true do
			if script.Parent.Bullets.Value == 0 then
				if reloaddb == true then return end
				script.Parent.ThingsForSound.EmptyEvent:FireServer()
				return
			end

			if firedb == true or reloaddb == true then return end
			firedb = true

			local screenCenter = Vector2.new(cam.ViewportSize.X / 2, cam.ViewportSize.Y / 2)
			local ray = cam:ViewportPointToRay(screenCenter.X, screenCenter.Y)
			local raycastParams = RaycastParams.new()
			raycastParams.FilterDescendantsInstances = {plr.Character}
			raycastParams.FilterType = Enum.RaycastFilterType.Exclude

			local raycastResult = workspace:Raycast(ray.Origin, ray.Direction * 1000, raycastParams)
			local hitPosition = raycastResult and raycastResult.Position or (ray.Origin + ray.Direction * 1000)

			script.Parent.RaycastEvent:FireServer(ray.Origin, hitPosition)

			script.Parent.OtherEvents.EffectEvent:FireServer()
			script.Parent.Bullets.Value -= 1
			script.Parent.Name = script.Parent.Bullets.Value

			local thing = game.ReplicatedStorage.ThingAfterShotCool:Clone()
			thing.Parent = workspace
			thing.Position = ray.Origin + ray.Direction * 1000

			local raycast = workspace:Raycast(ray.Origin, ray.Direction * 1000)
			local target = raycast and raycast.Instance
			pcall(function()
				if target and (target:IsA("BasePart") or target:IsA("MeshPart") or target:IsA("UnionOperation")) then
					thing.ThingAfterGunshotThingCool.Color = ColorSequence.new(target.Color)
				end
			end)

			thing.ThingAfterGunshotThingCool:Emit(5)

			idletrack:Play()
			firetrack:Play()
			holdtrack:Stop()
			game:GetService("Debris"):AddItem(thing, 0.3)
			task.wait(0.3)

			firedb = false
		end
	end
end)

plr.PlayerGui.MobileReloadButton.ImageButton3.InputEnded:Connect(function(input)
	if input.UserInputType == Enum.UserInputType.Touch then
		mousewhiledb = false
	end
end)

server script:

local startpoint = script.Parent.Handle.RaycastStart
local bulletTemplate = game.ReplicatedStorage.Bullet

script.Parent.RaycastEvent.OnServerEvent:Connect(function(plr, camOrigin, camHitPos)
	if not (typeof(camOrigin) == "Vector3" and typeof(camHitPos) == "Vector3") then return end

	script.Parent.Handle.Fire:Play()

	local direction = (camHitPos - camOrigin).Unit
	local damageRaycastParams = RaycastParams.new()
	damageRaycastParams.FilterDescendantsInstances = {script.Parent.Parent, workspace:FindFirstChild(plr.Name)}
	damageRaycastParams.FilterType = Enum.RaycastFilterType.Exclude

	local raycast = workspace:Raycast(camOrigin, direction * 1000, damageRaycastParams)
	local hitpoint = raycast and raycast.Position or camHitPos
	local hitpart = raycast and raycast.Instance

	local distance = (hitpoint - startpoint.Position).Magnitude

	local bullet = bulletTemplate:Clone()
	bullet.CFrame = CFrame.lookAt(startpoint.Position, hitpoint) * CFrame.new(0, 0, -distance / 2)
	bullet.Size = Vector3.new(0.15, 0.15, distance)
	bullet.Parent = workspace

	local TweenService = game:GetService("TweenService")
	local Debris = game:GetService("Debris")

	local tween = TweenService:Create(bullet, TweenInfo.new(0.2), {Transparency = 1})
	tween:Play()
	Debris:AddItem(bullet, 0.2)

	if hitpart then
		local model = hitpart:FindFirstAncestorOfClass("Model")
		local hum = model and model:FindFirstChildOfClass("Humanoid")
		if hum then
			local damage = (hitpart.Name == "Head") and 40 or 20
			hum:TakeDamage(damage)
		end
	end
end)
1 Like

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