Hello everyone, I have a weapon system, but I’m having some issues with the mobile firing system. The main problem is this: the bullet should travel straight in the direction of the cursor, but it always goes downwards and hits the ground near my feet instead of going to the cursor’s location. I’m aiming the cursor at NPCs, but the bullet doesn’t even reach them. I really need someone who can help me solve this problem, as it’s significantly slowing down the game development process. I would appreciate it if someone familiar with the raycast system could assist me.
client module
--[[
Created by devsydiens
]]
local gunClient = {}
local inputService = game:GetService("UserInputService")
local tweenService = game:GetService("TweenService")
local us = game.ReplicatedStorage:FindFirstChild("stroke")
local ust = game.ReplicatedStorage:FindFirstChild("strokee")
local player = game:GetService("Players").LocalPlayer
local mouse = player:GetMouse()
local camera = workspace.CurrentCamera
local bulletModule = require(player.PlayerScripts:WaitForChild("bulletRender"))
local gunGui = script:WaitForChild("gunGui")
local totalFired = 0
-- Mobil control
local isMobile = inputService.TouchEnabled and not inputService.MouseEnabled
local mobileCursorGui = nil
local mobileControlsGui = nil
--\\ Mobil cursor
local function ensureMobileCursor()
if mobileCursorGui and mobileCursorGui.Parent then
return mobileCursorGui
end
local oldGui = player.PlayerGui:FindFirstChild("MobileCrosshair")
if oldGui then oldGui:Destroy() end
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "MobileCrosshair"
screenGui.ResetOnSpawn = false
screenGui.DisplayOrder = 100
screenGui.IgnoreGuiInset = true
local cursor = Instance.new("Frame")
cursor.Name = "Cursor"
cursor.Size = UDim2.new(0, 4, 0, 4)
cursor.Position = UDim2.new(0.5, -2, 0.5, -2)
cursor.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
cursor.BorderSizePixel = 0
cursor.Parent = screenGui
local line1 = Instance.new("Frame")
line1.Size = UDim2.new(0, 20, 0, 2)
line1.Position = UDim2.new(0.5, -10, 0.5, -1)
line1.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
line1.BorderSizePixel = 0
line1.Parent = screenGui
local line2 = Instance.new("Frame")
line2.Size = UDim2.new(0, 2, 0, 20)
line2.Position = UDim2.new(0.5, -1, 0.5, -10)
line2.BackgroundColor3 = Color3.fromRGB(255, 255, 255)
line2.BorderSizePixel = 0
line2.Parent = screenGui
screenGui.Parent = player.PlayerGui
mobileCursorGui = screenGui
return screenGui
end
--\\ Mobil buttons
local function createMobileButtons(fireCallback, reloadCallback)
if mobileControlsGui then
mobileControlsGui:Destroy()
end
local screenGui = Instance.new("ScreenGui")
screenGui.Name = "MobileGunControls"
screenGui.ResetOnSpawn = false
screenGui.DisplayOrder = 50
screenGui.IgnoreGuiInset = true
local fireButton = Instance.new("TextButton")
fireButton.Name = "FireButton"
fireButton.Size = UDim2.new(0, 60, 0, 60)
fireButton.Position = UDim2.new(1, -120, 1, -160)
fireButton.BackgroundColor3 = Color3.fromRGB(70, 70, 70)
fireButton.BorderSizePixel = 0
fireButton.Text = "Fire"
fireButton.BackgroundTransparency = 0.3
fireButton.TextSize = 40
fireButton.TextScaled = true
fireButton.TextColor3 = Color3.new(1, 1, 1)
fireButton.Font = Enum.Font.GothamBold
fireButton.AutoButtonColor = false
fireButton.Parent = screenGui
if ust then ust:Clone().Parent = fireButton end
local fireCorner = Instance.new("UICorner")
fireCorner.CornerRadius = UDim.new(0, 40)
fireCorner.Parent = fireButton
local reloadButton = Instance.new("TextButton")
reloadButton.Name = "ReloadButton"
reloadButton.Size = UDim2.new(0, 60, 0, 60)
reloadButton.Position = UDim2.new(0, 20, 1, -160)
reloadButton.BackgroundColor3 = Color3.fromRGB(70, 70, 70)
reloadButton.BorderSizePixel = 0
reloadButton.Text = "Reload"
reloadButton.TextSize = 35
reloadButton.TextScaled = true
reloadButton.TextColor3 = Color3.new(1, 1, 1)
reloadButton.BackgroundTransparency = 0.3
reloadButton.Font = Enum.Font.GothamBold
reloadButton.AutoButtonColor = false
reloadButton.Parent = screenGui
if us then us:Clone().Parent = reloadButton end
local reloadCorner = Instance.new("UICorner")
reloadCorner.CornerRadius = UDim.new(0, 40)
reloadCorner.Parent = reloadButton
fireButton.MouseButton1Down:Connect(function()
fireButton.BackgroundColor3 = Color3.fromRGB(33, 33, 33)
fireCallback(true)
end)
fireButton.MouseButton1Up:Connect(function()
fireButton.BackgroundColor3 = Color3.fromRGB(70, 70, 70)
fireCallback(false)
end)
reloadButton.MouseButton1Click:Connect(function()
reloadButton.BackgroundColor3 = Color3.fromRGB(33, 33, 33)
reloadCallback()
wait(0.2)
reloadButton.BackgroundColor3 = Color3.fromRGB(70, 70, 70)
end)
screenGui.Parent = player.PlayerGui
mobileControlsGui = screenGui
return screenGui
end
function gunClient.init(tool)
repeat wait() until player.Character and player.Character.Parent == workspace; wait(.25)
local character = player.Character
local humanoid = character:WaitForChild("Humanoid")
local root = character:WaitForChild("HumanoidRootPart")
local emptySound = tool:WaitForChild("Handle"):WaitForChild("emptySound")
local reloadSound = tool:WaitForChild("Handle"):WaitForChild("reloadSound")
local mag = tool:WaitForChild("GunModel"):WaitForChild("Mag")
local bulletStorage = workspace:WaitForChild("bulletStorage")
local gunStorage = game:GetService("ReplicatedStorage"):WaitForChild("gunStorage")
local gunEvents = gunStorage:WaitForChild("events")
local fireEvent = gunEvents:WaitForChild("fire")
local reloadEvent = gunEvents:WaitForChild("reload")
local posVerifier = gunEvents:WaitForChild("positionVerifier")
local config = tool:WaitForChild("config")
local animations = config:WaitForChild("animations")
local mainConfigs = config:WaitForChild("main")
local miscConfgs = config:WaitForChild("misc")
local modeConfigs = config:WaitForChild("mode")
local settings = tool:FindFirstChild("settings")
local CAMERA_SHAKE_INTENSITY = settings and settings:FindFirstChild("cameraShake") and settings.cameraShake.Value or 1.5
local BLUR_SIZE = settings and settings:FindFirstChild("blurSize") and settings.blurSize.Value or 8
local shotgun = mainConfigs.shotgun.Value
local pellets = mainConfigs.shotgun.pellets.Value
local rpm = mainConfigs.rpm.Value
local spread = mainConfigs:WaitForChild("spread").Value
local ammo = tool:WaitForChild("ammo")
local totalAmmoValue = tool:FindFirstChild("totalAmmo")
if not totalAmmoValue then
totalAmmoValue = Instance.new("IntValue")
totalAmmoValue.Name = "totalAmmo"
local magazineSize = mainConfigs.ammo.Value
local magazineCount = mainConfigs:FindFirstChild("magazineCount") and mainConfigs.magazineCount.Value or 2
totalAmmoValue.Value = magazineSize * magazineCount
totalAmmoValue.Parent = tool
end
local aimAnim = humanoid:LoadAnimation(animations:WaitForChild("aimAnim"))
local holsterAnim = humanoid:LoadAnimation(animations:WaitForChild("holsterAnim"))
local reloadAnim = humanoid:LoadAnimation(animations:WaitForChild("reloadAnim"))
local recoilAnim = humanoid:LoadAnimation(animations:WaitForChild("recoilAnim"))
local equipAnim
if animations:FindFirstChild("equipAnim") then
equipAnim = humanoid:LoadAnimation(animations:FindFirstChild("equipAnim"))
equipAnim.Priority = Enum.AnimationPriority.Action4
end
local hAnim = false
local canFire = true
local equipped = false
local mouseDown = false
local reloading = false
local canRespond = false
local gunMode
local blur = Instance.new("BlurEffect")
blur.Size = 0
blur.Parent = camera
for _,v in pairs(modeConfigs:GetChildren()) do
if v.Value then
gunMode = v.Name
end
end
mouse.TargetFilter = workspace.bulletStorage
aimAnim.Priority = Enum.AnimationPriority.Action
holsterAnim.Priority = Enum.AnimationPriority.Action
recoilAnim.Priority = Enum.AnimationPriority.Action
reloadAnim.Priority = Enum.AnimationPriority.Action
local function cameraShake()
spawn(function()
for i = 1, 3 do
local randomOffset = CFrame.Angles(
math.rad(math.random(-CAMERA_SHAKE_INTENSITY, CAMERA_SHAKE_INTENSITY)),
math.rad(math.random(-CAMERA_SHAKE_INTENSITY, CAMERA_SHAKE_INTENSITY)),
math.rad(math.random(-CAMERA_SHAKE_INTENSITY, CAMERA_SHAKE_INTENSITY))
)
camera.CFrame = camera.CFrame * randomOffset
wait(0.02)
end
end)
spawn(function()
tweenService:Create(blur, TweenInfo.new(0.05), {Size = BLUR_SIZE}):Play()
wait(0.05)
tweenService:Create(blur, TweenInfo.new(0.15), {Size = 0}):Play()
end)
end
local ammoListen
local totalAmmoListen
local currentGui
local function updateGUI()
if currentGui and currentGui.Parent then
currentGui.infoFrame.gunAmmo.Text = tostring(ammo.Value).." / "..tostring(totalAmmoValue.Value)
end
end
local function reload()
if not reloading and ammo.Value < mainConfigs.ammo.Value and totalAmmoValue.Value > 0 then
reloading = true
mag.Transparency = 1
reloadAnim:Play(.1, 1, reloadAnim.Length / mainConfigs.reloadTime.Value)
local maxAmmo = mainConfigs.ammo.Value
local neededAmmo = maxAmmo - ammo.Value
local ammoToLoad = math.min(neededAmmo, totalAmmoValue.Value)
reloadEvent:FireServer(tool.Name, ammoToLoad)
local canStop = true
local unequipListen
unequipListen = tool.Unequipped:Connect(function()
unequipListen:Disconnect()
canStop = false
end)
wait(mainConfigs.reloadTime.Value)
unequipListen:Disconnect()
if canStop then
ammo.Value = ammo.Value + ammoToLoad
totalAmmoValue.Value = totalAmmoValue.Value - ammoToLoad
updateGUI()
reloading = false
mag.Transparency = 0
end
end
end
-- ════════════════════════════════════════════════════════════
-- mouse hit
-- ════════════════════════════════════════════════════════════
local function getMouseHit()
-- PC mouse.Hit
if not isMobile then
return mouse.Hit.Position
end
-- Mobilde raycast
local screenCenter = Vector2.new(camera.ViewportSize.X / 2, camera.ViewportSize.Y / 2)
local unitRay = camera:ScreenPointToRay(screenCenter.X, screenCenter.Y)
-- Ray oluştur
local origin = unitRay.Origin
local direction = unitRay.Direction * 1000
-- Raycast
local ray = Ray.new(origin, direction)
local hitPart, hitPos = workspace:FindPartOnRayWithIgnoreList(ray, {character, bulletStorage}, false, true)
if hitPos then
return hitPos
else
return origin + direction
end
end
local function fire()
if not equipped or not canFire or humanoid.Health <= 0 or reloading then
return
end
if ammo.Value <= 0 then
emptySound:Play()
return
end
local ray = Ray.new(root.CFrame.p, (tool.barrel.CFrame.p - root.CFrame.p).unit * (root.Position - tool.barrel.Position).magnitude)
local part = workspace:FindPartOnRayWithIgnoreList(ray, {character, bulletStorage}, false, true)
if not part or part.Parent:FindFirstChildOfClass("Humanoid") or part.Parent.Parent:FindFirstChildOfClass("Humanoid") then
-- take the position
local targetPos = getMouseHit()
if shotgun then
spawn(function()
for i = 1, pellets do
local endPos = targetPos + Vector3.new(
math.random(-spread * 10, spread * 10) / 10,
math.random(-spread * 10, spread * 10) / 10,
math.random(-spread * 10, spread * 10) / 10
)
bulletModule.renderBullet(player, tool.Name, endPos)
if i >= pellets then
fireEvent:FireServer(tool.Name, endPos, true)
else
fireEvent:FireServer(tool.Name, endPos, false)
totalFired += 1
end
end
end)
else
local endPos = targetPos + Vector3.new(
math.random(-spread * 10, spread * 10) / 10,
math.random(-spread * 10, spread * 10) / 10,
math.random(-spread * 10, spread * 10) / 10
)
fireEvent:FireServer(tool.Name, endPos)
totalFired += 1
bulletModule.renderBullet(player, tool.Name, endPos)
end
local totalWhenFired = totalFired
canFire = false
delay(60 / rpm, function()
canFire = true
end)
local canRespond = true
posVerifier.OnClientInvoke = function()
if canRespond then
canRespond = false
local response = player.UserId * player.UserId - player.UserId
response = response + #game:GetService("Players"):GetPlayers()
return response, totalWhenFired
end
end
recoilAnim:Play()
cameraShake()
end
end
-- ════════════════════════════════════════════════════════════
-- fire system ending
-- ════════════════════════════════════════════════════════════
local function mouseBegan()
if gunMode == "auto" then
while mouseDown and equipped do
if canFire then
fire()
end
wait(60 / rpm)
end
elseif gunMode == "semi" then
if canFire and equipped then
fire()
end
end
end
tool.Equipped:Connect(function()
if equipAnim then
equipAnim:Play()
wait(equipAnim.Length * 0.5)
end
aimAnim:Play()
if isMobile then
local cursor = ensureMobileCursor()
if cursor then
cursor.Enabled = true
end
createMobileButtons(
function(isPressed)
if isPressed then
mouseDown = true
spawn(mouseBegan)
else
mouseDown = false
end
end,
function()
spawn(reload)
end
)
else
mouse.Icon = "rbxassetid://"..config.misc.crosshair.Value
end
equipped = true
currentGui = gunGui:Clone()
currentGui.Parent = player.PlayerGui
currentGui.infoFrame.gunName.Text = tool.Name
updateGUI()
ammoListen = ammo.Changed:Connect(updateGUI)
totalAmmoListen = totalAmmoValue.Changed:Connect(updateGUI)
end)
tool.Unequipped:Connect(function()
holsterAnim:Stop()
aimAnim:Stop()
reloadAnim:Stop()
recoilAnim:Stop()
mag.Transparency = 0
if equipAnim then
equipAnim:Stop()
end
blur.Size = 0
if mobileCursorGui then
mobileCursorGui.Enabled = false
end
if mobileControlsGui then
mobileControlsGui:Destroy()
mobileControlsGui = nil
end
if currentGui then
currentGui:Destroy()
currentGui = nil
end
if ammoListen then
ammoListen:Disconnect()
ammoListen = nil
end
if totalAmmoListen then
totalAmmoListen:Disconnect()
totalAmmoListen = nil
end
equipped = false
mouseDown = false
reloading = false
mouse.Icon = ""
end)
inputService.InputBegan:Connect(function(input, gpe)
if equipped and not gpe then
if input.UserInputType == Enum.UserInputType.MouseButton1 then
mouseDown = true
mouseBegan()
elseif input.UserInputType == Enum.UserInputType.Keyboard then
if input.KeyCode == Enum.KeyCode.R then
if not reloading then
reload()
end
elseif input.KeyCode == Enum.KeyCode.F then
if not hAnim then
hAnim = true
holsterAnim:Play()
else
hAnim = false
holsterAnim:Stop()
end
end
end
end
end)
inputService.InputEnded:Connect(function(input, gpe)
if input.UserInputType == Enum.UserInputType.MouseButton1 then
mouseDown = false
end
end)
end
return gunClient
bullet render code
local bulletModule = {}
local player = game:GetService("Players").LocalPlayer
local tweenService = game:GetService("TweenService")
local debrisService = game:GetService("Debris")
local bulletStorage = workspace:WaitForChild("bulletStorage")
local gunStorage = game:GetService("ReplicatedStorage"):WaitForChild("gunStorage")
local gunEvents = gunStorage:WaitForChild("events")
local fireEvent = gunEvents:WaitForChild("fire")
local damageEvent = gunEvents:WaitForChild("damage")
local gunObjects = gunStorage:WaitForChild("objects")
local fleshTemp = gunObjects:WaitForChild("fleshHit")
local objectTemp = gunObjects:WaitForChild("objectHit")
local hitmarker = game:GetService("SoundService"):WaitForChild("hitmarker")
-- Mermi ayarları
local BULLET_SPEED = 1000
local BULLET_LENGTH = 2
local BULLET_WIDTH = 0.2
--\\ functions
local function getCallersGun(caller, gunName)
local tool = caller.Character:FindFirstChildOfClass("Tool")
if tool and tool:FindFirstChild("gunClient") and tool.Name == gunName then
return tool
else
for _,v in pairs(caller.Backpack:GetChildren()) do
if v:IsA("Tool") and v:FindFirstChild("gunClient") and v.Name == gunName then
return v
end
end
end
end
local function burstCosmetics(object, length)
for _,v in pairs(object:GetChildren()) do
if v:IsA("ParticleEmitter") or v:IsA("Light") then
v.Enabled = true
delay(length, function()
v.Enabled = false
end)
end
end
end
local function firesound(object, lenght)
for _,b in pairs(object:GetChildren()) do
if b:IsA("Sound") then
b:Play()
end
end
end
function bulletModule.renderBullet(caller, gunName, endPos)
local gun = getCallersGun(caller, gunName)
if gun ~= nil then
local barrel = gun.barrel
local handle = gun.Handle
if player.Character then
local root = player.Character:FindFirstChild("HumanoidRootPart")
if root and (root.Position - barrel.Position).magnitude < 750 then
burstCosmetics(barrel)
firesound(handle)
-- Raycast ile hedef bul
local ray = Ray.new(barrel.Position, (endPos - barrel.Position).Unit * 5000)
local hitPart, hitPos, hitNorm = workspace:FindPartOnRayWithIgnoreList(ray, {caller.Character, bulletStorage}, false, true)
local startPos = barrel.Position
local targetPos = hitPos or endPos
local direction = (targetPos - startPos).Unit
local distance = (targetPos - startPos).Magnitude
local bullet = Instance.new("Part")
bullet.Name = "Bullet"
bullet.Anchored = true
bullet.CanCollide = false
bullet.Transparency = 1
bullet.Size = Vector3.new(BULLET_WIDTH, BULLET_WIDTH, BULLET_LENGTH)
bullet.CFrame = CFrame.new(startPos, targetPos)
bullet.Color = Color3.fromRGB(255, 240, 100)
bullet.Material = Enum.Material.Neon
bullet.Parent = bulletStorage
local travelTime = distance / BULLET_SPEED
local startTime = tick()
local heartbeatConnection
heartbeatConnection = game:GetService("RunService").Heartbeat:Connect(function()
local elapsed = tick() - startTime
local alpha = math.min(elapsed / travelTime, 1)
if alpha >= 1 then
-- Hedefe ulaştı
heartbeatConnection:Disconnect()
bullet:Destroy()
else
local currentPos = startPos + (direction * distance * alpha)
bullet.CFrame = CFrame.new(currentPos, currentPos + direction)
end
end)
--anti
debrisService:AddItem(bullet, travelTime + 0.2)
if hitPart ~= nil and hitPart.Parent ~= nil then
local humanoid = hitPart.Parent:FindFirstChildOfClass("Humanoid") or hitPart.Parent.Parent:FindFirstChildOfClass("Humanoid")
if humanoid then
if player == caller then
hitmarker:Play()
damageEvent:FireServer(gunName, hitPart)
end
local fleshHit = fleshTemp:Clone()
fleshHit.CFrame = CFrame.new(hitPos, hitPos + hitNorm)
fleshHit.Parent = bulletStorage
burstCosmetics(fleshHit, .15)
debrisService:AddItem(fleshHit, .15)
else
local objectHit = objectTemp:Clone()
objectHit.CFrame = CFrame.new(hitPos, hitPos + hitNorm)
objectHit.Parent = bulletStorage
burstCosmetics(objectHit, .15)
debrisService:AddItem(objectHit, .15)
end
end
end
end
end
end
--\\ returning module
return bulletModule




