### Quick Start: Lifecycle Approach Source: https://github.com/ldgerrits/quickzone/blob/main/docs/intro.md Recommended for modern code. Define relationships declaratively and manage state with a single lifecycle function. This example sets up an anti-gravity system. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone, Group, Observer = QuickZone.Zone, QuickZone.Group, QuickZone.Observer -- Create a group that automatically tracks the client's character (including respawns) local myPlayer = Group.localPlayer() -- Find all current and future instances with the 'Water' tag. local zones = Zone.fromTag('AntiGravity', { metadata = { GravityMultiplier = 0.4 } }) -- Create an observer subscribed to the group and attached to the zones. local gravityObserver = Observer.new({ groups = { myPlayer }, zones = { zones } }) -- Define behavior gravityObserver:observe(function(player, zone) local character = player.Character local hrp = character and character:FindFirstChild('HumanoidRootPart') if not hrp then return end -- Get the zone's gravity multiplier using metadata. local meta = zone:getMetadata() local multiplier = meta and meta.GravityMultiplier or 1 -- Create the Anti-Gravity force on enter local force = Instance.new('BodyForce') force.Name = 'AntiGravityForce' force.Force = Vector3.new(0, hrp.AssemblyMass * workspace.Gravity * (1- multiplier), 0) force.Parent = hrp -- Automatically destroy the force when the player exits the zone return function() force:Destroy() end end) ``` -------------------------------- ### Initialize QuickZone Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Standard setup for requiring the QuickZone module and accessing its primary components. ```lua local QuickZone = require(game:GetService('ReplicatedStorage').QuickZone) local Zone, Group, Observer = QuickZone.Zone, QuickZone.Group, QuickZone.Observer ``` -------------------------------- ### Install QuickZone via Wally Source: https://github.com/ldgerrits/quickzone/blob/main/docs/intro.md Add this line to your wally.toml file to install the QuickZone library. ```toml ldgerrits/quickzone@^1.3.187 ``` -------------------------------- ### Quick Start: Event-Driven Approach Source: https://github.com/ldgerrits/quickzone/blob/main/docs/intro.md Familiar for ZonePlus users. Manually wire objects and use events like `onEnter` for one-off actions. This example also implements an anti-gravity system. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone, Group, Observer = QuickZone.Zone, QuickZone.Group, QuickZone.Observer local localPlayer = game:GetService('Players').LocalPlayer local myPlayer = Group.localPlayer() local zones = Zone.fromChildren(workspace.AntiGravityParts) local gravityObserver = Observer.new():subscribe(myPlayer):attach(zones) -- Connect events gravityObserver:onLocalPlayerEnter(function(zone) local character = localPlayer.Character local hrp = character and character:FindFirstChild('HumanoidRootPart') if not hrp then return end local ref = zone:getReference() -- This returns the zone's part local multiplier = ref and ref:GetAttribute("GravityMultiplier") or 1 -- You can use attributes! local force = Instance.new('BodyForce') force.Name = 'AntiGravityForce' force.Force = Vector3.new(0, hrp.AssemblyMass * workspace.Gravity * (1- multiplier), 0) force.Parent = hrp end) gravityObserver:onLocalPlayerExit(function(zone) local character = localPlayer.Character local hrp = character and character:FindFirstChild('HumanoidRootPart') if hrp and hrp:FindFirstChild('AntiGravityForce') then hrp.AntiGravityForce:Destroy() end end) ``` -------------------------------- ### Install QuickZone via NPM Source: https://github.com/ldgerrits/quickzone/blob/main/docs/intro.md Install the QuickZone package for roblox-ts projects using npm. ```bash npm i @rbxts/quickzone ``` -------------------------------- ### Query Observer State with Iterators Source: https://context7.com/ldgerrits/quickzone/llms.txt This snippet illustrates how to query the current state of entities within observer zones using both array-based methods and zero-allocation iterators. It covers getting all entities, entities in specific zones, checking point containment, and iterating over attached zones and groups. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local playerGroup = QuickZone.Group.players() local arenaZones = QuickZone.Zone.fromTag('Arena') local observer = QuickZone.Observer.new({ groups = { playerGroup }, zones = { arenaZones } }) -- Get all entities currently inside local entitiesInside = observer:getEntitiesInside() local playersInside = observer:getPlayersInside() -- Get entities/players in a specific zone local zone = arenaZones:getZones()[1] local entitiesInZone = observer:getEntitiesInZone(zone) local playersInZone = observer:getPlayersInZone(zone) -- Query specific entity's zone local player = game.Players:GetPlayers()[1] local playerZone = observer:getZoneOfEntity(player) local playerZone2 = observer:getZoneOfPlayer(player) -- Alias for players -- Get observer's zones and groups local attachedZones = observer:getZones() local subscribedGroups = observer:getGroups() -- Check if a point is inside any attached zone local isInside = observer:isPointInside(Vector3.new(0, 10, 0)) -- Zero-allocation iterators (ideal for per-frame processing) for entity, zone in observer:iterEntitiesInside() do print(entity, 'is in', zone:getId()) end for player, zone in observer:iterPlayersInside() do print(player.Name, 'is in', zone:getId()) end for entity in observer:iterEntitiesInZone(zone) do print(entity, 'is in the zone') end for player in observer:iterPlayersInZone(zone) do print(player.Name, 'is in the zone') end for zone in observer:iterZones() do print('Attached zone:', zone:getId()) end for group in observer:iterGroups() do print('Subscribed group:', group:getId()) end ``` -------------------------------- ### Initialize Observers Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Configure an observer to listen to groups and attach to zones. ```lua local observer = Observer.new({ updateRate = 60, -- Check up to 60 times a second precision = 1.0, -- Only query if the entity moves more than 1 stud priority = 5 -- Used to resolve overlapping zones }) observer:subscribe(allPlayers) observer:attach(healingZones) ``` -------------------------------- ### Configure QuickZone Global Settings Source: https://context7.com/ldgerrits/quickzone/llms.txt Configure global QuickZone settings like automatic updates, sync rate, and frame budget. Disable auto-updates and manually step using RunService.Heartbeat for deterministic control. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) -- Configure all settings at once QuickZone:configure({ enabled = true, -- Enable automatic spatial updates autoSyncRate = 30, -- Sync dynamic zones 30 times per second frameBudget = 1, -- Allow 1ms of frame time for processing }) -- Or configure individual settings QuickZone:setEnabled(true) QuickZone:setAutoSyncRate(30) QuickZone:setFrameBudget(0.5) -- More conservative budget -- For ECS/manual control, disable auto-updates and step manually QuickZone:setEnabled(false) RunService.Heartbeat:Connect(function(dt) QuickZone:update(dt) -- Manual deterministic stepping end) ``` -------------------------------- ### QuickZone Configuration Source: https://context7.com/ldgerrits/quickzone/llms.txt Configure global QuickZone settings including automatic updates, sync rate for dynamic zones, and frame budget allocation to prevent performance drops. ```APIDOC ## QuickZone Configuration ### Description Configure global QuickZone settings including automatic updates, sync rate for dynamic zones, and frame budget allocation to prevent performance drops. ### Method `QuickZone:configure(settings)` ### Parameters #### Request Body - **settings** (table) - Required - A table containing configuration options. - **enabled** (boolean) - Optional - Enable automatic spatial updates. - **autoSyncRate** (number) - Optional - Sync dynamic zones at this rate per second. - **frameBudget** (number) - Optional - Allow this many milliseconds of frame time for processing. ### Request Example ```lua QuickZone:configure({ enabled = true, autoSyncRate = 30, frameBudget = 1 }) ``` ### Method `QuickZone:setEnabled(enabled)` ### Parameters #### Request Body - **enabled** (boolean) - Required - Whether to enable automatic spatial updates. ### Request Example ```lua QuickZone:setEnabled(true) ``` ### Method `QuickZone:setAutoSyncRate(rate)` ### Parameters #### Request Body - **rate** (number) - Required - The rate at which to sync dynamic zones per second. ### Request Example ```lua QuickZone:setAutoSyncRate(30) ``` ### Method `QuickZone:setFrameBudget(budget)` ### Parameters #### Request Body - **budget** (number) - Required - The allowed frame time in milliseconds for processing. ### Request Example ```lua QuickZone:setFrameBudget(0.5) ``` ### Method `QuickZone:update(dt)` ### Description Manually step the QuickZone simulation. Useful for ECS or manual control when automatic updates are disabled. ### Parameters #### Request Body - **dt** (number) - Required - The delta time since the last update. ### Request Example ```lua RunService.Heartbeat:Connect(function(dt) QuickZone:update(dt) end) ``` ``` -------------------------------- ### Create Zones in Bulk Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Construct multiple zones simultaneously using CollectionService tags or existing object hierarchies. ```lua -- Create zones from a CollectionService tag local lavaZones = Zone.fromTag('Lava', { metadata = { damage = 10 } }) -- Create zones from an array of parts local safeZones = Zone.fromParts(workspace.SafeZones:GetChildren()) -- Create zones from all BaseParts inside a Model or Folder (Deep search) local hazardZones = Zone.fromDescendants(workspace.TrapModel) -- Create zones from only the direct children of a Folder (Shallow search) local flatZones = Zone.fromChildren(workspace.FlatFolder) ``` -------------------------------- ### Set QuickZone Frame Budget Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Constrain the CPU time allowed for QuickZone processing per frame to maintain performance. ```lua -- Allow 0.5 milliseconds per frame (default is 1ms) QuickZone:setFrameBudget(0.5) ``` -------------------------------- ### Manage Lifecycle with observe Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Recommended approach for persistent state management, using a cleanup function for exit logic. ```lua observer:observePlayer(function(player, zone) -- Logic here runs when entering return function() -- Logic here runs when exiting end end) ``` -------------------------------- ### Observe Player Transitions with Dynamic Damage Source: https://context7.com/ldgerrits/quickzone/llms.txt This snippet demonstrates how to observe player transitions between zones and dynamically update damage values based on zone metadata. It includes a damage loop that applies the current damage and cleans up listeners when the player is no longer active. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local playerGroup = QuickZone.Group.players() local hazardZones = QuickZone.Zone.fromTag('Hazard', { metadata = { Damage = 10 } }) local hazardObserver = QuickZone.Observer.new({ groups = { playerGroup }, zones = { hazardZones }, updateRate = 20 }) -- Track damage with transitions for dynamic damage values hazardObserver:observePlayer(function(player, initialZone) local currentDamage = initialZone:getMetadata().Damage or 10 local active = true -- Listen for transitions to update damage local disconnectTransition = hazardObserver:onPlayerTransition(function(transitioningPlayer, newZone) if transitioningPlayer ~= player then return end currentDamage = newZone:getMetadata().Damage or 10 print(player.Name .. ' transitioned. New damage: ' .. currentDamage) end) -- Damage loop task.spawn(function() while active do local humanoid = player.Character and player.Character:FindFirstChild('Humanoid') if humanoid then humanoid:TakeDamage(currentDamage) end task.wait(1) end end) return function() active = false disconnectTransition() end end) -- Simple transition logging hazardObserver:onTransition(function(entity, newZone) print('Entity transitioned to zone: ' .. newZone:getId()) end) -- Player-specific transition hazardObserver:onPlayerTransition(function(player, newZone) print(player.Name .. ' moved to new hazard area') end) -- LocalPlayer transition (client-side) hazardObserver:onLocalPlayerTransition(function(newZone) print('You entered a different hazard zone') end) ``` -------------------------------- ### Configure Observer with Observer.new() Source: https://context7.com/ldgerrits/quickzone/llms.txt Observer.new() creates an observer to monitor groups and zones, triggering callbacks on entity entry/exit. It allows detailed configuration of priority, update rate, precision, and safety. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone, Group, Observer = QuickZone.Zone, QuickZone.Group, QuickZone.Observer local playerGroup = Group.players() local safeZone = Zone.fromPart(workspace.SafeZone) local hazardZones = Zone.fromTag('Hazard') -- Create observer with full configuration local observer = Observer.new({ groups = { playerGroup }, -- Groups to monitor zones = { safeZone, hazardZones }, -- Zones to watch priority = 10, -- Higher priority wins overlaps updateRate = 60, -- Check 60 times per second precision = 0.5, -- Ignore movement < 0.5 studs enabled = true, -- Start enabled safety = true -- Wrap callbacks in task.spawn }) -- Or create empty and configure later local dynamicObserver = Observer.new({ zones = {}, groups = {} }) dynamicObserver:subscribe(playerGroup) dynamicObserver:attach(safeZone) -- Chain configuration observer:setPriority(20) :setUpdateRate(30) :setPrecision(1.0) :setEnabled(true) :setSafety(false) -- Unsafe mode: no yielding allowed! -- Query state print(observer:getId()) print(observer:getPriority()) print(observer:getUpdateRate()) print(observer:getPrecision()) print(observer:isEnabled()) print(observer:isSafe()) ``` -------------------------------- ### Handle one-off events with Observer.onEnter and Observer.onExit Source: https://context7.com/ldgerrits/quickzone/llms.txt Implement event-driven callbacks for specific actions triggered when entities enter or exit defined zones. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local playerGroup = QuickZone.Group.players() local checkpointZones = QuickZone.Zone.fromTag('Checkpoint') local observer = QuickZone.Observer.new({ groups = { playerGroup }, zones = { checkpointZones } }) -- Generic enter/exit events local disconnectEnter = observer:onEnter(function(entity, zone) print(entity.Name .. ' entered zone ' .. zone:getId()) end) local disconnectExit = observer:onExit(function(entity, zone) print(entity.Name .. ' exited zone ' .. zone:getId()) end) -- Player-specific events observer:onPlayerEnter(function(player, zone) print(player.Name .. ' reached checkpoint!') -- Play sound effect local sound = workspace.Sounds.CheckpointReached:Clone() sound.Parent = player.Character.PrimaryPart sound:Play() game.Debris:AddItem(sound, 2) end) observer:onPlayerExit(function(player, zone) print(player.Name .. ' left checkpoint area') end) -- LocalPlayer-specific events (client-side only) observer:onLocalPlayerEnter(function(zone) -- Update UI game.Players.LocalPlayer.PlayerGui.ZoneLabel.Text = 'Checkpoint' end) observer:onLocalPlayerExit(function(zone) game.Players.LocalPlayer.PlayerGui.ZoneLabel.Text = '' end) -- Disconnect when done disconnectEnter() disconnectExit() ``` -------------------------------- ### Create Zone with Explicit Parameters Source: https://context7.com/ldgerrits/quickzone/llms.txt Create zones using explicit CFrame, size, and shape parameters, suitable for procedural generation or areas without physical parts. Query zone properties like ID, position, and metadata. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone = QuickZone.Zone -- Create a static block zone local lobbyZone = Zone.new({ cframe = CFrame.new(0, 10, 0), size = Vector3.new(50, 20, 50), shape = 'Block', -- 'Block', 'Ball', 'Cylinder', 'Wedge', 'CornerWedge' isDynamic = false, metadata = { Name = 'Lobby', SafeArea = true } }) -- Create a dynamic spherical zone that follows a reference local bossArena = Zone.new({ cframe = workspace.BossRoom.CFrame, size = Vector3.new(100, 100, 100), shape = 'Ball', isDynamic = true, reference = workspace.BossRoom, autoSync = true, -- Automatically syncs position with reference metadata = { BossName = 'Dragon', Difficulty = 'Hard' } }) -- Query zone properties print(lobbyZone:getId()) -- Unique zone ID print(lobbyZone:getPosition()) -- Vector3 position print(lobbyZone:getCFrame()) -- Full CFrame print(lobbyZone:getSize()) -- Vector3 size print(lobbyZone:getShape()) -- 'Block', 'Ball', etc. print(lobbyZone:getMetadata()) -- { Name = 'Lobby', SafeArea = true } print(lobbyZone:isPointInside(Vector3.new(0, 10, 0))) -- true ``` -------------------------------- ### Zone.new Source: https://context7.com/ldgerrits/quickzone/llms.txt Creates a zone with explicit CFrame, size, and shape parameters. Ideal for procedural generation or areas without physical parts. ```APIDOC ## Zone.new ### Description Creates a zone with explicit CFrame, size, and shape parameters. Ideal for procedural generation or areas without physical parts. ### Method `Zone.new(properties)` ### Parameters #### Request Body - **properties** (table) - Required - Properties for the new zone. - **cframe** (CFrame) - Required - The position and orientation of the zone. - **size** (Vector3) - Required - The dimensions of the zone. - **shape** (string) - Required - The shape of the zone. Options: 'Block', 'Ball', 'Cylinder', 'Wedge', 'CornerWedge'. - **isDynamic** (boolean) - Optional - Whether the zone's position should update automatically. Defaults to false. - **reference** (Instance) - Optional - An object whose CFrame the zone should follow if `isDynamic` and `autoSync` are true. - **autoSync** (boolean) - Optional - If true and `isDynamic` is true, automatically syncs position with the `reference` object. Defaults to false. - **metadata** (table) - Optional - Custom data associated with the zone. ### Request Example ```lua local lobbyZone = Zone.new({ cframe = CFrame.new(0, 10, 0), size = Vector3.new(50, 20, 50), shape = 'Block', isDynamic = false, metadata = { Name = 'Lobby', SafeArea = true } }) ``` ### Method `zone:getId()` ### Description Returns the unique identifier for the zone. ### Response #### Success Response (string) - **id** (string) - The unique ID of the zone. ### Method `zone:getPosition()` ### Description Returns the Vector3 position of the zone's center. ### Response #### Success Response (Vector3) - **position** (Vector3) - The position of the zone. ### Method `zone:getCFrame()` ### Description Returns the CFrame of the zone. ### Response #### Success Response (CFrame) - **cframe** (CFrame) - The CFrame of the zone. ### Method `zone:getSize()` ### Description Returns the Vector3 size of the zone. ### Response #### Success Response (Vector3) - **size** (Vector3) - The size of the zone. ### Method `zone:getShape()` ### Description Returns the shape of the zone. ### Response #### Success Response (string) - **shape** (string) - The shape of the zone. ### Method `zone:getMetadata()` ### Description Returns the metadata associated with the zone. ### Response #### Success Response (table) - **metadata** (table) - The metadata of the zone. ### Method `zone:isPointInside(point)` ### Description Checks if a given point is inside the zone. ### Parameters #### Request Body - **point** (Vector3) - Required - The point to check. ### Response #### Success Response (boolean) - **inside** (boolean) - True if the point is inside the zone, false otherwise. ### Request Example ```lua print(lobbyZone:isPointInside(Vector3.new(0, 10, 0))) ``` ``` -------------------------------- ### Observe Group Enter/Exit Events Source: https://context7.com/ldgerrits/quickzone/llms.txt This snippet shows how to use `observeGroup`, `onGroupEnter`, and `onGroupExit` to manage group-level events. It demonstrates spawning a boss when enemies enter a zone and cleaning it up when they leave, along with playing/stopping music. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local enemyGroup = QuickZone.Group.new({ entities = workspace.Enemies:GetChildren() }) local bossRoom = QuickZone.Zone.fromPart(workspace.BossRoom) local observer = QuickZone.Observer.new({ groups = { enemyGroup }, zones = { bossRoom } }) -- Lifecycle pattern for groups observer:observeGroup(function(group, zone) print('Group ' .. group:getId() .. ' has arrived!') -- Spawn boss when enemies arrive local boss = workspace.Boss:Clone() boss.Parent = workspace return function() print('All enemies defeated or left!') boss:Destroy() end end) -- Event-driven group callbacks observer:onGroupEnter(function(group, zone) print('First member of group ' .. group:getId() .. ' entered!') -- Start encounter music workspace.Sounds.BattleMusic:Play() end) observer:onGroupExit(function(group, zone) print('Last member of group ' .. group:getId() .. ' left!') -- Stop music, give rewards workspace.Sounds.BattleMusic:Stop() end) ``` -------------------------------- ### Track All Players with Group.players() Source: https://context7.com/ldgerrits/quickzone/llms.txt Use Group.players() to create a group that automatically tracks all players on the server. This group handles player joins, leaves, and respawns automatically. It's useful for server-wide zone monitoring. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Group = QuickZone.Group -- Automatically tracks all players local allPlayers = Group.players() -- Players are automatically added/removed as they join/leave -- Character respawns are handled automatically -- HumanoidRootPart streaming is handled automatically -- Use with an observer local safeZone = QuickZone.Zone.fromPart(workspace.SafeZone) local safeObserver = QuickZone.Observer.new({ groups = { allPlayers }, zones = { safeZone } }) safeObserver:onPlayerEnter(function(player, zone) print(player.Name .. " entered the safe zone!") end) -- Get all currently tracked players for entity in allPlayers:iterEntities() do -- entity is the HumanoidRootPart, but callbacks receive the Player object print(entity.Parent.Name) end ``` -------------------------------- ### Implement ECS/Polling Pattern with QuickZone Source: https://context7.com/ldgerrits/quickzone/llms.txt Use this pattern for ECS frameworks or continuous logic processing spatial data every frame. It requires disabling automatic updates and manually stepping QuickZone within a RunService.Heartbeat connection. ```lua local RunService = game:GetService('RunService') local QuickZone = require(game.ReplicatedStorage.QuickZone) -- Disable automatic updates for deterministic control QuickZone:setEnabled(false) local playerGroup = QuickZone.Group.players() local hazardZones = QuickZone.Zone.fromTag('Hazard', { metadata = { Damage = 10 } }) local hazardObserver = QuickZone.Observer.new({ groups = { playerGroup }, zones = { hazardZones } }) -- Manual update loop RunService.Heartbeat:Connect(function(dt) -- Step QuickZone deterministically QuickZone:update(dt) -- Process spatial data with zero-allocation iterators for player, zone in hazardObserver:iterPlayersInside() do local character = player.Character local humanoid = character and character:FindFirstChild('Humanoid') if not humanoid or humanoid.Health <= 0 then continue end local metadata = zone:getMetadata() or {} local dps = metadata.Damage or 10 humanoid:TakeDamage(dps * dt) if humanoid.Health <= 0 then print(player.Name .. ' died in ' .. (metadata.Name or 'hazard')) end end end) -- Force immediate tree rebuild if needed QuickZone:rebuild() ``` -------------------------------- ### Create Dynamic Zones from Parts Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Create a dynamic zone attached to a physical part for moving objects. ```lua local trainZone = Zone.fromPart(workspace.TrainCarriage, { isDynamic = true, metadata = { route = 'North' } }) ``` -------------------------------- ### Track LocalPlayer with Group.localPlayer() Source: https://context7.com/ldgerrits/quickzone/llms.txt Use Group.localPlayer() on the client to create a group that tracks only the LocalPlayer. This is ideal for client-side effects that should only trigger for the player running the code. ```lua -- Client-side only! local QuickZone = require(game.ReplicatedStorage.QuickZone) local Group = QuickZone.Group local myPlayer = Group.localPlayer() -- Use for client-side effects local effectZones = QuickZone.Zone.fromTag('VisualEffect') local effectObserver = QuickZone.Observer.new({ groups = { myPlayer }, zones = { effectZones } }) effectObserver:observeLocalPlayer(function(zone) -- Add visual effect local blur = Instance.new('BlurEffect') blur.Size = 10 blur.Parent = game.Lighting return function() -- Cleanup when exiting blur:Destroy() end end) ``` -------------------------------- ### Zone.fromPart Source: https://context7.com/ldgerrits/quickzone/llms.txt Creates a zone from an existing BasePart, automatically inheriting its CFrame, size, and shape. ```APIDOC ## Zone.fromPart ### Description Creates a zone from an existing BasePart, automatically inheriting its CFrame, size, and shape (Block, Ball, Cylinder, Wedge, CornerWedge). ### Method `Zone.fromPart(part, properties)` ### Parameters #### Request Body - **part** (BasePart) - Required - The BasePart to create the zone from. - **properties** (table) - Optional - Additional properties for the zone. - **isDynamic** (boolean) - Optional - Whether the zone's position should update automatically. Defaults to false. - **autoSync** (boolean) - Optional - If true and `isDynamic` is true, automatically syncs position with the `part`. Defaults to false. - **metadata** (table) - Optional - Custom data associated with the zone. ### Request Example ```lua local safeZone = Zone.fromPart(workspace:WaitForChild('SafeZone')) local truckZone = Zone.fromPart(workspace.Truck.Hitbox, { isDynamic = true, autoSync = true, metadata = { VehicleType = 'Truck', Capacity = 4 } }) ``` ### Method `zone:sync()` ### Description Manually synchronizes the zone's properties (CFrame, Size, Shape) with its associated BasePart. Only applicable if the zone was created using `Zone.fromPart` and `autoSync` is false. ### Request Example ```lua trainZone:sync() ``` ### Method `zone:setPosition(position)` ### Description Sets the position of the zone. If the zone is dynamic and `autoSync` is true, this will be overridden by the reference's position. ### Parameters #### Request Body - **position** (Vector3) - Required - The new position for the zone. ### Request Example ```lua trainZone:setPosition(Vector3.new(100, 0, 0)) ``` ### Method `zone:setCFrame(cframe)` ### Description Sets the CFrame of the zone. If the zone is dynamic and `autoSync` is true, this will be overridden by the reference's CFrame. ### Parameters #### Request Body - **cframe** (CFrame) - Required - The new CFrame for the zone. ### Request Example ```lua trainZone:setCFrame(CFrame.new(0, 50, 0) * CFrame.Angles(0, math.rad(45), 0)) ``` ### Method `zone:setSize(size)` ### Description Sets the size of the zone. If the zone was created from a part and `autoSync` is true, this will be overridden by the part's size. ### Parameters #### Request Body - **size** (Vector3) - Required - The new size for the zone. ### Request Example ```lua trainZone:setSize(Vector3.new(20, 10, 40)) ``` ``` -------------------------------- ### Create Specialized Groups Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Built-in abstractions for tracking player lifecycles. ```lua -- Tracks all players in the server local allPlayers = Group.players() -- Tracks only the local player (client-side only) local myPlayer = Group.localPlayer() ``` -------------------------------- ### Handle Events with onPlayerEnter Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Classic event-driven approach suitable for one-off actions. ```lua observer:onPlayerEnter(function(player, zone) print(player.Name .. " entered " .. zone:getId()) end) ``` -------------------------------- ### Implement Data-Driven Observer Transitions Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Use this pattern to handle logic updates when a player moves between overlapping zones managed by the same observer. ```lua hazardObserver:observePlayer(function(player, initialZone) local currentDamage = initialZone:getMetadata().Damage or 10 local active = true local disconnectTransition = hazardObserver:onPlayerTransition(function(transitioningPlayer, newZone) if transitioningPlayer ~= player then return end currentDamage = newZone:getMetadata().Damage or 10 print(player.Name .. " transitioned. New damage: " .. currentDamage) end) task.spawn(function() while active do player.Character.Humanoid:TakeDamage(currentDamage) task.wait(1) end end) return function() active = false disconnectTransition() -- Clean up the listener end end) ``` -------------------------------- ### Create Zones from CollectionService Tags Source: https://context7.com/ldgerrits/quickzone/llms.txt Dynamically creates and manages zones for BaseParts with a specific CollectionService tag. Zones automatically update as tagged instances are added or removed. Use `isDynamic = false` for static zones. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone = QuickZone.Zone -- Create zones from all parts tagged "Lava" local lavaZones = Zone.fromTag('Lava', { isDynamic = false, metadata = { Name = 'Lava', Damage = 50 } }) -- Create zones from all parts tagged "ToxicGas" local toxicZones = Zone.fromTag('ToxicGas', { metadata = { Name = 'ToxicGas', Damage = 10 } }) -- Zones automatically update when tags are added/removed -- No manual management needed! -- Access all zones in the collection local allZones = lavaZones:getZones() for _, zone in allZones do print(zone:getId(), zone:getMetadata().Damage) end -- Check if a point is inside any zone in the collection local isInLava = lavaZones:isPointInside(Vector3.new(10, 5, 0)) -- Clean up when done lavaZones:destroy() ``` -------------------------------- ### Create Zones Manually Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Define zones procedurally using CFrame and Size parameters. ```lua local zone = Zone.new({ cframe = CFrame.new(0, 10, 0), size = Vector3.new(10, 10, 10), shape = 'Block', isDynamic = true, metadata = { Name = 'Lobby' } }) ``` -------------------------------- ### Perform Spatial Queries with QuickZone Source: https://context7.com/ldgerrits/quickzone/llms.txt Use these methods to query zones, groups, and entities at specific points or associated with specific objects. Zero-allocation iterators are available for performance-sensitive ECS systems. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) -- Get all zones at a specific point local position = Vector3.new(10, 5, 0) local zonesAtPoint = QuickZone:getZonesAtPoint(position) for _, zone in zonesAtPoint do print('Found zone:', zone:getId(), zone:getMetadata()) end -- Zero-allocation iterator for ECS systems for zone in QuickZone:iterZonesAtPoint(position) do print('Zone at point:', zone:getId()) break -- Safe to break early end -- Get zones an entity is currently inside local zonesOfEntity = QuickZone:getZonesOfEntity(workspace.Player) -- Get groups an entity belongs to local groupsOfEntity = QuickZone:getGroupsOfEntity(workspace.NPC) -- Iterate over zones of an entity for zone in QuickZone:iterZonesOfEntity(workspace.Player) do print('Player is in zone:', zone:getId()) end -- Global getters local allZones = QuickZone:getZones() local allGroups = QuickZone:getGroups() local allObservers = QuickZone:getObservers() local allEntities = QuickZone:getEntities() -- Global zero-allocation iterators for zone in QuickZone:iterZones() do print('Zone:', zone:getId()) end for group in QuickZone:iterGroups() do print('Group:', group:getId()) end for observer in QuickZone:iterObservers() do print('Observer:', observer:getId()) end for entity in QuickZone:iterEntities() do print('Entity:', entity) end for group in QuickZone:iterGroupsOfEntity(workspace.NPC) do print('Entity is in group:', group:getId()) end ``` -------------------------------- ### Implement Manual Polling with QuickZone Source: https://github.com/ldgerrits/quickzone/blob/main/docs/intro.md Disables auto-updating to manually control the spatial system and process entity state via observers. ```lua local Players = game:GetService('Players') local RunService = game:GetService("RunService") local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone, Group, Observer = QuickZone.Zone, QuickZone.Group, QuickZone.Observer -- Disable auto-update for deterministic, manual stepping (optional) QuickZone:setEnabled(false) local localPlayer = Players.LocalPlayer local characterModel = localPlayer.Character or localPlayer.CharacterAdded:Wait() -- Track this Model's physical position, but return the local player in queries QuickZone:setReference(characterModel, localPlayer) -- Add the local player to the spatial group (QuickZone tracks the mapped model automatically) local playerGroup = Group.new():add(localPlayer) local zones = Zone.fromTag('AntiGravity', { metadata = { GravityMultiplier = 0.4 } }) local gravityObserver = Observer.new({ groups = { playerGroup }, zones = { zones } }) -- Process all spatial movement and LBVH tree updates in a specific order (only needed if :setEnabled(false)) local function spatialSystem(dt) QuickZone:update(dt) end local function gravitySystem(dt) -- Instead of creating a BodyForce, we apply continuous math statelessly. for player, zone in gravityObserver:iterEntitiesInside() do local character = player.Character local hrp = character and character:FindFirstChild('HumanoidRootPart') if not hrp then continue end local meta = zone:getMetadata() local multiplier = meta and meta.GravityMultiplier or 1 local upwardForce = hrp.AssemblyMass * workspace.Gravity * (1- multiplier) hrp:ApplyImpulse(Vector3.new(0, upwardForce * dt, 0)) end end RunService.Heartbeat:Connect(function(dt) spatialSystem(dt) gravitySystem(dt) end) ``` -------------------------------- ### Update Dynamic Zones Automatically Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Enable autoSync to have QuickZone update the zone's position based on its physical reference. ```lua local truckZone = Zone.fromPart(workspace.Truck.Hitbox, { isDynamic = true, autoSync = true }) ``` ```lua local trainZone = Zone.new({ cframe = train.CabinAttachment.WorldCFrame, size = Vector3.new(15, 10, 30), shape = 'Block', reference = train.CabinAttachment, autoSync = true, -- QuickZone automatically moves the dynamic zone with the attachment every frame! metadata = { route = 'North Express' } }) ``` -------------------------------- ### Manage persistent states with Observer.observe Source: https://context7.com/ldgerrits/quickzone/llms.txt Use lifecycle callbacks to manage persistent states; the callback runs on entry and returns a cleanup function for exit. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local playerGroup = QuickZone.Group.players() local effectZone = QuickZone.Zone.fromPart(workspace.EffectZone) local observer = QuickZone.Observer.new({ groups = { playerGroup }, zones = { effectZone } }) -- Generic observation (works with any entity) local disconnect = observer:observe(function(entity, zone) print('Entered:', entity) -- Setup persistent state local highlight = Instance.new('Highlight') highlight.Parent = entity -- Return cleanup function return function() print('Exited:', entity) highlight:Destroy() end end) -- Later: stop observing disconnect() -- Player-specific observation observer:observePlayer(function(player, zone) local character = player.Character local forceField = Instance.new('ForceField') forceField.Parent = character -- Heal the player local humanoid = character:FindFirstChild('Humanoid') if humanoid and humanoid.Health < 100 then humanoid.Health = 100 end return function() if forceField and forceField.Parent then forceField:Destroy() end end end) -- LocalPlayer-specific observation (client-side only) observer:observeLocalPlayer(function(zone) local sound = workspace.Sounds.Ambience:Clone() sound.Parent = workspace sound:Play() return function() sound:Stop() sound:Destroy() end end) ``` -------------------------------- ### QuickZone Entity References Source: https://context7.com/ldgerrits/quickzone/llms.txt Map physical entities to custom references for ECS integration or custom tracking systems. ```APIDOC ## QuickZone Entity References Map physical entities to custom references for ECS integration or custom tracking systems. ### Map a physical part to a custom ID ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local transformComponent = workspace.EntityPart local entityId = 12345 QuickZone:setReference(transformComponent, entityId) -- Now callbacks will receive entityId instead of transformComponent local group = QuickZone.Group.new({ entities = { transformComponent } }) local zone = QuickZone.Zone.fromPart(workspace.Zone) local observer = QuickZone.Observer.new({ groups = { group }, zones = { zone } }) observer:onEnter(function(reference, zone) -- reference is now 12345, not the part! print('Entity ID entered:', reference) end) ``` ### Get the physical entity from a reference ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local entity = QuickZone:getEntityOfReference(entityId) print(entity) -- workspace.EntityPart ``` ### Get the reference from a physical entity ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local ref = QuickZone:getReferenceOfEntity(transformComponent) print(ref) -- 12345 ``` ### Clear the reference ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) QuickZone:setReference(transformComponent, nil) ``` ### Remove an entity from all groups ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) -- Fires exit callbacks QuickZone:removeEntity(workspace.EntityPart) ``` ``` -------------------------------- ### Enable Debug Visualization Source: https://context7.com/ldgerrits/quickzone/llms.txt Toggle visual rendering of zones in the workspace using BoxHandleAdornments to distinguish between active and inactive states. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) -- Enable debug visualization QuickZone:visualize(true) -- Zones are rendered as BoxHandleAdornments: -- - Static Active zones: one color -- - Static Inactive zones: another color -- - Dynamic Active zones: another color -- - Dynamic Inactive zones: another color -- Disable when done QuickZone:visualize(false) ``` -------------------------------- ### Handle Observer Events Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Use event listeners for logic that triggers exactly once on entry or exit. ```lua -- Individual entity events observer:onEnter(function(entity, zone) print(entity.Name .. ' entered ' .. zone:getId()) end) observer:onExit(function(entity, zone) print(entity.Name .. ' exited ' .. zone:getId()) end) -- Transition event (Fires when swapping between overlapping zones within the same observer) observer:onTransition(function(entity, newZone) print(entity.Name .. ' seamlessly moved to a new zone without leaving the area!') end) -- Group-level events observer:onGroupEnter(function(group, zone) print('The first member of group ' .. group:getId() .. ' entered!') end) observer:onGroupExit(function(group, zone) print('The last member of group ' .. group:getId() .. ' left!') end) -- Convenient player events observer:onPlayerEnter(function(player, zone) ... end) observer:onPlayerExit(function(player, zone) ... end) observer:onPlayerTransition(function(player, newZone) ... end) observer:onLocalPlayerEnter(function(zone) ... end) observer:onLocalPlayerExit(function(zone) ... end) observer:onLocalPlayerTransition(function(newZone) ... end) ``` -------------------------------- ### Create Zone from BasePart Source: https://context7.com/ldgerrits/quickzone/llms.txt Create zones directly from existing BaseParts, inheriting their properties. Supports dynamic zones with automatic or manual synchronization. Manually update zone properties like position and CFrame. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Zone = QuickZone.Zone -- Create static zone from a part local safeZonePart = workspace:WaitForChild('SafeZone') local safeZone = Zone.fromPart(safeZonePart) -- Create dynamic zone for a moving platform with auto-sync local truckZone = Zone.fromPart(workspace.Truck.Hitbox, { isDynamic = true, autoSync = true, -- Automatically tracks the part's position metadata = { VehicleType = 'Truck', Capacity = 4 } }) -- Manual sync control for precise timing local trainZone = Zone.fromPart(workspace.Train.Carriage, { isDynamic = true, autoSync = false, metadata = { Route = 'North Express' } }) -- Manually update when needed trainZone:sync() -- Syncs CFrame, Size, and Shape from the part trainZone:setPosition(Vector3.new(100, 0, 0)) trainZone:setCFrame(CFrame.new(0, 50, 0) * CFrame.Angles(0, math.rad(45), 0)) trainZone:setSize(Vector3.new(20, 10, 40)) ``` -------------------------------- ### Zone.fromTag Source: https://context7.com/ldgerrits/quickzone/llms.txt Dynamically creates and manages zones for all BaseParts with a specific CollectionService tag. ```APIDOC ## Zone.fromTag ### Description Dynamically creates and manages zones for all BaseParts with a specific CollectionService tag. Automatically updates as tagged instances are added or removed. ### Parameters #### Request Body - **tag** (string) - Required - The CollectionService tag to track. - **options** (table) - Optional - Configuration table including `isDynamic` (boolean) and `metadata` (table). ``` -------------------------------- ### Zone.fromParts Source: https://context7.com/ldgerrits/quickzone/llms.txt Creates zones from a static array of BaseParts. ```APIDOC ## Zone.fromParts ### Description Creates zones from a static array of BaseParts. Useful when you have a predefined list of parts. ### Parameters #### Request Body - **parts** (array) - Required - An array of BaseParts. - **options** (table) - Optional - Configuration table including `isDynamic` (boolean), `autoSync` (boolean), and `metadata` (table). ``` -------------------------------- ### Manage Observer Lifecycle Source: https://github.com/ldgerrits/quickzone/blob/main/docs/usage.md Use observe methods to run logic when entities enter or exit zones, returning a cleanup function for exit events. ```lua -- Generic observation observer:observe(function(entity, zone) print('Entered', entity) local highlight = Instance.new('Highlight', entity) return function() print('Exited', entity) highlight:Destroy() end end) -- The callback fires when the first entity of a group enters, and the -- returned cleanup function fires when the last entity of the group leaves. observer:observeGroup(function(group, zone) print('Group ' .. group:getId() .. ' has arrived!') local boss = workspace.Boss:Clone() boss.Parent = workspace return function() print('The group has been wiped out or left.') boss:Destroy() end end) -- Player specific observer:observePlayer(function(player, zone) local forceField = Instance.new('ForceField', player.Character) return function() forceField:Destroy() end end) -- LocalPlayer specific observer:observeLocalPlayer(function(zone) local sound = workspace.Sounds.SafeZoneAmbience sound:Play() return function() sound:Stop() end end) ``` -------------------------------- ### Track Tagged Instances with Group.fromTag() Source: https://context7.com/ldgerrits/quickzone/llms.txt Use Group.fromTag() to create a group that automatically tracks all instances with a specific CollectionService tag. Instances are added when tagged and present in the workspace, and removed when untagged or destroyed. ```lua local QuickZone = require(game.ReplicatedStorage.QuickZone) local Group = QuickZone.Group -- Track all instances tagged "NPC" local npcGroup = Group.fromTag('NPC') -- Track all instances tagged "Projectile" local projectileGroup = Group.fromTag('Projectile') -- Instances are automatically added when tagged and in workspace -- Instances are automatically removed when untagged or removed from workspace local hazardZones = QuickZone.Zone.fromTag('HazardZone') local hazardObserver = QuickZone.Observer.new({ groups = { npcGroup }, zones = { hazardZones } }) hazardObserver:onEnter(function(entity, zone) print(entity.Name .. " (NPC) entered hazard zone") end) ```