Skip to main content

Perception

The helpers the built-in senses are made of, and the place to start when writing your own. A Types.Sense is any table with Tick; the work inside one is usually "who could I be looking at" (AliveRoots), "is it in front of me" (InCone), "is anything in the way" (LineOfSight), "which is nearest" (SortByDistance), and this module answers each of those on its own.

Everything here is about targets: Perception.HumanoidOf takes a root a sense wrote to the blackboard and finds that character's humanoid. The agent's own humanoid is agent.Entity:FindFirstChildOfClass("Humanoid"), and the two look alike in code, so name the variable.

Targets on every built-in sense defaults to Perception.AliveRoots, and Ignore on Sight defaults to Perception.IgnoreList. A brain that acts on a target root a sense wrote to the blackboard reaches the rest of the character through Perception.CharacterOf and Perception.HumanoidOf.

Everything that takes a part reads it duck-typed, so a spec can pass plain tables.

Properties​

HIDDEN_ATTRIBUTE​

Perception.HIDDEN_ATTRIBUTE: "VLUXYAI_HIDDEN"

The attribute name that hides a character from every sense: set it to true on the character (or on its root) while the player is in a locker, under a bed, or otherwise out of the game's sight rules, and Perception.AliveRoots leaves them out until it is cleared.

LIGHT_TAG​

Perception.LIGHT_TAG: "VLUXYAI_LIGHT"

The CollectionService tag a light must carry for Perception.LightAt to count it.

Functions​

IsHidden​

Perception.IsHidden(
root: BasePart--

A target root, or anything with GetAttribute.

) → boolean--

true when the root or its character carries VLUXYAI_HIDDEN = true.

AliveRoots​

Perception.AliveRoots() → {BasePart}--

The HumanoidRootPart of every player whose character is alive and not hidden.

Every player character with a Humanoid whose Health is above zero, by its root, minus the ones hidden with Perception.HIDDEN_ATTRIBUTE and the ones standing in an enabled SafeAreas area. The default Targets of every sense, so hiding a player once hides them from Sight, Proximity and Watched together. Takes no arguments, so it can be a Targets function as it is.

AllAliveRoots​

Perception.AllAliveRoots() → {BasePart}--

Every alive player root, hidden ones included.

For the checks a hidden player still takes part in: Perception.SeenByAnyone uses it, since a player inside a locker can still see out of it.

IgnoreList​

Perception.IgnoreList(agent: Agent) → {Instance}--

{ agent.Entity }, or {} for a headless agent.

The raycast filter for a ray cast from the agent outwards: its entity alone, which FilterDescendantsInstances extends to everything under it. The default Ignore of Sight.

PositionOf​

Perception.PositionOf(agent: Agent) → Vector3

Where the agent is: its mover's position when it has one, else its entity's pivot. The real Agent has GetPosition; a spec's fake may not.

PivotOf​

Perception.PivotOf(agent: Agent) → CFrame

Where the agent is and which way it faces: Agent:GetPivot, which works with or without a Model. A spec's fake without GetPivot falls back to its entity's pivot.

ModelOf​

Perception.ModelOf(part: BasePart) → Instance?--

The nearest ancestor Model, or nil.

The Model a part belongs to, or nil when it has none or is a fake without FindFirstAncestorOfClass. Perception.CharacterOf is the same lookup typed as a Model.

CharacterOf​

Perception.CharacterOf(
part: BasePart--

A root a sense wrote to the blackboard, or any part of a character.

) → Model?--

The nearest ancestor Model, or nil when there is none.

The character a part belongs to. What a brain calls on SeenTarget or NearestTarget when it wants the whole model rather than the root.

HumanoidOf​

Perception.HumanoidOf(
part: BasePart--

A root a sense wrote to the blackboard, or any part of a character.

) → Humanoid?--

The Humanoid inside the part's character, or nil when there is no character or no Humanoid.

The Humanoid a brain damages, checks the health of, or reads the state of, found through Perception.CharacterOf.

EyeOf​

Perception.EyeOf(root: BasePart) → BasePart--

The character's Head when it has one, else root itself.

The part to look out of for a character. Watched judges an observer's look direction by this part's CFrame.

RaycastParams​

Perception.RaycastParams(
ignore: {Instance}--

Passed through, with their descendants.

) → RaycastParams

Raycast parameters that exclude ignore (and their descendants) and water.

Transparent​

Perception.Transparent(
threshold: number?--

Transparency at or above which a part is see-through. Default 0.5.

) → (part: BasePart) → boolean--

A SeeThrough predicate for Perception.LineOfSight and Sight.

Glass and fences: parts this transparent do not block sight.

LineOfSight​

Perception.LineOfSight(
from: Vector3,
to: Vector3,
ignore: {Instance},--

What the ray passes through.

allow: Instance?,--

What the ray may hit and still count as clear, usually the target's own model.

seeThrough: ((part: BasePart) → boolean)?--

Parts this returns true for do not block; the ray is cast again past them, up to eight times.

) → boolean

Is the straight line from from to to clear? true when the ray hits nothing, or hits allow or something under it, which is how a target's own body does not block sight of it. Without seeThrough, anything else the ray hits blocks, glass included; pass Perception.Transparent or your own rule to look past it.

CountWalls​

Perception.CountWalls(
from: Vector3,
to: Vector3,
ignore: {Instance},--

What does not count, with descendants: the listener's entity, the source's part.

limit: number?,--

Stop counting here. Default 3.

margin: number?--

Hits within this many studs of to do not count, so a noise inside a body is not walled off by that body. Default 2.

) → number--

How many distinct parts the straight line crosses, up to limit.

How many walls stand between a sound and a listener. Hearing and Sounds multiply the loudness by Occlusion once per wall.

LightFalloff​

Perception.LightFalloff(
distance: number,--

Studs from the light.

range: number,--

The light's Range.

brightness: number--

The light's Brightness.

) → number--

brightness at the light, falling linearly to 0 at range. Pure.

LightContribution​

Perception.LightContribution(
light: Light,--

A PointLight, SpotLight or SurfaceLight on a part or an attachment.

position: Vector3
) → number--

How much this light lights position: 0 when disabled, out of range, not on a part, or outside a spot light's cone.

Read duck-typed (Enabled, Brightness, Range, Parent, and for a spot light Angle and Face), so a spec can pass a plain table.

LightAt​

Perception.LightAt(
position: Vector3,
ambient: number?--

Light everywhere, added before the clamp. 0 is pitch black where no light reaches. Default 0.

) → number--

The light level at position in [0, 1]: ambient plus every tagged light's Perception.LightContribution, clamped.

How lit a spot is, from the lights tagged Perception.LIGHT_TAG. Tag the lights that matter to the AI (a flashlight, the ceiling lamps, a fire) and pass this as a Sight Visibility, and a player in the dark is invisible until a light finds them. Not physically accurate; a gameplay number.

SeenByAnyone​

Perception.SeenByAnyone(
position: Vector3,
options: SeenByOptions?
) → boolean--

true when any observer has position inside their view cone with a clear line to it.

Types

​

interface SeenByOptions {
FieldOfView: number?--

The full angle of each observer's view cone. Default 100.

Range: number?--

Studs. Default math.huge.

Eye: ((root: BasePart) → CFrame?)?--

Where an observer looks from. Default: the character's head, else its root (Perception.EyeOf). A game that replicates the camera returns it here.

Ignore: {Instance}?--

What the sight ray passes through. Default nothing.

Observers: {BasePart}?--

Who may be looking. Default Perception.AllAliveRoots, hidden players included.

}

"Would anyone see it happen here?" The check to run before a stalker teleports, spawns or vanishes, so it only ever does so off screen. The Watched sense is the same question asked about the agent every tick; this is the one-off version about any point.

InCone​

Perception.InCone(
origin: CFrame,--

The eye; its LookVector is the centre of the cone.

position: Vector3,
fieldOfViewDegrees: number,--

The full angle of the cone.

range: number--

Studs.

) → (
boolean,--

Inside the cone and within range.

number--

The dot product between the look vector and the direction to position: 1 dead ahead, 0 beside, -1 behind.

)

Is position inside the cone that opens fieldOfViewDegrees wide around origin.LookVector, within range studs? The angle is the true angle in three dimensions, so something high above is outside just as something off to the side is. A position at the origin counts as inside. The dot is returned either way, so a consideration can score on it.

SortByDistance​

Perception.SortByDistance(
origin: Vector3,
parts: {BasePart}
) → {BasePart}--

A new list, nearest first.

A new list of parts, nearest to origin first. Stable: parts at the same distance keep their order. parts is not touched.

Show raw api
{
    "functions": [
        {
            "name": "IsHidden",
            "desc": "",
            "params": [
                {
                    "name": "root",
                    "desc": "A target root, or anything with `GetAttribute`.",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [
                {
                    "desc": "`true` when the root or its character carries `VLUXYAI_HIDDEN = true`.",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 62,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "AliveRoots",
            "desc": "Every player character with a Humanoid whose Health is above zero, by its root, minus the\nones hidden with [Perception.HIDDEN_ATTRIBUTE] and the ones standing in an enabled\n[SafeAreas] area. The default `Targets` of every sense, so\nhiding a player once hides them from Sight, Proximity and Watched together. Takes no\narguments, so it can be a `Targets` function as it is.",
            "params": [],
            "returns": [
                {
                    "desc": "The `HumanoidRootPart` of every player whose character is alive and not hidden.",
                    "lua_type": "{ BasePart }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 115,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "AllAliveRoots",
            "desc": "For the checks a hidden player still takes part in: [Perception.SeenByAnyone] uses it, since\na player inside a locker can still see out of it.",
            "params": [],
            "returns": [
                {
                    "desc": "Every alive player root, hidden ones included.",
                    "lua_type": "{ BasePart }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 126,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "IgnoreList",
            "desc": "The raycast filter for a ray cast from the agent outwards: its entity alone, which\n`FilterDescendantsInstances` extends to everything under it. The default `Ignore` of [Sight].",
            "params": [
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                }
            ],
            "returns": [
                {
                    "desc": "`{ agent.Entity }`, or `{}` for a headless agent.",
                    "lua_type": "{ Instance }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 138,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "PositionOf",
            "desc": "Where the agent is: its mover's position when it has one, else its entity's pivot. The real\n[Agent] has `GetPosition`; a spec's fake may not.",
            "params": [
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Vector3"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 151,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "PivotOf",
            "desc": "Where the agent is and which way it faces: [Agent:GetPivot], which works with or without a\nModel. A spec's fake without `GetPivot` falls back to its entity's pivot.",
            "params": [
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "CFrame"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 168,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "ModelOf",
            "desc": "The Model a part belongs to, or `nil` when it has none or is a fake without\n`FindFirstAncestorOfClass`. [Perception.CharacterOf] is the same lookup typed as a `Model`.",
            "params": [
                {
                    "name": "part",
                    "desc": "",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [
                {
                    "desc": "The nearest ancestor Model, or `nil`.",
                    "lua_type": "Instance?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 185,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "CharacterOf",
            "desc": "The character a part belongs to. What a brain calls on `SeenTarget` or `NearestTarget` when\nit wants the whole model rather than the root.",
            "params": [
                {
                    "name": "part",
                    "desc": "A root a sense wrote to the blackboard, or any part of a character.",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [
                {
                    "desc": "The nearest ancestor Model, or `nil` when there is none.",
                    "lua_type": "Model?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 202,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "HumanoidOf",
            "desc": "The Humanoid a brain damages, checks the health of, or reads the state of, found through\n[Perception.CharacterOf].",
            "params": [
                {
                    "name": "part",
                    "desc": "A root a sense wrote to the blackboard, or any part of a character.",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [
                {
                    "desc": "The Humanoid inside the part's character, or `nil` when there is no character or no Humanoid.",
                    "lua_type": "Humanoid?"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 214,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "EyeOf",
            "desc": "The part to look out of for a character. [Watched] judges an observer's look direction by\nthis part's `CFrame`.",
            "params": [
                {
                    "name": "root",
                    "desc": "",
                    "lua_type": "BasePart"
                }
            ],
            "returns": [
                {
                    "desc": "The character's `Head` when it has one, else `root` itself.",
                    "lua_type": "BasePart"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 236,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "RaycastParams",
            "desc": "Raycast parameters that exclude `ignore` (and their descendants) and water.",
            "params": [
                {
                    "name": "ignore",
                    "desc": "Passed through, with their descendants.",
                    "lua_type": "{ Instance }"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "RaycastParams"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 259,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "Transparent",
            "desc": "Glass and fences: parts this transparent do not block sight.",
            "params": [
                {
                    "name": "threshold",
                    "desc": "`Transparency` at or above which a part is see-through. Default `0.5`.",
                    "lua_type": "number?"
                }
            ],
            "returns": [
                {
                    "desc": "A `SeeThrough` predicate for [Perception.LineOfSight] and [Sight].",
                    "lua_type": "(part: BasePart) -> boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 288,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "LineOfSight",
            "desc": "Is the straight line from `from` to `to` clear? `true` when the ray hits nothing, or hits\n`allow` or something under it, which is how a target's own body does not block sight of it.\nWithout `seeThrough`, anything else the ray hits blocks, glass included; pass\n[Perception.Transparent] or your own rule to look past it.",
            "params": [
                {
                    "name": "from",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "to",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "ignore",
                    "desc": "What the ray passes through.",
                    "lua_type": "{ Instance }"
                },
                {
                    "name": "allow",
                    "desc": "What the ray may hit and still count as clear, usually the target's own model.",
                    "lua_type": "Instance?"
                },
                {
                    "name": "seeThrough",
                    "desc": "Parts this returns `true` for do not block; the ray is cast again past them, up to eight times.",
                    "lua_type": "((part: BasePart) -> boolean)?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 310,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "CountWalls",
            "desc": "How many walls stand between a sound and a listener. [Hearing] and [Sounds] multiply the\nloudness by `Occlusion` once per wall.",
            "params": [
                {
                    "name": "from",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "to",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "ignore",
                    "desc": "What does not count, with descendants: the listener's entity, the source's part.",
                    "lua_type": "{ Instance }"
                },
                {
                    "name": "limit",
                    "desc": "Stop counting here. Default `3`.",
                    "lua_type": "number?"
                },
                {
                    "name": "margin",
                    "desc": "Hits within this many studs of `to` do not count, so a noise inside a body is not walled off by that body. Default `2`.",
                    "lua_type": "number?"
                }
            ],
            "returns": [
                {
                    "desc": "How many distinct parts the straight line crosses, up to `limit`.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 360,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "LightFalloff",
            "desc": "",
            "params": [
                {
                    "name": "distance",
                    "desc": "Studs from the light.",
                    "lua_type": "number"
                },
                {
                    "name": "range",
                    "desc": "The light's `Range`.",
                    "lua_type": "number"
                },
                {
                    "name": "brightness",
                    "desc": "The light's `Brightness`.",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "`brightness` at the light, falling linearly to `0` at `range`. Pure.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 406,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "LightContribution",
            "desc": "Read duck-typed (`Enabled`, `Brightness`, `Range`, `Parent`, and for a spot light `Angle` and\n`Face`), so a spec can pass a plain table.",
            "params": [
                {
                    "name": "light",
                    "desc": "A `PointLight`, `SpotLight` or `SurfaceLight` on a part or an attachment.",
                    "lua_type": "Light"
                },
                {
                    "name": "position",
                    "desc": "",
                    "lua_type": "Vector3"
                }
            ],
            "returns": [
                {
                    "desc": "How much this light lights `position`: `0` when disabled, out of range, not on a part, or outside a spot light's cone.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 422,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "LightAt",
            "desc": "How lit a spot is, from the lights tagged [Perception.LIGHT_TAG]. Tag the lights that matter\nto the AI (a flashlight, the ceiling lamps, a fire) and pass this as a [Sight] `Visibility`,\nand a player in the dark is invisible until a light finds them. Not physically accurate; a\ngameplay number.",
            "params": [
                {
                    "name": "position",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "ambient",
                    "desc": "Light everywhere, added before the clamp. `0` is pitch black where no light reaches. Default `0`.",
                    "lua_type": "number?"
                }
            ],
            "returns": [
                {
                    "desc": "The light level at `position` in `[0, 1]`: `ambient` plus every tagged light's [Perception.LightContribution], clamped.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 480,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "SeenByAnyone",
            "desc": "\"Would anyone see it happen here?\" The check to run before a stalker teleports, spawns or\nvanishes, so it only ever does so off screen. The [Watched] sense is the same question asked\nabout the agent every tick; this is the one-off version about any point.",
            "params": [
                {
                    "name": "position",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "SeenByOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "`true` when any observer has `position` inside their view cone with a clear line to it.",
                    "lua_type": "boolean"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 522,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "InCone",
            "desc": "Is `position` inside the cone that opens `fieldOfViewDegrees` wide around `origin.LookVector`,\nwithin `range` studs? The angle is the true angle in three dimensions, so something high above\nis outside just as something off to the side is. A position at the origin counts as inside.\nThe dot is returned either way, so a consideration can score on it.",
            "params": [
                {
                    "name": "origin",
                    "desc": "The eye; its `LookVector` is the centre of the cone.",
                    "lua_type": "CFrame"
                },
                {
                    "name": "position",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "fieldOfViewDegrees",
                    "desc": "The full angle of the cone.",
                    "lua_type": "number"
                },
                {
                    "name": "range",
                    "desc": "Studs.",
                    "lua_type": "number"
                }
            ],
            "returns": [
                {
                    "desc": "Inside the cone and within range.",
                    "lua_type": "boolean"
                },
                {
                    "desc": "The dot product between the look vector and the direction to `position`: `1` dead ahead, `0` beside, `-1` behind.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 567,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "SortByDistance",
            "desc": "A new list of `parts`, nearest to `origin` first. Stable: parts at the same distance keep\ntheir order. `parts` is not touched.",
            "params": [
                {
                    "name": "origin",
                    "desc": "",
                    "lua_type": "Vector3"
                },
                {
                    "name": "parts",
                    "desc": "",
                    "lua_type": "{ BasePart }"
                }
            ],
            "returns": [
                {
                    "desc": "A new list, nearest first.",
                    "lua_type": "{ BasePart }"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 599,
                "path": "lib/Senses/Perception.luau"
            }
        }
    ],
    "properties": [
        {
            "name": "HIDDEN_ATTRIBUTE",
            "desc": "The attribute name that hides a character from every sense: set it to `true` on the\ncharacter (or on its root) while the player is in a locker, under a bed, or otherwise out of\nthe game's sight rules, and [Perception.AliveRoots] leaves them out until it is cleared.",
            "lua_type": "\"VLUXYAI_HIDDEN\"",
            "source": {
                "line": 55,
                "path": "lib/Senses/Perception.luau"
            }
        },
        {
            "name": "LIGHT_TAG",
            "desc": "The `CollectionService` tag a light must carry for [Perception.LightAt] to count it.",
            "lua_type": "\"VLUXYAI_LIGHT\"",
            "source": {
                "line": 397,
                "path": "lib/Senses/Perception.luau"
            }
        }
    ],
    "types": [
        {
            "name": "SeenByOptions",
            "desc": "",
            "fields": [
                {
                    "name": "FieldOfView",
                    "lua_type": "number?",
                    "desc": "The full angle of each observer's view cone. Default `100`."
                },
                {
                    "name": "Range",
                    "lua_type": "number?",
                    "desc": "Studs. Default `math.huge`."
                },
                {
                    "name": "Eye",
                    "lua_type": "((root: BasePart) -> CFrame?)?",
                    "desc": "Where an observer looks from. Default: the character's head, else its root ([Perception.EyeOf]). A game that replicates the camera returns it here."
                },
                {
                    "name": "Ignore",
                    "lua_type": "{ Instance }?",
                    "desc": "What the sight ray passes through. Default nothing."
                },
                {
                    "name": "Observers",
                    "lua_type": "{ BasePart }?",
                    "desc": "Who may be looking. Default [Perception.AllAliveRoots], hidden players included."
                }
            ],
            "source": {
                "line": 504,
                "path": "lib/Senses/Perception.luau"
            }
        }
    ],
    "name": "Perception",
    "desc": "The helpers the built-in senses are made of, and the place to start when writing your own.\nA [Types.Sense] is any table with `Tick`; the work inside one is usually \"who could I be\nlooking at\" (`AliveRoots`), \"is it in front of me\" (`InCone`), \"is anything in the way\"\n(`LineOfSight`), \"which is nearest\" (`SortByDistance`), and this module answers each of those\non its own.\n\nEverything here is about *targets*: [Perception.HumanoidOf] takes a root a sense wrote to the\nblackboard and finds that character's humanoid. The agent's own humanoid is\n`agent.Entity:FindFirstChildOfClass(\"Humanoid\")`, and the two look alike in code, so name\nthe variable.\n\n`Targets` on every built-in sense defaults to [Perception.AliveRoots], and `Ignore` on\n[Sight] defaults to [Perception.IgnoreList]. A brain that acts on a target root a sense wrote\nto the blackboard reaches the rest of the character through [Perception.CharacterOf] and\n[Perception.HumanoidOf].\n\nEverything that takes a part reads it duck-typed, so a spec can pass plain tables.",
    "source": {
        "line": 43,
        "path": "lib/Senses/Perception.luau"
    }
}