Roblox Hit Detection Guide: Raycast, RaycastParams & FastCast for Weapons
TL;DR
Building a solid hit detection system for ranged weapons needs three things: understanding Workspace:Raycast(), configuring RaycastParams to filter the right objects, and knowing when to swap to FastCast for projectiles with gravity and travel time. On top of that, all damage logic must be validated server-side.
1. Workspace:Raycast() — The Hitscan Foundation
When you call Workspace:Raycast(origin, direction, raycastParams), the engine checks along the path of the ray and returns information about the first object it intersects, such as the position of the hit, the normal of the surface, and the part itself.
A minimal hitscan fire function on the LocalScript side:
local Players = game:GetService("Players")
local player = Players.LocalPlayer
local char = player.Character or player.CharacterAdded:Wait()
local handle = script.Parent:WaitForChild("Handle")
local function fireWeapon(targetPosition)
local origin = handle.Position
local direction = (targetPosition - origin).Unit * 500 -- 500 stud range
local rayParams = RaycastParams.new()
rayParams.FilterDescendantsInstances = {char}
rayParams.FilterType = Enum.RaycastFilterType.Exclude
rayParams.IgnoreWater = true
local result = workspace:Raycast(origin, direction, rayParams)
if result then
script.Parent.HitEvent:FireServer(result.Instance, result.Position)
end
end
2026 API update: Roblox has added
RaycastParams.ExcludeInstancesandRaycastParams.IncludeInstancesas cleaner replacements forFilterDescendantsInstances. TheExcludeInstancesandIncludeInstancesproperties store a set of objects and their descendants that will be excluded and included from the query. Note thatFilterDescendantsInstanceshas been superseded byExcludeInstancesandIncludeInstances, which should be used for new work.
2. Configuring RaycastParams
Raycast parameters allow you to fine-tune what your raycast detects — for example, you might want to ignore certain objects like transparent parts, or only detect objects tagged as enemies. Using RaycastParams, you can set a collision group to include or exclude specific groups, or ignore the player's own character to avoid self-collision.
| Property | Use |
|---|---|
ExcludeInstances | Objects to skip — shooter's character, weapon model |
IncludeInstances | Whitelist — only these objects can be hit |
IgnoreWater | Skip Terrain water |
CollisionGroup | Restrict to a physics collision group |
FilterType quick reference:
Exclude— everyBasePartis considered except those that are descendants of objects in the array.Include— onlyBasePartsthat are descendants of objects in the array are considered.
3. FastCast — Projectiles with Gravity
Standard raycasting is instantaneous — great for lasers and hitscan rifles. For arrows, grenades, or any bullet with visible travel time and drop, use FastCast.
FastCast is a module that uses raycasting (commonly used for hitscan weapons, like lasers) in such a way that these raycasts can simulate bullet physics. Each frame it casts a short ray between the projectile's previous and current position, catching collisions accurately even at high speeds.
Get FastCast Redux from the Toolbox (library ID 4453855787).
Setup
local FastCast = require(game.ReplicatedStorage.FastCastRedux)
local caster = FastCast.new()
local castParams = RaycastParams.new()
castParams.FilterDescendantsInstances = {workspace.Bullets}
castParams.FilterType = Enum.RaycastFilterType.Exclude
local castBehavior = FastCast.newBehavior()
castBehavior.Acceleration = Vector3.new(0, -workspace.Gravity, 0)
castBehavior.CosmeticBulletTemplate = game.ReplicatedStorage.BulletPart
castBehavior.CosmeticBulletContainer = workspace.Bullets
local function shootProjectile(origin, direction, speed)
caster:Fire(origin, direction, speed, castBehavior, castParams)
end
Setting castBehavior.Acceleration to Vector3.new(0, -workspace.Gravity, 0) produces natural bullet drop. Tune it per weapon — an arrow needs more arc than a sniper bullet.
Hit Event
caster.LengthChanged:Connect(function(cast, lastPoint, dir, length, velocity, bullet)
if bullet then
bullet.CFrame = CFrame.lookAt(lastPoint + dir * length, lastPoint + dir * (length + 1))
end
end)
caster.RayHit:Connect(function(cast, result, velocity, bullet)
game.ReplicatedStorage.HitEvent:FireServer(result.Instance, result.Position, velocity)
if bullet then bullet:Destroy() end
end)
caster.CastTerminating:Connect(function(cast)
local b = cast.RayInfo.CosmeticBulletObject
if b then b:Destroy() end
end)
4. Server-Side Hit Validation
This is the step most tutorials skip — and it's the most important for anti-cheat.
The biggest issue with remote events is that an exploiter can fire them from the client and alter the value sent to be something it shouldn't. Always validate on the server before applying damage.
-- ServerScript
local MAX_RANGE = 500
local MAX_DAMAGE = 50
game.ReplicatedStorage.HitEvent.OnServerEvent:Connect(function(shooter, hitPart, hitPos)
-- 1. Type safety
if typeof(hitPart) ~= "Instance" or not hitPart:IsA("BasePart") then return end
if typeof(hitPos) ~= "Vector3" then return end
-- 2. Magnitude / range check
local root = shooter.Character and shooter.Character:FindFirstChild("HumanoidRootPart")
if not root then return end
if (root.Position - hitPos).Magnitude > MAX_RANGE * 1.15 then return end -- 15% latency buffer
-- 3. Identify victim
local hitChar = hitPart:FindFirstAncestorOfClass("Model")
local victim = game.Players:GetPlayerFromCharacter(hitChar)
if not victim or victim == shooter then return end
local humanoid = hitChar:FindFirstChildOfClass("Humanoid")
if not humanoid or humanoid.Health <= 0 then return end
-- 4. Optional: server-side confirmatory raycast
local sParams = RaycastParams.new()
sParams.FilterDescendantsInstances = {shooter.Character}
sParams.FilterType = Enum.RaycastFilterType.Exclude
local dir = (hitPos - root.Position)
local sResult = workspace:Raycast(root.Position, dir, sParams)
if sResult and sResult.Instance:IsDescendantOf(hitChar) then
humanoid:TakeDamage(MAX_DAMAGE)
end
end)
When working on a system with client-side hit detection, basic sanity checks on the server — like magnitude checks — are essential to make sure hits are legit. For competitive games, the confirmatory server raycast in step 4 is highly recommended.
5. Common Pitfalls
Shooter hits themselves — Always add the full Character model (not just HumanoidRootPart) to your exclusion list. Accessories and hat parts are separate BasePart instances that the ray can still hit.
Ray fires but hits nothing — Check that your direction vector is scaled by weapon range (e.g. .Unit * 500). A raw unit vector only casts 1 stud.
FastCast bullet spawns below the barrel — Usually caused by an unanchored view model experiencing physics gravity. Parent cosmetic bullets to a plain Folder in Workspace rather than to the tool.
High fire rate floods the server — The common method is to shoot a ray from the client and another from the server via a remote event, but with automatic guns that have a very high fire rate, many remote events would have to be fired for each player. For automatic weapons, batch hit reports every 0.1–0.2 s instead of one event per bullet.
FilterDescendantsInstances vs. ExcludeInstances confusion — Roblox has added RaycastParams.ExcludeInstances and RaycastParams.IncludeInstances as simpler, more flexible APIs to supersede FilterDescendantsInstances for raycasts and other spatial queries. The old pattern still works, but prefer the new one for any new code.
6. Which Approach to Use?
| Scenario | Method |
|---|---|
| Instant-hit rifle / laser / shotgun | Workspace:Raycast() hitscan |
| Arrow, grenade, rocket with arc | FastCast + Acceleration |
| Melee swing detection | Multiple raycasts along blade per RunService.Stepped |
| AoE / splash detection | workspace:GetPartBoundsInRadius() / SphereCast |
FAQ
Q: Client or server raycast? Cast on the client for responsiveness, validate on the server before dealing damage. Pure server-side casting adds noticeable input lag.
Q: Is FastCast still maintained? The core module has been stable for years and is widely used in shipped games. It's open source, so you can fork it if needed.
Q: Can I use ExcludeInstances with FastCast? Yes — FastCast accepts any RaycastParams object you construct, new API included.
Q: Where can I find a complete working example? The GM Market marketplace has Roblox combat scripts that pair FastCast with server-side validation, useful as a reference if you want to study a full system before building your own.
Sources: Roblox Creator Hub — Hit Detection with Lasers · RaycastParams API · New Spatial Query Filters · FastCast thread