SimpleZone | A simple, fast and new Zone module!

SimpleZone is a simple(r) alternative to other Zone modules such as ZonePlus.
SimpleZone offers much customizability because of its clean source code and short amount of dependencies.

Documentation:

QueryOptions

The QueryOptions object is simply something that contains information about how you want the Zone to query BaseParts, and is used when calling constructors.

You can construct a new QueryOptions object using SimpleZone.QueryOptions.new(), or simply just manually making it by using the table constructor {...}

  1. QueryOptions.FireMode :: "OnEnter"|"OnExit"|"Both"|"None"

    • All this determines is what events will be fired when an item enters or exits the Zone.
      If FireMode is "OnEnter", then Zone.ItemExited will not fire, only Zone.ItemEntered.
      Same thing for all the other FireModes.

  2. QueryOptions.TrackItemEnabled :: boolean

    • This boolean determines wether or not you are able to use Zone:TrackItem(...).
      The reason why this needs to exist is because it adds 1 more for loop to the :Update() method, which may be undesirable for people who only want the default behaviour :slight_smile:
Constructor

The SimpleZone module has 6 constructors, each of which return a Zone object.

  1. SimpleZone.fromPart(part: BasePart, queryOp: QueryOptions)

    • This constructor creates a Zone using a BasePart.
      If you want your Zone to respect certain geometries, or if you want your Zone to be able to move around with a Part, then use this!

  2. SimpleZone.fromBox(cframe: CFrame, size: Vector3, queryOp: QueryOptions?)

    • This constructor creates a Zone using a bounding box (a CFrame and a Vector3 size!)
      If you wanna save more computation time by using workspace:GetPartBoundsInBox() instead of workspace:GetPartsInPart(), then use this!

  3. SimpleZone.fromBoxes(boxes: {{cframe: CFrame, size: Vector3}}, queryOp: QueryOptions?)

    • If you wish to create a single Zone encompassing multiple bounding boxes, this is the constructor you use!
      This constructor uses a Bounding Volume Hierarchy structure for optimal efficiency when querying multiple bounding boxes.

  4. SimpleZone.fromCustom(queryFn: (params: OverlapParams?) -> {Instance}, queryOp: QueryOptions)

    • If any of the previous constructors didn’t suit your needs, then you can use SimpleZone.fromCustom(...) and provide your own custom query function!
      Make sure it returns an array of Instances.

  5. SimpleZone.new(...)

    • This is the overloaded constructor function that encompasses the first 2 constructor function.
      If you don’t wanna waste time typing out all those letters, then use this!

  6. SimpleZone.fromPartParallel(part: BasePart, queryOp: QueryOptions?)

    • This constructor is similar to SimpleZone.fromPart(), except it constructs a new Actor for dealing with spatial queries.
      Due to this, it significantly speeds things up, especially for hundreds of Zones!

And that’s basically all of the constructor functions! Now onto the methods of Zone itself.

Methods and Callbacks

The Zone object has quite a number of methods, so lets go through them one by one!

  1. Zone:BindToHeartbeat(params: OverlapParams?): ()

    • Once you’ve created your Zone using one of the 4 constructor functions, make sure to call this method to initiate automatic querying of the Zone!
      Otherwise, none of the other things would really work :sweat_smile:

  2. Zone:UnbindFromHeartbeat(): ()
    • This one is self-explanatory, really. It just stops the current heartbeat loop (if any).
      Useful if you want to pause the Zone query, maybe for a instakill area that periodically deactivates!


      Note that calling this will also clear all stored items inside the Zone.

  3. Zone:ListenTo(datatype: string, mode: "Entered"|"Exited", fn: (item: Instance) -> ()): RBXScriptConnection

    • Now, with all this Zone query stuff, you probably want to detect when something of a certain ClassName datatype enters/exits the Zone.


      And that’s exactly what this method is for!


      You can choose if fn will be called when the item enters or exits with the mode argument.

  4. Zone:SearchFor(properties: {[string]: "Tag"|any}, mode: "And"|"Or"): {BasePart}

    • This is personally my favorite method, it allows you to search the Zone for Instances matching specific properties!
      • mode argument: What is it?

        If mode is “And”, then this method will only return Instances that fully match the properties provided.
        Otherwise, if mode is “Or”, then this method will return Instances that match atleast 1 of the properties provided.
      • How is the properties table supposed to be formatted like?

        Heres the rundown of how you format the properties table to search for specific attributes, tags, children, parents, etc!

        -- properties table example
        {
        Parent_Parent_Parent_Name = "Foo" -- Yes, you can recursively search parents!
        Child1_Child2_Child3_Name = "Bar" -- Yes, you can even recursively search children!
        
        SomeProperty = 123 -- This is just for normal properties, like Anchored = true for example.
        
        SomeTagName = "Tag" -- If you want to specify that something is a tag, simply change its value to "Tag"!
        Attribute_SomeAttributeName = "Baz" -- If you want to specify that something is an attribute, simply prefix "Attribute_" before the name!
        }
        

  1. Zone.Query(params: OverlapParams?): {Instance}

    • This is a callback function that will be called anytime any of the Zones methods needs to query the Zone.
      You can change this to something else if you wish to update the query part/bounding box, for example!

  2. Zone:Update(params: OverlapParams?, onEnter: boolean?, onExit: boolean?): ()

    • All this method does is simply update the items of the Zone and fire the corresponding events based on the result of Zone.Query().
      This is the method that Zone:BindToHeartbeat() calls every heartbeat

  3. Zone:Destroy()

    • If you don’t want a Zone to exist anymore (maybe to free up some memory), use this method to completely destroy it!

  4. Zone:TrackItem(item: any)

    • If a descendant of item is found during query, item is returned instead of the descendant, similar to ZonePlus:trackItem(…).
      Note that you need to set TrackItemEnabled in the QueryOptions to true for this to work!

  5. Zone:UntrackItem(item: any)

    • Self explanatory xd

And that’s all of the methods covered! I know, quite short, right? Now, onto the events!

Events

The Zone object only has 2 events, so this should be pretty short!

  1. Zone.ItemEntered<Instance>

    • This event fires whenever a Part or Player is detected entering the Zone, and passes it as an argument to the connected function.
      However, this event fires for every type of item that enters. If you wish to narrow it down to either Parts or Players, then use Zone:GetItemSignal(...) instead!

  2. Zone.ItemExited<Instance>

    • Similar to Zone.ItemEntered, except when an item exits the Zone!

And that’s all! Now, by now you may be wondering how to use this all together. Well, I’ve put together a section going over use cases for EVERYTHING!

Example Usage
  1. A biome script!
local Zone = require(path_to_simplezone)

local biomeZone = Zone.fromBox(workspace.Biome.CFrame, workspace.Biome.Size) -- Using a Part here instead of a Model, doesn't affect anything as long as you provide the correct arguments!

biomeZone:BindToHeartbeat()

biomeZone:ListenTo("Player", "Entered", function(player: Player)
	player:SetAttribute("Biome", "ThisBiome") -- Setting a Biome attribute on the player to this biome!
end)

biomeZone:ListenTo("Player", "Exited", function(player: Player)
	player:SetAttribute("Biome", "None") -- Player has exited the zone, so theyre not in any biome!
end)
  1. A periodically-disabling instakill area!
local Zone = require(path_to_simplezone)
local QueryOptions = Zone.QueryOptions

-- We only care about the players entering the zone, dead players cant leave! XD
local options = QueryOptions.new()
options.FireMode = "OnEnter"

local killZone = Zone.fromBox(workspace.KillZone.CFrame, workspace.KillZone.Size, options)

killZone:ListenTo("Player", "Entered", function(player: Player)
	local character = player.Character
	local humanoid = character:WaitForChild("Humanoid")
	
	-- KILL THEM >:3
	humanoid.Health = 0
end)

while true do
	killZone:BindToHeartbeat()
	task.wait(10)
	-- Disable the zone for 10 seconds!
	killZone:UnbindFromHeartbeat()
	task.wait(10)
end
  1. A zone that destroys any parts that come inside it!
local Zone = require(path_to_simplezone)
local QueryOptions = Zone.QueryOptions

-- We only care about the parts entering the zone, because were destroying the parts!!!!
local options = QueryOptions.new()
options.FireMode = "OnEnter"

local killZone = Zone.fromBox(workspace.DestroyZone.CFrame, workspace.DestroyZone.Size, options)

killZone:BindToHeartbeat()

killZone:ListenTo("Part", "Entered", function(part: Part)
	part:Destroy() -- DIEEEE
end)
  1. A zone that kills anyone who exits it!
local Zone = require(path_to_simplezone)
local QueryOptions = Zone.QueryOptions

-- We only care about the players exiting the zone, players who stay inside it are good boys >:3
local options = QueryOptions.new()
options.FireMode = "OnExit"

local roomZone = Zone.fromBox(workspace.RoomBox.CFrame, workspace.RoomBox.Size, options)

roomZone:BindToHeartbeat()

roomZone:ListenTo("Player", "Exited"):Connect(function(player: Player)
	local character = player.Character
	local humanoid = character:WaitForChild("Humanoid")
	
	-- DIEIEEEEE..... AGAIN!!!
	humanoid.Health = 0
end)

Hopefully these use cases can help you see how and where to use SimpleZone!

Flaws

Unfortunately, as simple as SimpleZone is, it doesn’t come without its flaws… well, flaw, really.

Because all queries with SimpleZone are done with a .Query function (with no actual volume information being stored inside the Zone itself), that means functions like :findPoint() or :getRandomPoint() in ZonePlus aren’t able to be implemented into SimpleZone.

For the time being, SimpleZone is purely a spatial-querying module.

And that’s all! Let me know if you find any errors/bugs, and happy (late) new yeaaarr!!!

Download SimpleZone now! →
Zone.rbxm (14.0 KB)

Test place download to test the capabilities of SimpleZone →
test.rbxl (109.8 KB)

If you want some more features like :GetRandomPoint() or :IsPointWithinZone(), check out LessSimpleZone, an extension of SimpleZone! →

Was this module useful for you?

  • Yes
  • No (Please post your reason down in the replies!)

0 voters

27 Likes

:frowning:

5 Likes

Oops, I forgot to public the module! Give me a second…

1 Like

The module should be public now, enjoy!!!

2 Likes

Update: Changed the module link into a .rbxm download to utilize PackageLinks for ease of updates :slight_smile:

2 Likes

Hm. I would’ve went with the usual .PlayerEntered event format.

4 Likes

I find this cuts down on the amount of members in the object, which is ideal because it is called “SimpleZone” afterall :sweat_smile: If I added a .PlayerEntered and a .PlayerExited, then id have to do the same for parts… and then also have the .ItemEntered and .ItemExited event, that’s 6 events!!

4 Likes

Update: Added a Zone:Update(...) method so that you aren’t forced to use Zone:BindToHeartbeat()!

2 Likes

Great module! I was looking for a functioning zone module for such a long time.

3 Likes

BugReports!

1.
The path to “SimpleSignal” Is wrong in “Types”

Path = Replicated.Modules.Utility.SimpleSignal

Screenshot:
image

2.
Creating a localized queryoptions bugs due to the “table.freeze”

Screenshots:
image

Error:

2 Likes

For the first one, simply just fix it by requiring the SimpleSignal module dependency. I forgot to change it :sweat_smile:

  1. Yeah just remove the table freeze if you want, will be fixing these issues in the download soon, currently away from home
2 Likes

Thats kind of weird though, are you sure you didnt download the old version? Ive published the version with no bugs to the package link

2 Likes

The download link has been updated, I’ll look into why the PackageLink isn’t working

3 Likes

Ok so I think what you did was you downloaded the module through the marketplace, which is no longer being updated xd, also you edit the query options this way, as QueryOptions.new() does not accept any arguments:

local options = ZoneModule.QueryOptions.new()
options.FireMode = "OnEnter"

You could use parallel luau to improve performance for games with many zones, GetPartBoundsInBox can eat up performance https://youtu.be/kBUA-UTjBBQ

Ooh, good idea, I’ll add an option for this.

Give me a minute…

asdadasdadasad

Added!

This is about 40 zones, not much lag even on my bad PC (most of the lag youre seeing is from the pure act of recording xd)!

1 Like

Feel free to test more zones yourself!

I also added it into the documentation!