Skip to main content

Hitbox

Hitbox wraps an invisible, non-colliding Part and uses Workspace:GetPartsInPart to find what is overlapping it. Use it for melee swings, area-of-effect abilities, trigger zones or anything else where you want to know which parts or humanoids are inside a volume right now, or for a period of time.

A Hitbox can be queried once (Check, CheckHumanoids), scanned for a fixed duration on Heartbeat (Scan, ScanHumanoids) or scanned on your own schedule using a repeat interval or any signal (ScanWithEvent, ScanHumanoidsWithEvent). The hitbox part can be welded to another part so it follows a character or weapon. Everything the hitbox creates (the part, welds, timers and connections) is tracked by a Trove and released by Destroy; destroying the hitbox part also destroys the hitbox.

Create one with Hitbox.new, or from existing geometry with Hitbox.fromPart, Hitbox.fromModel or Hitbox.convert.

local Hitbox = require(path.to.Hitbox)

local function swing(character: Model)
	local rootPart = character:WaitForChild("HumanoidRootPart") :: BasePart

	local params = OverlapParams.new()
	params.FilterType = Enum.RaycastFilterType.Exclude
	params.FilterDescendantsInstances = { character }

	-- A 4x4x4 block one stud in front of the character, welded to the root so it follows them
	local hitbox = Hitbox.new(
		rootPart.CFrame * CFrame.new(0, 0, -3),
		Vector3.new(4, 4, 4),
		Enum.PartType.Block,
		params,
		workspace,
		false,
		rootPart
	)

	-- Scan every frame for half a second and damage each humanoid once
	local alreadyHit: { [Humanoid]: boolean } = {}
	hitbox:ScanHumanoids(0.5, function(humanoid)
		if alreadyHit[humanoid] then
			return
		end
		alreadyHit[humanoid] = true
		humanoid:TakeDamage(10)
	end)

	task.delay(0.5, function()
		hitbox:Destroy()
	end)
end

Credits: Trove and Timer are by sleitnick (sleitnick's RbxUtil). Wally installs both.

Installation and guide: Hitbox package page.

Types​

ConnectionLike​

interface ConnectionLike {
Connected: boolean--

Whether the connection is still active.

Disconnect: (self: ConnectionLike) → ()--

Stops the scan this connection belongs to.

}

The object returned by every Scan* method. It is the connection returned by the signal (or internal Timer) that drives the scan; call Disconnect to stop scanning early. The hitbox's Trove also disconnects it when the hitbox is destroyed.

SignalLike​

interface SignalLike {
Connect: (
self: SignalLike,
callback: (...any) → ...any
) → ConnectionLike--

Connects a callback and returns its connection.

Once: (
self: SignalLike,
callback: (...any) → ...any
) → ConnectionLike--

Connects a callback that runs once.

}

Anything with a Connect method can drive a scan: RBXScriptSignals such as RunService.Heartbeat, or custom signals like sleitnick's Signal. The arguments the signal fires with are ignored; each firing simply triggers one check.

PredicateFunc​

type PredicateFunc = (part: BasePart) → boolean

Filter applied to each overlapping part. Return true to keep the part. The predicate is called inside pcall, so a part is also dropped if the predicate errors.

DisconnectPredicateFunc​

type DisconnectPredicateFunc = () → boolean

Called before every check of an event-driven scan. Return true to keep scanning; returning false disconnects the scan instead of performing that check. Scan and ScanHumanoids use one of these internally to stop after their duration.

BasePartCallback​

type BasePartCallback = (part: BasePart) → ()

Receives one overlapping part. Called in its own thread (task.spawn) for every part found on every check, so a part that stays inside the hitbox is reported once per check.

HumanoidCallback​

type HumanoidCallback = (humanoid: Humanoid) → ()

Receives one living humanoid. Called in its own thread (task.spawn) for every humanoid found on every check, so the same humanoid is reported repeatedly while it stays inside the hitbox.

Properties​

Trove​

This item is read only and cannot be modified. Read Only
Hitbox.Trove: Trove

The Trove that owns everything the hitbox creates: the hitbox part, the weld, scan timers and scan connections. Destroy destroys it. You can add your own objects to it so they are cleaned up with the hitbox.

HitboxPart​

This item is read only and cannot be modified. Read Only
Hitbox.HitboxPart: Part

The physical part used for Workspace:GetPartsInPart. It is anchored (until welded), has CanCollide, CastShadow off and Massless on. Move it with CFrame/PivotTo if you want to reposition an unwelded hitbox. The Trove is attached to this instance, so destroying the part destroys the hitbox.

HitboxParams​

Hitbox.HitboxParams: OverlapParams

The OverlapParams passed to every GetPartsInPart call. Change its filter list at any time to include or exclude instances from future checks.

Weld​

This item is read only and cannot be modified. Read Only
Hitbox.Weld: WeldConstraint?

The WeldConstraint created by WeldTo, or nil if the hitbox is not welded to anything. It is parented to the part the hitbox was welded to.

Functions​

new​

Constructor
Hitbox.new(
cframe: CFrame,--

World position and rotation of the hitbox part.

size: Vector3,--

Size of the hitbox part in studs.

shape: Enum.PartType,--

Shape of the hitbox part (Block, Ball, Cylinder, ...).

hitboxParams: OverlapParams,--

Filter used for every GetPartsInPart query; stored as HitboxParams.

parent: Instance?,--

Where to parent the hitbox part; defaults to game.

visible: boolean?,--

Show the part as red and half transparent instead of invisible.

weldTo: BasePart?--

If given, the hitbox part is welded to this part right away.

) → Hitbox--

The new hitbox.

Creates a hitbox by building a new part from a position, size and shape.

The part is anchored, non-colliding, massless and casts no shadow. When visible is true it is drawn as a red, half-transparent part (handy while tuning a hitbox); otherwise it is fully invisible. When weldTo is given, WeldTo is called immediately so the hitbox part follows that part and keeps the offset described by cframe.

CAUTION

parent defaults to game (the DataModel) when omitted. Pass workspace or a folder inside it so the part is actually in the world.

Errors

TypeDescription
"CFrame must be a CFrame"`cframe` is not a CFrame.
"Size must be a Vector3"`size` is not a Vector3.
"Shape must be an Enum.PartType item"`shape` is not an EnumItem.
"HitboxParams must be an OverlapParams"`hitboxParams` is not an OverlapParams.

fromPart​

Constructor
Hitbox.fromPart(
part: BasePart,--

Part whose position, size and shape are copied.

hitboxParams: OverlapParams,--

Filter used for every GetPartsInPart query.

parent: Instance?,--

Where to parent the hitbox part; defaults to game.

visible: boolean?,--

Show the part as red and half transparent instead of invisible.

weldTo: BasePart?--

If given, the hitbox part is welded to this part right away.

) → Hitbox--

The new hitbox.

Creates a hitbox that copies the CFrame, Size and shape of an existing part. The shape is only copied when part is a Part; other BaseParts (meshes, unions, wedges) produce a Block. The original part is left untouched; use convert if you want it removed.

The parent, visible and weldTo arguments behave exactly as in new.

fromModel​

Constructor
Hitbox.fromModel(
model: Model,--

Model whose bounding box defines the hitbox.

hitboxParams: OverlapParams,--

Filter used for every GetPartsInPart query.

parent: Instance?,--

Where to parent the hitbox part; defaults to game.

visible: boolean?,--

Show the part as red and half transparent instead of invisible.

weldTo: BasePart?--

If given, the hitbox part is welded to this part right away.

) → Hitbox--

The new hitbox.

Creates a Block hitbox that covers a model's bounding box, using Model:GetBoundingBox() for the CFrame and Model:GetExtentsSize() for the size. The model itself is left untouched; use convert if you want it removed.

The parent, visible and weldTo arguments behave exactly as in new.

convert​

Constructor
Hitbox.convert(
instanceToConvert: Model | BasePart,--

The part or model to turn into a hitbox; it is destroyed afterwards.

hitboxParams: OverlapParams,--

Filter used for every GetPartsInPart query.

parent: Instance?,--

Where to parent the hitbox part; defaults to game.

visible: boolean?,--

Show the part as red and half transparent instead of invisible.

weldTo: BasePart?--

If given, the hitbox part is welded to this part right away.

) → Hitbox--

The new hitbox.

Replaces a placeholder part or model with a hitbox. Behaves like fromPart for a BasePart and fromModel for a Model, then destroys the original instance. Useful when you lay out hitbox volumes in Studio and want them swapped for real hitboxes at runtime.

The parent, visible and weldTo arguments behave exactly as in new.

Errors

TypeDescription
"Unable to convert class ..."`instanceToConvert` is neither a `BasePart` nor a `Model`.

Check​

Hitbox:Check(
predicate: PredicateFunc?--

Optional filter; return true to keep a part.

) → {BasePart}--

The parts currently inside the hitbox that passed the filter.

Performs one Workspace:GetPartsInPart(HitboxPart, HitboxParams) query and returns the overlapping parts. When a predicate is given, only parts it returns true for are kept; the predicate is wrapped in pcall, so a part is also dropped if the predicate errors.

This is the raw building block. Use CheckHumanoids when you care about characters rather than individual parts, and the Scan* methods to run checks repeatedly.

local parts = hitbox:Check(function(part)
	return part:HasTag("Breakable")
end)

CheckHumanoids​

Hitbox:CheckHumanoids() → {Humanoid}--

Living humanoids whose character parts are inside the hitbox.

Performs one check and resolves the overlapping parts to living humanoids. For each part found by Check, the nearest Model ancestor is looked up and the first Humanoid descendant of that model with Health > 0 is collected. Dead humanoids and parts that are not inside a model with a humanoid are ignored.

CAUTION

The list is not de-duplicated: a character with several parts inside the hitbox appears once per part. Track humanoids you have already handled if you need one hit per character.

ScanWithEvent​

Hitbox:ScanWithEvent(
interval: number | SignalLike,--

Seconds between checks, or a signal whose firings trigger checks.

callback: BasePartCallback,--

Called with each part found on each check.

predicate: PredicateFunc?,--

Optional filter passed to Check.

disconnectPredicate: DisconnectPredicateFunc?--

Return false to stop the scan before the next check.

) → ConnectionLike--

Connection driving the scan; Disconnect it to stop early.

Repeatedly runs Check(predicate) and calls callback for every part found, driven either by a fixed interval or by a signal:

  • If interval is a number, a Timer is constructed inside the hitbox's Trove with that many seconds between ticks, started, and its Tick signal drives the checks.
  • If interval is a signal (RunService.Heartbeat, a custom Signal, ...), one check runs every time the signal fires. The signal's arguments are ignored.

Before each check disconnectPredicate is called (if given); when it returns false the scan disconnects itself instead of checking. Scan uses this to stop after a duration. Without a disconnect predicate the scan runs until you call Disconnect on the returned connection or destroy the hitbox.

Each callback is spawned in its own thread, and a part that stays inside the hitbox is reported on every check, so de-duplicate in the callback if you only want to react once per part.

-- Scan every 0.1s while the ability is active
local connection = hitbox:ScanWithEvent(0.1, function(part)
	print("Touching", part:GetFullName())
end, nil, function()
	return ability.Active
end)

Scan​

Hitbox:Scan(
duration: number,--

How many seconds to keep scanning.

callback: BasePartCallback,--

Called with each part found on each frame.

predicate: PredicateFunc?--

Optional filter passed to Check.

) → ConnectionLike--

Connection driving the scan; Disconnect it to stop early.

Scans for parts on every RunService.Heartbeat for duration seconds, then stops on its own. This is ScanWithEvent(RunService.Heartbeat, callback, predicate, ...) with a disconnect predicate that compares os.clock() against the start time. The callback runs for every part on every frame it is inside the hitbox.

Returns immediately; the scan happens in the background. Use ScanWithEvent if you need a slower interval or a different signal.

ScanHumanoidsWithEvent​

Hitbox:ScanHumanoidsWithEvent(
interval: number | SignalLike,--

Seconds between checks, or a signal whose firings trigger checks.

callback: HumanoidCallback,--

Called with each living humanoid found on each check.

disconnectPredicate: DisconnectPredicateFunc?--

Return false to stop the scan before the next check.

) → ConnectionLike--

Connection driving the scan; Disconnect it to stop early.

Humanoid version of ScanWithEvent: repeatedly runs CheckHumanoids and calls callback for every living humanoid found. interval is either a number of seconds (an internal Timer is created and started in the Trove) or a signal that triggers a check each time it fires. disconnectPredicate is called before each check and stops the scan when it returns false.

Each callback is spawned in its own thread. Because CheckHumanoids does not de-duplicate, the same humanoid can be reported several times per check and again on every following check while it stays inside the hitbox.

ScanHumanoids​

Hitbox:ScanHumanoids(
duration: number,--

How many seconds to keep scanning.

callback: HumanoidCallback--

Called with each living humanoid found on each frame.

) → ConnectionLike--

Connection driving the scan; Disconnect it to stop early.

Scans for living humanoids on every RunService.Heartbeat for duration seconds, then stops on its own. Equivalent to ScanHumanoidsWithEvent(RunService.Heartbeat, callback, ...) with a time-based disconnect predicate. Returns immediately; the scan runs in the background.

local hit = {}
hitbox:ScanHumanoids(0.4, function(humanoid)
	if not hit[humanoid] then
		hit[humanoid] = true
		humanoid:TakeDamage(25)
	end
end)

WeldTo​

Hitbox:WeldTo(
part: BasePart--

The part the hitbox should follow (becomes Part0 of the weld).

) → ()

Attaches the hitbox part to part with a WeldConstraint so it moves with it, keeping the current relative offset between the two. The hitbox part is unanchored, and the constraint is parented to part (named after the hitbox part) and stored in Weld. Any previous weld is destroyed first, so calling this again re-parents the hitbox to a different part.

The weld is added to the Trove and removed when the hitbox is destroyed. Position the hitbox part where you want it relative to part before welding.

Destroy​

Hitbox:Destroy() → ()

Destroys the hitbox: the Trove is destroyed, which removes the hitbox part and weld, stops any running scan timers and disconnects every scan connection. Always call this (or destroy HitboxPart, which has the same effect) when you are done, otherwise the part and its scans keep running.

Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "Creates a hitbox by building a new part from a position, size and shape.\n\nThe part is anchored, non-colliding, massless and casts no shadow. When `visible` is true it is\ndrawn as a red, half-transparent part (handy while tuning a hitbox); otherwise it is fully\ninvisible. When `weldTo` is given, `WeldTo` is called immediately so the hitbox part follows\nthat part and keeps the offset described by `cframe`.\n\n:::caution\n`parent` defaults to `game` (the DataModel) when omitted. Pass `workspace` or a folder inside\nit so the part is actually in the world.\n:::",
            "params": [
                {
                    "name": "cframe",
                    "desc": "World position and rotation of the hitbox part.",
                    "lua_type": "CFrame"
                },
                {
                    "name": "size",
                    "desc": "Size of the hitbox part in studs.",
                    "lua_type": "Vector3"
                },
                {
                    "name": "shape",
                    "desc": "Shape of the hitbox part (`Block`, `Ball`, `Cylinder`, ...).",
                    "lua_type": "Enum.PartType"
                },
                {
                    "name": "hitboxParams",
                    "desc": "Filter used for every `GetPartsInPart` query; stored as `HitboxParams`.",
                    "lua_type": "OverlapParams"
                },
                {
                    "name": "parent",
                    "desc": "Where to parent the hitbox part; defaults to `game`.",
                    "lua_type": "Instance?"
                },
                {
                    "name": "visible",
                    "desc": "Show the part as red and half transparent instead of invisible.",
                    "lua_type": "boolean?"
                },
                {
                    "name": "weldTo",
                    "desc": "If given, the hitbox part is welded to this part right away.",
                    "lua_type": "BasePart?"
                }
            ],
            "returns": [
                {
                    "desc": "The new hitbox.",
                    "lua_type": "Hitbox"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"CFrame must be a CFrame\"",
                    "desc": "`cframe` is not a CFrame."
                },
                {
                    "lua_type": "\"Size must be a Vector3\"",
                    "desc": "`size` is not a Vector3."
                },
                {
                    "lua_type": "\"Shape must be an Enum.PartType item\"",
                    "desc": "`shape` is not an EnumItem."
                },
                {
                    "lua_type": "\"HitboxParams must be an OverlapParams\"",
                    "desc": "`hitboxParams` is not an OverlapParams."
                }
            ],
            "source": {
                "line": 291,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "fromPart",
            "desc": "Creates a hitbox that copies the `CFrame`, `Size` and shape of an existing part. The shape is\nonly copied when `part` is a `Part`; other `BasePart`s (meshes, unions, wedges) produce a\n`Block`. The original part is left untouched; use `convert` if you want it removed.\n\nThe `parent`, `visible` and `weldTo` arguments behave exactly as in `new`.",
            "params": [
                {
                    "name": "part",
                    "desc": "Part whose position, size and shape are copied.",
                    "lua_type": "BasePart"
                },
                {
                    "name": "hitboxParams",
                    "desc": "Filter used for every `GetPartsInPart` query.",
                    "lua_type": "OverlapParams"
                },
                {
                    "name": "parent",
                    "desc": "Where to parent the hitbox part; defaults to `game`.",
                    "lua_type": "Instance?"
                },
                {
                    "name": "visible",
                    "desc": "Show the part as red and half transparent instead of invisible.",
                    "lua_type": "boolean?"
                },
                {
                    "name": "weldTo",
                    "desc": "If given, the hitbox part is welded to this part right away.",
                    "lua_type": "BasePart?"
                }
            ],
            "returns": [
                {
                    "desc": "The new hitbox.",
                    "lua_type": "Hitbox"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 342,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "fromModel",
            "desc": "Creates a `Block` hitbox that covers a model's bounding box, using `Model:GetBoundingBox()`\nfor the CFrame and `Model:GetExtentsSize()` for the size. The model itself is left untouched;\nuse `convert` if you want it removed.\n\nThe `parent`, `visible` and `weldTo` arguments behave exactly as in `new`.",
            "params": [
                {
                    "name": "model",
                    "desc": "Model whose bounding box defines the hitbox.",
                    "lua_type": "Model"
                },
                {
                    "name": "hitboxParams",
                    "desc": "Filter used for every `GetPartsInPart` query.",
                    "lua_type": "OverlapParams"
                },
                {
                    "name": "parent",
                    "desc": "Where to parent the hitbox part; defaults to `game`.",
                    "lua_type": "Instance?"
                },
                {
                    "name": "visible",
                    "desc": "Show the part as red and half transparent instead of invisible.",
                    "lua_type": "boolean?"
                },
                {
                    "name": "weldTo",
                    "desc": "If given, the hitbox part is welded to this part right away.",
                    "lua_type": "BasePart?"
                }
            ],
            "returns": [
                {
                    "desc": "The new hitbox.",
                    "lua_type": "Hitbox"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "source": {
                "line": 364,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "convert",
            "desc": "Replaces a placeholder part or model with a hitbox. Behaves like `fromPart` for a `BasePart`\nand `fromModel` for a `Model`, then **destroys the original instance**. Useful when you lay\nout hitbox volumes in Studio and want them swapped for real hitboxes at runtime.\n\nThe `parent`, `visible` and `weldTo` arguments behave exactly as in `new`.",
            "params": [
                {
                    "name": "instanceToConvert",
                    "desc": "The part or model to turn into a hitbox; it is destroyed afterwards.",
                    "lua_type": "Model | BasePart"
                },
                {
                    "name": "hitboxParams",
                    "desc": "Filter used for every `GetPartsInPart` query.",
                    "lua_type": "OverlapParams"
                },
                {
                    "name": "parent",
                    "desc": "Where to parent the hitbox part; defaults to `game`.",
                    "lua_type": "Instance?"
                },
                {
                    "name": "visible",
                    "desc": "Show the part as red and half transparent instead of invisible.",
                    "lua_type": "boolean?"
                },
                {
                    "name": "weldTo",
                    "desc": "If given, the hitbox part is welded to this part right away.",
                    "lua_type": "BasePart?"
                }
            ],
            "returns": [
                {
                    "desc": "The new hitbox.",
                    "lua_type": "Hitbox"
                }
            ],
            "function_type": "static",
            "tags": [
                "Constructor"
            ],
            "errors": [
                {
                    "lua_type": "\"Unable to convert class ...\"",
                    "desc": "`instanceToConvert` is neither a `BasePart` nor a `Model`."
                }
            ],
            "source": {
                "line": 389,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "Check",
            "desc": "Performs one `Workspace:GetPartsInPart(HitboxPart, HitboxParams)` query and returns the\noverlapping parts. When a `predicate` is given, only parts it returns `true` for are kept; the\npredicate is wrapped in `pcall`, so a part is also dropped if the predicate errors.\n\nThis is the raw building block. Use `CheckHumanoids` when you care about characters rather\nthan individual parts, and the `Scan*` methods to run checks repeatedly.\n\n```lua\nlocal parts = hitbox:Check(function(part)\n\treturn part:HasTag(\"Breakable\")\nend)\n```",
            "params": [
                {
                    "name": "predicate",
                    "desc": "Optional filter; return `true` to keep a part.",
                    "lua_type": "PredicateFunc?"
                }
            ],
            "returns": [
                {
                    "desc": "The parts currently inside the hitbox that passed the filter.",
                    "lua_type": "{ BasePart }"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 424,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "CheckHumanoids",
            "desc": "Performs one check and resolves the overlapping parts to living humanoids. For each part found\nby `Check`, the nearest `Model` ancestor is looked up and the first `Humanoid` descendant of\nthat model with `Health > 0` is collected. Dead humanoids and parts that are not inside a model\nwith a humanoid are ignored.\n\n:::caution\nThe list is not de-duplicated: a character with several parts inside the hitbox appears once\nper part. Track humanoids you have already handled if you need one hit per character.\n:::",
            "params": [],
            "returns": [
                {
                    "desc": "Living humanoids whose character parts are inside the hitbox.",
                    "lua_type": "{ Humanoid }"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 454,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "ScanWithEvent",
            "desc": "Repeatedly runs `Check(predicate)` and calls `callback` for every part found, driven either by\na fixed interval or by a signal:\n\n- If `interval` is a **number**, a [Timer](https://sleitnick.github.io/RbxUtil/api/Timer/) is\n  constructed inside the hitbox's Trove with that many seconds between ticks, started, and its\n  `Tick` signal drives the checks.\n- If `interval` is a **signal** (`RunService.Heartbeat`, a custom Signal, ...), one check runs\n  every time the signal fires. The signal's arguments are ignored.\n\nBefore each check `disconnectPredicate` is called (if given); when it returns `false` the scan\ndisconnects itself instead of checking. `Scan` uses this to stop after a duration. Without a\ndisconnect predicate the scan runs until you call `Disconnect` on the returned connection or\ndestroy the hitbox.\n\nEach callback is spawned in its own thread, and a part that stays inside the hitbox is reported\non every check, so de-duplicate in the callback if you only want to react once per part.\n\n```lua\n-- Scan every 0.1s while the ability is active\nlocal connection = hitbox:ScanWithEvent(0.1, function(part)\n\tprint(\"Touching\", part:GetFullName())\nend, nil, function()\n\treturn ability.Active\nend)\n```",
            "params": [
                {
                    "name": "interval",
                    "desc": "Seconds between checks, or a signal whose firings trigger checks.",
                    "lua_type": "number | SignalLike"
                },
                {
                    "name": "callback",
                    "desc": "Called with each part found on each check.",
                    "lua_type": "BasePartCallback"
                },
                {
                    "name": "predicate",
                    "desc": "Optional filter passed to `Check`.",
                    "lua_type": "PredicateFunc?"
                },
                {
                    "name": "disconnectPredicate",
                    "desc": "Return `false` to stop the scan before the next check.",
                    "lua_type": "DisconnectPredicateFunc?"
                }
            ],
            "returns": [
                {
                    "desc": "Connection driving the scan; `Disconnect` it to stop early.",
                    "lua_type": "ConnectionLike"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 506,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "Scan",
            "desc": "Scans for parts on every `RunService.Heartbeat` for `duration` seconds, then stops on its own.\nThis is `ScanWithEvent(RunService.Heartbeat, callback, predicate, ...)` with a disconnect\npredicate that compares `os.clock()` against the start time. The callback runs for every part\non every frame it is inside the hitbox.\n\nReturns immediately; the scan happens in the background. Use `ScanWithEvent` if you need a\nslower interval or a different signal.",
            "params": [
                {
                    "name": "duration",
                    "desc": "How many seconds to keep scanning.",
                    "lua_type": "number"
                },
                {
                    "name": "callback",
                    "desc": "Called with each part found on each frame.",
                    "lua_type": "BasePartCallback"
                },
                {
                    "name": "predicate",
                    "desc": "Optional filter passed to `Check`.",
                    "lua_type": "PredicateFunc?"
                }
            ],
            "returns": [
                {
                    "desc": "Connection driving the scan; `Disconnect` it to stop early.",
                    "lua_type": "ConnectionLike"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 546,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "ScanHumanoidsWithEvent",
            "desc": "Humanoid version of `ScanWithEvent`: repeatedly runs `CheckHumanoids` and calls `callback` for\nevery living humanoid found. `interval` is either a number of seconds (an internal Timer is\ncreated and started in the Trove) or a signal that triggers a check each time it fires.\n`disconnectPredicate` is called before each check and stops the scan when it returns `false`.\n\nEach callback is spawned in its own thread. Because `CheckHumanoids` does not de-duplicate,\nthe same humanoid can be reported several times per check and again on every following check\nwhile it stays inside the hitbox.",
            "params": [
                {
                    "name": "interval",
                    "desc": "Seconds between checks, or a signal whose firings trigger checks.",
                    "lua_type": "number | SignalLike"
                },
                {
                    "name": "callback",
                    "desc": "Called with each living humanoid found on each check.",
                    "lua_type": "HumanoidCallback"
                },
                {
                    "name": "disconnectPredicate",
                    "desc": "Return `false` to stop the scan before the next check.",
                    "lua_type": "DisconnectPredicateFunc?"
                }
            ],
            "returns": [
                {
                    "desc": "Connection driving the scan; `Disconnect` it to stop early.",
                    "lua_type": "ConnectionLike"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 568,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "ScanHumanoids",
            "desc": "Scans for living humanoids on every `RunService.Heartbeat` for `duration` seconds, then stops\non its own. Equivalent to `ScanHumanoidsWithEvent(RunService.Heartbeat, callback, ...)` with a\ntime-based disconnect predicate. Returns immediately; the scan runs in the background.\n\n```lua\nlocal hit = {}\nhitbox:ScanHumanoids(0.4, function(humanoid)\n\tif not hit[humanoid] then\n\t\thit[humanoid] = true\n\t\thumanoid:TakeDamage(25)\n\tend\nend)\n```",
            "params": [
                {
                    "name": "duration",
                    "desc": "How many seconds to keep scanning.",
                    "lua_type": "number"
                },
                {
                    "name": "callback",
                    "desc": "Called with each living humanoid found on each frame.",
                    "lua_type": "HumanoidCallback"
                }
            ],
            "returns": [
                {
                    "desc": "Connection driving the scan; `Disconnect` it to stop early.",
                    "lua_type": "ConnectionLike"
                }
            ],
            "function_type": "method",
            "source": {
                "line": 613,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "WeldTo",
            "desc": "Attaches the hitbox part to `part` with a `WeldConstraint` so it moves with it, keeping the\ncurrent relative offset between the two. The hitbox part is unanchored, and the constraint is\nparented to `part` (named after the hitbox part) and stored in `Weld`. Any previous weld is\ndestroyed first, so calling this again re-parents the hitbox to a different part.\n\nThe weld is added to the Trove and removed when the hitbox is destroyed. Position the hitbox\npart where you want it relative to `part` **before** welding.",
            "params": [
                {
                    "name": "part",
                    "desc": "The part the hitbox should follow (becomes `Part0` of the weld).",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 631,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "Destroy",
            "desc": "Destroys the hitbox: the Trove is destroyed, which removes the hitbox part and weld, stops any\nrunning scan timers and disconnects every scan connection. Always call this (or destroy\n`HitboxPart`, which has the same effect) when you are done, otherwise the part and its scans\nkeep running.",
            "params": [],
            "returns": [],
            "function_type": "method",
            "source": {
                "line": 652,
                "path": "packages/src/Hitbox/init.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "Trove",
            "desc": "The [Trove](https://sleitnick.github.io/RbxUtil/api/Trove/) that owns everything the hitbox\ncreates: the hitbox part, the weld, scan timers and scan connections. `Destroy` destroys it.\nYou can add your own objects to it so they are cleaned up with the hitbox.",
            "lua_type": "Trove",
            "readonly": true,
            "source": {
                "line": 85,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "HitboxPart",
            "desc": "The physical part used for `Workspace:GetPartsInPart`. It is anchored (until welded), has\n`CanCollide`, `CastShadow` off and `Massless` on. Move it with `CFrame`/`PivotTo` if you want to\nreposition an unwelded hitbox. The Trove is attached to this instance, so destroying the part\ndestroys the hitbox.",
            "lua_type": "Part",
            "readonly": true,
            "source": {
                "line": 95,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "HitboxParams",
            "desc": "The `OverlapParams` passed to every `GetPartsInPart` call. Change its filter list at any time\nto include or exclude instances from future checks.",
            "lua_type": "OverlapParams",
            "source": {
                "line": 102,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "Weld",
            "desc": "The `WeldConstraint` created by `WeldTo`, or `nil` if the hitbox is not welded to anything.\nIt is parented to the part the hitbox was welded to.",
            "lua_type": "WeldConstraint?",
            "readonly": true,
            "source": {
                "line": 110,
                "path": "packages/src/Hitbox/init.luau"
            }
        }
    ],
    "types": [
        {
            "name": "ConnectionLike",
            "desc": "The object returned by every `Scan*` method. It is the connection returned by the signal (or\ninternal Timer) that drives the scan; call `Disconnect` to stop scanning early. The hitbox's\nTrove also disconnects it when the hitbox is destroyed.",
            "fields": [
                {
                    "name": "Connected",
                    "lua_type": "boolean",
                    "desc": "Whether the connection is still active."
                },
                {
                    "name": "Disconnect",
                    "lua_type": "(self: ConnectionLike) -> ()",
                    "desc": "Stops the scan this connection belongs to."
                }
            ],
            "source": {
                "line": 132,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "SignalLike",
            "desc": "Anything with a `Connect` method can drive a scan: `RBXScriptSignal`s such as\n`RunService.Heartbeat`, or custom signals like sleitnick's Signal. The arguments the signal\nfires with are ignored; each firing simply triggers one check.",
            "fields": [
                {
                    "name": "Connect",
                    "lua_type": "(self: SignalLike, callback: (...any) -> ...any) -> ConnectionLike",
                    "desc": "Connects a callback and returns its connection."
                },
                {
                    "name": "Once",
                    "lua_type": "(self: SignalLike, callback: (...any) -> ...any) -> ConnectionLike",
                    "desc": "Connects a callback that runs once."
                }
            ],
            "source": {
                "line": 147,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "PredicateFunc",
            "desc": "Filter applied to each overlapping part. Return `true` to keep the part. The predicate is\ncalled inside `pcall`, so a part is also dropped if the predicate errors.",
            "lua_type": "(part: BasePart) -> boolean",
            "source": {
                "line": 159,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "DisconnectPredicateFunc",
            "desc": "Called before every check of an event-driven scan. Return `true` to keep scanning; returning\n`false` disconnects the scan instead of performing that check. `Scan` and `ScanHumanoids` use\none of these internally to stop after their duration.",
            "lua_type": "() -> boolean",
            "source": {
                "line": 168,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "BasePartCallback",
            "desc": "Receives one overlapping part. Called in its own thread (`task.spawn`) for every part found on\nevery check, so a part that stays inside the hitbox is reported once per check.",
            "lua_type": "(part: BasePart) -> ()",
            "source": {
                "line": 177,
                "path": "packages/src/Hitbox/init.luau"
            }
        },
        {
            "name": "HumanoidCallback",
            "desc": "Receives one living humanoid. Called in its own thread (`task.spawn`) for every humanoid found\non every check, so the same humanoid is reported repeatedly while it stays inside the hitbox.",
            "lua_type": "(humanoid: Humanoid) -> ()",
            "source": {
                "line": 185,
                "path": "packages/src/Hitbox/init.luau"
            }
        }
    ],
    "name": "Hitbox",
    "desc": "Hitbox wraps an invisible, non-colliding `Part` and uses `Workspace:GetPartsInPart` to find\nwhat is overlapping it. Use it for melee swings, area-of-effect abilities, trigger zones or\nanything else where you want to know which parts or humanoids are inside a volume right now,\nor for a period of time.\n\nA Hitbox can be queried once (`Check`, `CheckHumanoids`), scanned for a fixed duration on\n`Heartbeat` (`Scan`, `ScanHumanoids`) or scanned on your own schedule using a repeat interval\nor any signal (`ScanWithEvent`, `ScanHumanoidsWithEvent`). The hitbox part can be welded to\nanother part so it follows a character or weapon. Everything the hitbox creates (the part,\nwelds, timers and connections) is tracked by a [Trove](https://sleitnick.github.io/RbxUtil/api/Trove/)\nand released by `Destroy`; destroying the hitbox part also destroys the hitbox.\n\nCreate one with `Hitbox.new`, or from existing geometry with `Hitbox.fromPart`,\n`Hitbox.fromModel` or `Hitbox.convert`.\n\n```lua\nlocal Hitbox = require(path.to.Hitbox)\n\nlocal function swing(character: Model)\n\tlocal rootPart = character:WaitForChild(\"HumanoidRootPart\") :: BasePart\n\n\tlocal params = OverlapParams.new()\n\tparams.FilterType = Enum.RaycastFilterType.Exclude\n\tparams.FilterDescendantsInstances = { character }\n\n\t-- A 4x4x4 block one stud in front of the character, welded to the root so it follows them\n\tlocal hitbox = Hitbox.new(\n\t\trootPart.CFrame * CFrame.new(0, 0, -3),\n\t\tVector3.new(4, 4, 4),\n\t\tEnum.PartType.Block,\n\t\tparams,\n\t\tworkspace,\n\t\tfalse,\n\t\trootPart\n\t)\n\n\t-- Scan every frame for half a second and damage each humanoid once\n\tlocal alreadyHit: { [Humanoid]: boolean } = {}\n\thitbox:ScanHumanoids(0.5, function(humanoid)\n\t\tif alreadyHit[humanoid] then\n\t\t\treturn\n\t\tend\n\t\talreadyHit[humanoid] = true\n\t\thumanoid:TakeDamage(10)\n\tend)\n\n\ttask.delay(0.5, function()\n\t\thitbox:Destroy()\n\tend)\nend\n```\n\n**Credits:** [Trove](https://sleitnick.github.io/RbxUtil/api/Trove/) and [Timer](https://sleitnick.github.io/RbxUtil/api/Timer/) are by [sleitnick](https://github.com/Sleitnick) ([sleitnick's RbxUtil](https://sleitnick.github.io/RbxUtil/)). Wally installs both.\n\nInstallation and guide: [Hitbox package page](/docs/packages/hitbox).",
    "source": {
        "line": 74,
        "path": "packages/src/Hitbox/init.luau"
    }
}