§ How to · with Picoo
How to make a gun in Roblox Studio
By Sametcan Tasgiran, Founder & Developer·Published ·Updated
A gun is where client trust becomes free kills: if the client reports the hit, the client decides who dies.
The Tool, the Handle configuration, and the damage validation are three separate things people get wrong — usually in that order.
Equippable Tool
Handle configured so it actually sits in the hand: CanCollide false, Massless, correct Grip.
Server-authoritative damage
The client says "I fired, here is my aim"; the server decides what was hit and for how much.
Rate limit + origin check
Fire rate enforced server-side, and the shot origin is checked against the player's actual position.
Range and cone
Damage falls inside a defined range and spread — not an unbounded ray to anywhere on the map.
Visual feedback
Muzzle effect and impact on the client so shooting feels responsive without waiting on a round trip.
Reskinnable
Flamethrower, laser, blaster, water gun — same skeleton, different cosmetics and numbers.
Files Picoo ships for this prompt
3 files · 147 lines · ~30s · 1 credit
FlamethrowerServer
Damage, rate limit, origin validation. The authority.
72 lines
FlamethrowerLocal
Input, aim direction, local muzzle feedback.
35 lines
FlamethrowerCosmetic
Handle styling and effects so it does not look like a grey brick.
40 lines
Sample output: ServerScriptService.FlamethrowerServer
-- The client sends its AIM. It does not send its HITS.
-- If it sent hits, an exploiter would send "I hit everyone, for 1000".
fireEvent.OnServerEvent:Connect(function(plr, direction)
if typeof(direction) ~= "Vector3" then return end
-- Rate limit on the server. The client's cooldown is cosmetic.
local now = os.clock()
if lastFire[plr] and now - lastFire[plr] < FIRE_RATE then return end
lastFire[plr] = now
local char = plr.Character
local hrp = char and char:FindFirstChild("HumanoidRootPart")
if not hrp then return end
-- Origin comes from the SERVER's view of where the player is, never from
-- the client — otherwise "I shot from inside your base" is a valid claim.
local origin = hrp.Position
local params = RaycastParams.new()
params.FilterType = Enum.RaycastFilterType.Exclude
params.FilterDescendantsInstances = { char }
local result = workspace:Raycast(origin, direction.Unit * RANGE, params)
local hum = result and result.Instance.Parent:FindFirstChildOfClass("Humanoid")
if hum and hum.Health > 0 then
hum:TakeDamage(DAMAGE)
end
end)Building a gun in Roblox Studio
Guns fail in three distinct places, and most tutorials only cover the first one.
The first is the Tool itself. A Roblox Tool needs a BasePart child named exactly "Handle", and that part has to be CanCollide false and unanchored — leave it anchored and the weapon equips while the model stays hanging in the air where you built it. Massless keeps it from dragging the character's physics around. None of this errors; it just looks broken.
The second is who decides what got hit. The convenient design is for the client to raycast and tell the server "I hit this player" — and it is the reason so many Roblox shooters are trivially exploitable. That code runs on the player's machine, so the message can say anything: everyone, at once, for any damage. The fix is a boundary, not a check: the client sends its aim DIRECTION, and the server raycasts from the player's server-side position. The client can lie about where it is pointing; it cannot lie about what that ray hits.
The third is rate. Without a server-side fire-rate timestamp, a modified client fires every frame. Same shape as a cooldown, same reason it has to live on the server. Validate the origin too — a spoofed origin is how people shoot through walls from spawn.
Responsiveness comes from splitting feedback away from truth: muzzle flash, tracer and impact play locally the instant you click, so the weapon feels immediate, while damage resolves on the server a few frames later. Players never notice that gap. They notice damage that does not match what they saw — which is exactly what client-authoritative hits produce once someone starts cheating.
See more on the Luau generator, the game builder, or browse the full blog.
Frequently asked
Why does my Roblox gun not appear in the player's hand?+
The Handle. A Tool needs a BasePart child named exactly "Handle", and it must be CanCollide false, Anchored false and ideally Massless. An anchored Handle is the classic one — the Tool equips and the model stays floating where you built it.
Should the client or the server detect hits?+
The server. If the client reports what it hit, an exploiter reports hitting everyone at once from across the map, and no amount of client-side validation stops it — that code runs on their machine. Send the aim direction, let the server raycast.
Isn't server-side raycasting laggy?+
The shot feels instant because the visual feedback — muzzle flash, tracer, impact — is played locally the moment you click. Only the damage waits for the server, and a hit registering 60ms later is invisible to the player. What players actually notice is inconsistent damage, which is what client trust produces.
How do I stop rapid-fire exploits?+
Keep the fire rate on the server with a per-player timestamp, exactly like a cooldown. A client-side debounce is a UI convenience; the server's is the rule. Also validate the origin against the player's real position, or a spoofed origin lets them shoot through walls.
Can I make it a laser or a water gun instead?+
Yes — the skeleton is the same for any ranged weapon: aim from the client, validate and damage on the server, cosmetics on top. Change the effect, the range, the damage and the fire rate.
Related Picoo prompts
Roblox combat system
5 files · 140 lines · 42 seconds · 1 credit. Drop into your place and press Play.
an ability system with cooldowns in Roblox
If the cooldown lives on the client, it is a suggestion. Every cooldown, cost and damage number here is enforced on the server.
How to prevent exploits in Roblox
4 files · 160 lines · 44 seconds · 1 credit. Drop-in guards + a one-prompt audit of your existing scripts.