-- Script inside GetPlayerInput
local tool = script.Parent
local handle = tool:WaitForChild("Handle")
local firePoint = handle:WaitForChild("FirePoint")
local FireSound = handle:WaitForChild("Fire")
local sID = 8561647924
local mouseEvent = script.Parent:WaitForChild("MouseEvent")
local projectiles = game:GetService("ServerStorage"):WaitForChild("Projectiles")
--// Fire Rate Control
local fireRate = 0.1
local lastFireTime = {}
-- // Customizable Variables
local speed = 300
local damage = 15
local bulletMaterial = Enum.Material.Neon
local bulletSize = Vector3.new(0.2, 0.2, 0.3)
local bulletColor = Color3.fromRGB(255, 234, 0)
local bulletLife = 4
function fire()
local cl = FireSound:Clone()
cl.Parent = FireSound.Parent
cl:Play()
game:GetService("Debris"):AddItem(cl, cl.TimeLength)
end
-- Function to create and fire a bullet
local function createBullet(player, mousePosition)
-- Create the bullet
local bullet = projectiles.BulletPistol:Clone()
bullet.Name = "Bullet"
bullet.Material = bulletMaterial
bullet.Size = bulletSize
bullet.Color = bulletColor -- Set to your desired color
bullet.CanCollide = false
bullet.Anchored = false
bullet.CFrame = firePoint.WorldCFrame
-- Parent the bullet to the Workspace
bullet.Parent = game.Workspace
print("Bullet created at position:", bullet.Position)
-- Calculate direction and velocity
local direction = (mousePosition - bullet.Position).Unit
local velocity = direction * speed
-- Set the bullet's velocity
bullet.AssemblyLinearVelocity = velocity
print("Bullet velocity set to:", bullet.AssemblyLinearVelocity)
-- Add cleanup after some time (e.g., 5 seconds)
game.Debris:AddItem(bullet, 3)
bullet.Touched:Connect(function(otherPart)
if otherPart.Parent ~= player.Character or otherPart.Name ~= "Handle" then
if otherPart.Name == "Bullet" then return end
local humanoid = otherPart.Parent:FindFirstChild("Humanoid")
if humanoid and humanoid ~= player.Character:FindFirstChild("Humanoid") then
humanoid:TakeDamage(damage)
bullet:Destroy()
end
end
if otherPart:IsA("BasePart") and not otherPart.Parent:FindFirstChild("Humanoid") then
if otherPart.Name == "Bullet" then return end
if otherPart:GetAttribute("IsWindow") then
local hp = otherPart:GetAttribute("Health")
hp -= damage
otherPart:SetAttribute("Health", hp)
else
local snd = Instance.new("Sound")
snd.Parent = bullet
snd.Name = "ImpactPart"
snd.RollOffMode = Enum.RollOffMode.Linear
snd.RollOffMaxDistance = 150
snd.RollOffMinDistance = 0
snd.SoundId = "rbxassetid://4427232788"
snd.Pitch = math.random(0.8, 1.4)
snd:Play()
end
end
end)
end
mouseEvent.OnServerEvent:Connect(function(player, mousePosition)
local playerId = player.UserId
if not lastFireTime[playerId] then
lastFireTime[playerId] = 0
end
if tick() - lastFireTime[playerId] >= fireRate then
fire()
createBullet(player, mousePosition)
lastFireTime[playerId] = tick()
end
end)
I’m making guns for my game, but here’s the thing:
I decided to switch to cloning for testing purposes, but I there’s this tiny delay before the bullet moves, like a frame or so, is that normal? How do I prevent that?
Also, should I use bodymovers instead?
Hey, I just saw this, and though I don’t have the time to write an example I can explain probably the best way to do this.
Server
Use any info passed from the client like the speed and direction to calculate drop and other values.
Then, using these values, have the server move the CFrame of the Projectile or to update/create a method to move the projectile.
Then you could use a raycast or hitbox module (If you plan to do HitDetection on the server)
Client
Tell the server (using a Remote/Bindable) direction, speed and any other values like drop so that the server can use the values to move the projectile, thus it’s movements will be automatically replicated for all clients.
Sometimes I’d actually have hit detection on the client (depending on the situation), and then fire to the server, to verify the hit. If you doing HitDetection on the Client. RaycastHitboxV4 is quite good, I’m also pretty sure it works on the server, though it may be less performant than if it is used on the client.
Performance review of your code
First off, I’d like to warn against using .Touched due to there being a lot more accurate and efficient alternatives, like RaycastHitboxV4.
For now that’s all the time I have, there are a few other areas where this could improve on, but I wish you luck!
-- Variables
local tool = script.Parent
local player = game:GetService("Players").LocalPlayer or game:GetService("Players").PlayerAdded:Wait()
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local root = char:WaitForChild("HumanoidRootPart")
local mouse = player:GetMouse()
local mouseEvent = script.Parent:WaitForChild("MouseEvent")
local currentCamera = workspace.CurrentCamera
local automaticMode = true -- Toggle for automatic firing mode
local firing = false -- Debounce to prevent spamming
local active = false
-- // Customizable Variables
local speed = 100
local damage = 15
local bulletLife = 4
local bulletType = "BulletPistol"
-- Fire rate control (adjust as necessary)
local fireRate = 0.1
local lastFireTime = 0
-- ShiftLock Toggle
function toggleShiftLock(active)
if active then
---------------------------------------------------------
game:GetService("RunService"):BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter
local _, y = workspace.CurrentCamera.CFrame.Rotation:ToEulerAnglesYXZ() --Get the angles of the camera
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0,y,0)
hum.AutoRotate = false
hum.CameraOffset = Vector3.new(1.75,.5,0)
mouse.Icon = "rbxassetid://12829852445"
end)
---------------------------------------------------------
elseif not active then
---------------------------------------------------------
game:GetService("RunService"):UnbindFromRenderStep("ShiftLock")
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.Default
hum.AutoRotate = true
hum.CameraOffset = Vector3.new(0,0,0)
mouse.Icon = ""
end
end
-- Function to fire bullets
local function fireBullet()
if tick() - lastFireTime >= fireRate then
mouseEvent:FireServer(mouse.Hit.Position, speed, damage, bulletLife, fireRate, bulletType)
lastFireTime = tick()
end
end
-- Function to handle mouse button input
local function onMouseButtonDown()
if firing then return end
if automaticMode then
while mouseButton1Down do
fireBullet()
task.wait(fireRate) -- Ensuring bullets are fired at a controlled rate
end
else
firing = true
fireBullet()
task.wait(fireRate)
firing = false
end
end
-- Detecting mouse button down event
mouse.Button1Down:Connect(function()
if not active then return end
mouseButton1Down = true
onMouseButtonDown()
end)
-- Detecting mouse button up event (to stop automatic fire)
mouse.Button1Up:Connect(function()
mouseButton1Down = false
end)
-- Detecting if tool equipped to change mouse/cam
tool.Equipped:Connect(function()
active = true
toggleShiftLock(active)
end)
-- Detecting if tool unequipped to change mouse/cam
tool.Unequipped:Connect(function()
active = false
toggleShiftLock(active)
end)
This is what my local script looks like, everything look good?
Alright yea, sounds good, should be quite efficient, if you do notice any severe frame drop or lag, it would most likely come from either a memory leak with the hitboxes or memory leaks with the client, if it’s the client (LocalScript) that causes issues, I’d try and organize your loops into threads and then use a coroutine to schedule them, so they can run along side non cyclic code.
I’ve been getting an error saying: Players.RetroPect.Backpack.TestGun.MainControl:39: attempt to perform arithmetic (sub) on nil and Vector3
-- Variables
local tool = script.Parent
local player = game:GetService("Players").LocalPlayer or game:GetService("Players").PlayerAdded:Wait()
local char = player.Character or player.CharacterAdded:Wait()
local hum = char:WaitForChild("Humanoid")
local root = char:WaitForChild("HumanoidRootPart")
local mouse = player:GetMouse()
local mouseEvent = script.Parent:WaitForChild("MouseEvent")
local currentCamera = workspace.CurrentCamera
local automaticMode = true -- Toggle for automatic firing mode
local firing = false -- Debounce to prevent spamming
local active = false
-- // Customizable Variables
local speed = 100
local damage = 15
local bulletLife = 4
-- Fire rate control (adjust as necessary)
local fireRate = 0.1
local lastFireTime = 0
-- ShiftLock Toggle
function toggleShiftLock(active)
if active then
---------------------------------------------------------
game:GetService("RunService"):BindToRenderStep("ShiftLock", Enum.RenderPriority.Character.Value, function()
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.LockCenter
local _, y = workspace.CurrentCamera.CFrame.Rotation:ToEulerAnglesYXZ() --Get the angles of the camera
root.CFrame = CFrame.new(root.Position) * CFrame.Angles(0,y,0)
hum.AutoRotate = false
hum.CameraOffset = Vector3.new(1.75,.5,0)
mouse.Icon = "rbxassetid://12829852445"
end)
---------------------------------------------------------
elseif not active then
---------------------------------------------------------
game:GetService("RunService"):UnbindFromRenderStep("ShiftLock")
game:GetService("UserInputService").MouseBehavior = Enum.MouseBehavior.Default
hum.AutoRotate = true
hum.CameraOffset = Vector3.new(0,0,0)
mouse.Icon = ""
end
end
-- Function to fire bullets
local function fireBullet()
if tick() - lastFireTime >= fireRate then
mouseEvent:FireServer(mouse.Hit.Position, speed, damage, bulletLife, fireRate, bulletType)
lastFireTime = tick()
end
end
-- Function to handle mouse button input
local function onMouseButtonDown()
if firing then return end
if automaticMode then
while mouseButton1Down do
fireBullet()
task.wait(fireRate) -- Ensuring bullets are fired at a controlled rate
end
else
firing = true
fireBullet()
task.wait(fireRate)
firing = false
end
end
-- Detecting mouse button down event
mouse.Button1Down:Connect(function()
if not active then return end
mouseButton1Down = true
onMouseButtonDown()
end)
-- Detecting mouse button up event (to stop automatic fire)
mouse.Button1Up:Connect(function()
mouseButton1Down = false
end)
-- Detecting if tool equipped to change mouse/cam
tool.Equipped:Connect(function()
active = true
toggleShiftLock(active)
end)
-- Detecting if tool unequipped to change mouse/cam
tool.Unequipped:Connect(function()
active = false
toggleShiftLock(active)
end)
Local Script ^
Server Script:
-- Script inside GetPlayerInput
local tool = script.Parent
local handle = tool:WaitForChild("Handle")
local firePoint = handle:WaitForChild("FirePoint")
local FireSound = handle:WaitForChild("Fire")
local mouseEvent = script.Parent:WaitForChild("MouseEvent")
local projectiles = game:GetService("ServerStorage"):WaitForChild("Projectiles")
local hitbox = require(game:GetService("ReplicatedStorage").RaycastHitboxV4)
local bulletType = "BulletPistol"
local lastFireTime = {}
function fire()
local cl = FireSound:Clone()
cl.Parent = FireSound.Parent
cl:Play()
game:GetService("Debris"):AddItem(cl, cl.TimeLength)
end
mouseEvent.OnServerEvent:Connect(function(player, mousePosition, speed, damage, bulletLife, fRate)
local fireRate = fRate
local function createBullet(player, mousePosition, speed, damage, bulletLife)
local bullet = projectiles:FindFirstChild(bulletType):Clone()
bullet.CFrame = firePoint.WorldCFrame
local bulletHitbox = hitbox.new(bullet)
local Params = RaycastParams.new()
Params.FilterDescendantsInstances = {tool.Parent} --- remember to define our character!
Params.FilterType = Enum.RaycastFilterType.Exclude
local bodyForce = Instance.new("BodyForce")
-- Parent the bullet to the Workspace
bullet.Parent = game.Workspace
--bullet.AssemblyLinearVelocity = velocity
local forceDirection = (mousePosition - bullet.Position).Unit
local forceStrength = speed
bodyForce.Force = forceDirection * forceStrength
print("BodyForce velocity = "..bodyForce.Force.." BodyForce dir = "..forceDirection)
bodyForce.Parent = bullet
print("Bullet velocity set to:", bullet.AssemblyLinearVelocity)
-- Add cleanup after some time (e.g., 5 seconds)
game.Debris:AddItem(bullet, bulletLife)
bulletHitbox.OnHit:Connect(function(otherPart, humanoid)
if otherPart.Parent ~= player.Character or otherPart.Name ~= "Handle" then
if otherPart.Name == "Bullet" then return end
local humanoid = otherPart.Parent:FindFirstChild("Humanoid")
if humanoid and humanoid ~= player.Character:FindFirstChild("Humanoid") then
humanoid:TakeDamage(damage)
bullet:Destroy()
end
end
if otherPart.Parent.ClassName == "Accessory" and otherPart.Name == "Handle" then
if otherPart.Parent.Parent ~= player.Character then
if otherPart.Parent.Parent.Name == "Bullet" then return end
local humanoid = otherPart.Parent.Parent:FindFirstChild("Humanoid")
if humanoid and humanoid ~= player.Character:FindFirstChild("Humanoid") then
humanoid:TakeDamage(damage)
bullet:Destroy()
end
end
end
if otherPart:IsA("BasePart") and not otherPart.Parent:FindFirstChild("Humanoid") then
if otherPart.Name == "Bullet" then return end
if otherPart:GetAttribute("IsWindow") then
local hp = otherPart:GetAttribute("Health")
hp -= damage
otherPart:SetAttribute("Health", hp)
else
local snd = Instance.new("Sound")
snd.Parent = bullet
snd.Name = "ImpactPart"
snd.RollOffMode = Enum.RollOffMode.Linear
snd.RollOffMaxDistance = 150
snd.RollOffMinDistance = 0
snd.SoundId = "rbxassetid://4427232788"
snd.Pitch = math.random(0.8, 1.4)
snd:Play()
end
end
end)
bulletHitbox:HitStart()
end
local playerId = player.UserId
if not lastFireTime[playerId] then
lastFireTime[playerId] = 0
end
if tick() - lastFireTime[playerId] >= fireRate then
fire()
createBullet()
lastFireTime[playerId] = tick()
end
end)
I don’t get why it’s not firing the mouse position
I sent you a dm on discord, I found out the reason behind your error, aka you forgot to pass the parameters through the createBullet() function on about ~ Line 100 (Server Script)