Skip to main content

Sounds

A Types.Sense that hears the Sound instances in the world. Tag a sound with CollectionService ("VLUXYAI_SOUND" by default), parent it to a part or an attachment so it is 3D, and every agent with this sense judges it each tick while it plays: the sound's Volume rolled off over the distance the way Roblox plays it, times the sound's VLUXYAI_SOUND/Multiplier attribute when it has one. The loudest sound still above Threshold is written as HeardSound with its position, distance, loudness at the agent and the time, and the memory keys stay for Memory seconds after it stops so a state can go and look. Exposed as VluxyAI.Senses.Sounds. The tag and the attribute name are prefixed so they do not collide with a game's own; both are options.

-- the game, once per sound worth noticing
CollectionService:AddTag(radio.Sound, "VLUXYAI_SOUND")
radio.Sound:SetAttribute("VLUXYAI_SOUND/Multiplier", 2) -- twice as far as its roll-off alone

-- the agent
:AddSense(VluxyAI.Senses.Sounds.new({ Threshold = 0.1, Memory = 8 }))

-- a transition in the brain
{ To = "Investigate", When = function(agent)
	return agent.Blackboard.HeardSoundAt ~= nil and agent.Blackboard.HeardSoundAt ~= agent.Data.InvestigatedAt
end }

A sound with no tag, one that is not playing, one parented to a folder or to SoundService (so it is 2D), one with Volume 0 and one the agent itself carries are never heard. The one signal-based sense, Hearing, is for events your code fires; this one is for sounds the world is already playing. One instance may serve many agents: it keeps no per-agent state.

Types​

SoundsOptions​

interface SoundsOptions {
Tag: string?--

The CollectionService tag a Sound must carry to be heard. Default "VLUXYAI_SOUND".

Threshold: number?--

The volume a sound must still have at the agent, after roll-off and the attribute, to be heard. Default 0.05.

MultiplierAttribute: string?--

A number attribute on the Sound that multiplies its volume for the AI: 2 carries twice as loud, 0.5 half, 0 is never heard. Missing means 1. Default "VLUXYAI_SOUND/Multiplier".

Memory: number?--

Seconds the memory keys outlive the last audible tick. Default 5.

Occlusion: number?--

Volume multiplier per wall between the sound and the agent, 0 to 1. 1 (the default) casts no rays; 0.5 halves a sound per wall.

OcclusionWalls: number?--

Walls counted at most. Default 3.

Filter: ((
sound: Sound,
agent: Agent
) → boolean)?--

Return false to ignore a sound for this agent. The agent's own sounds (under its entity) are ignored without it.

Prefix: string?--

Prepended to every key this sense writes, so two instances can coexist. Default "".

}

Functions​

new​

Sounds.new(options: SoundsOptions?) → Sounds

Errors

TypeDescription
"unknown option"A key not in `SoundsOptions`.
"Threshold must be zero or more"

Attenuate​

Sounds.Attenuate(
distance: number,--

Studs from the sound to the listener.

volume: number,--

The sound's Volume.

minDistance: number,--

RollOffMinDistance: full volume up to here.

maxDistance: number,--

RollOffMaxDistance: silence (linear modes) or no further attenuation (inverse) from here.

mode: string--

The RollOffMode name: Inverse, InverseTapered, Linear or LinearSquare. Anything else is Inverse.

) → number--

The volume at the listener, in [0, volume].

The roll-off curves as Roblox plays them, pure. Within minDistance a sound is at full volume. Inverse falls as minDistance / distance and stops falling at maxDistance; Linear and LinearSquare fall to nothing at maxDistance, the second along a squared curve; InverseTapered is the quieter of Inverse and LinearSquare, so it follows the inverse curve up close and still reaches silence at maxDistance.

Tick​

Sounds.Tick(
self: Sounds,
agent: Agent,
deltaTime: number
) → ()

Judges every tagged sound and writes the keys listed above to agent.Blackboard.

Configure​

Sounds.Configure(
self: Sounds,
patch: SoundsOptions--

The options to change; the rest keep their values.

) → ()

Changes options after construction, for difficulty and pacing: a director widening the cone or shortening the memory mid-round. The same rules as new apply.

Errors

TypeDescription
"unknown option"
Show raw api
{
    "functions": [
        {
            "name": "new",
            "desc": "",
            "params": [
                {
                    "name": "options",
                    "desc": "",
                    "lua_type": "SoundsOptions?"
                }
            ],
            "returns": [
                {
                    "desc": "",
                    "lua_type": "Sounds"
                }
            ],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"unknown option\"",
                    "desc": "A key not in `SoundsOptions`."
                },
                {
                    "lua_type": "\"Threshold must be zero or more\"",
                    "desc": ""
                }
            ],
            "source": {
                "line": 140,
                "path": "lib/Senses/Sounds.luau"
            }
        },
        {
            "name": "Attenuate",
            "desc": "The roll-off curves as Roblox plays them, pure. Within `minDistance` a sound is at full volume.\n`Inverse` falls as `minDistance / distance` and stops falling at `maxDistance`; `Linear` and\n`LinearSquare` fall to nothing at `maxDistance`, the second along a squared curve;\n`InverseTapered` is the quieter of `Inverse` and `LinearSquare`, so it follows the inverse\ncurve up close and still reaches silence at `maxDistance`.",
            "params": [
                {
                    "name": "distance",
                    "desc": "Studs from the sound to the listener.",
                    "lua_type": "number"
                },
                {
                    "name": "volume",
                    "desc": "The sound's `Volume`.",
                    "lua_type": "number"
                },
                {
                    "name": "minDistance",
                    "desc": "`RollOffMinDistance`: full volume up to here.",
                    "lua_type": "number"
                },
                {
                    "name": "maxDistance",
                    "desc": "`RollOffMaxDistance`: silence (linear modes) or no further attenuation (inverse) from here.",
                    "lua_type": "number"
                },
                {
                    "name": "mode",
                    "desc": "The `RollOffMode` name: `Inverse`, `InverseTapered`, `Linear` or `LinearSquare`. Anything else is `Inverse`.",
                    "lua_type": "string"
                }
            ],
            "returns": [
                {
                    "desc": "The volume at the listener, in `[0, volume]`.",
                    "lua_type": "number"
                }
            ],
            "function_type": "static",
            "source": {
                "line": 175,
                "path": "lib/Senses/Sounds.luau"
            }
        },
        {
            "name": "Tick",
            "desc": "Judges every tagged sound and writes the keys listed above to `agent.Blackboard`.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Sounds"
                },
                {
                    "name": "agent",
                    "desc": "",
                    "lua_type": "Agent"
                },
                {
                    "name": "deltaTime",
                    "desc": "",
                    "lua_type": "number"
                }
            ],
            "returns": [],
            "function_type": "static",
            "source": {
                "line": 275,
                "path": "lib/Senses/Sounds.luau"
            }
        },
        {
            "name": "Configure",
            "desc": "Changes options after construction, for difficulty and pacing: a director widening the cone\nor shortening the memory mid-round. The same rules as `new` apply.",
            "params": [
                {
                    "name": "self",
                    "desc": "",
                    "lua_type": "Sounds"
                },
                {
                    "name": "patch",
                    "desc": "The options to change; the rest keep their values.",
                    "lua_type": "SoundsOptions"
                }
            ],
            "returns": [],
            "function_type": "static",
            "errors": [
                {
                    "lua_type": "\"unknown option\"",
                    "desc": ""
                }
            ],
            "source": {
                "line": 374,
                "path": "lib/Senses/Sounds.luau"
            }
        }
    ],
    "properties": [],
    "types": [
        {
            "name": "SoundsOptions",
            "desc": "",
            "fields": [
                {
                    "name": "Tag",
                    "lua_type": "string?",
                    "desc": "The `CollectionService` tag a `Sound` must carry to be heard. Default `\"VLUXYAI_SOUND\"`."
                },
                {
                    "name": "Threshold",
                    "lua_type": "number?",
                    "desc": "The volume a sound must still have at the agent, after roll-off and the attribute, to be heard. Default `0.05`."
                },
                {
                    "name": "MultiplierAttribute",
                    "lua_type": "string?",
                    "desc": "A number attribute on the `Sound` that multiplies its volume for the AI: `2` carries twice as loud, `0.5` half, `0` is never heard. Missing means `1`. Default `\"VLUXYAI_SOUND/Multiplier\"`."
                },
                {
                    "name": "Memory",
                    "lua_type": "number?",
                    "desc": "Seconds the memory keys outlive the last audible tick. Default `5`."
                },
                {
                    "name": "Occlusion",
                    "lua_type": "number?",
                    "desc": "Volume multiplier per wall between the sound and the agent, `0` to `1`. `1` (the default) casts no rays; `0.5` halves a sound per wall."
                },
                {
                    "name": "OcclusionWalls",
                    "lua_type": "number?",
                    "desc": "Walls counted at most. Default `3`."
                },
                {
                    "name": "Filter",
                    "lua_type": "((sound: Sound, agent: Agent) -> boolean)?",
                    "desc": "Return `false` to ignore a sound for this agent. The agent's own sounds (under its entity) are ignored without it."
                },
                {
                    "name": "Prefix",
                    "lua_type": "string?",
                    "desc": "Prepended to every key this sense writes, so two instances can coexist. Default `\"\"`."
                }
            ],
            "source": {
                "line": 66,
                "path": "lib/Senses/Sounds.luau"
            }
        }
    ],
    "name": "Sounds",
    "desc": "A [Types.Sense] that hears the `Sound` instances in the world. Tag a sound with\n`CollectionService` (`\"VLUXYAI_SOUND\"` by default), parent it to a part or an attachment so it is\n3D, and every agent with this sense judges it each tick while it plays: the sound's `Volume`\nrolled off over the distance the way Roblox plays it, times the sound's `VLUXYAI_SOUND/Multiplier`\nattribute when it has one. The loudest sound still above `Threshold` is written as\n`HeardSound` with its position, distance, loudness at the agent and the time, and the memory\nkeys stay for `Memory` seconds after it stops so a state can go and look. Exposed as\n`VluxyAI.Senses.Sounds`. The tag and the attribute name are prefixed so they do not collide\nwith a game's own; both are options.\n\n```lua\n-- the game, once per sound worth noticing\nCollectionService:AddTag(radio.Sound, \"VLUXYAI_SOUND\")\nradio.Sound:SetAttribute(\"VLUXYAI_SOUND/Multiplier\", 2) -- twice as far as its roll-off alone\n\n-- the agent\n:AddSense(VluxyAI.Senses.Sounds.new({ Threshold = 0.1, Memory = 8 }))\n\n-- a transition in the brain\n{ To = \"Investigate\", When = function(agent)\n\treturn agent.Blackboard.HeardSoundAt ~= nil and agent.Blackboard.HeardSoundAt ~= agent.Data.InvestigatedAt\nend }\n```\n\nA sound with no tag, one that is not playing, one parented to a folder or to `SoundService`\n(so it is 2D), one with `Volume` `0` and one the agent itself carries are never heard. The\none signal-based sense, [Hearing], is for events your code fires; this one is for sounds the\nworld is already playing. One instance may serve many agents: it keeps no per-agent state.",
    "source": {
        "line": 132,
        "path": "lib/Senses/Sounds.luau"
    }
}