Zone tagging - the poor mans octrees!

Hello dev forum,

I wanted to find out of combining CollectionService and spatial queries was useful.
Here is the idea:

Lets say you have a collection of BaseParts / Models tagged with the tag objects, scattered around your map. Instead of running spatial queries on the entire collection of objects, you would “zone tag” each object each interval t. What i mean by that is, you will be updating a special tag associated to each object, depending on their position and a zone size variable. The objects position will then be grid-snapped (with the grid size being the zone size) and the tag will be updated. (a tag can look like “_zone130/0/130”). This process will repeat until you decide to stop it, every interval t.

zonetagging_schema

This whole thing sounded really stupid but i decided to put it to the test to see if it is of any use

Experiments

Exp. number 1:

  • Query method: GetPartBoundsInBox()
  • Query Size: 50 x 10 x 50
  • Map size: (2048,8,2048) (Standard baseplate)
  • Total parts queried: 6K
  • Zone Size: 50

Results:
---------------------------------1M queries--------100K queries
Without zone tagging : 32 seconds--------3.2 seconds
With zone tagging : 18 seconds-------------1.4 seconds

Increasing the number of parts

Exp. number 2:

  • Query method: GetPartBoundsInBox()
  • Query Size: 50 x 10 x 50
  • Map size: (2048,8,2048) (Standard baseplate)
  • Total parts queried: 40K
  • Zone Size: 50
    Results:

---------------------------------10K queries--------100K queries
Without zone tagging : 7.3 seconds--------74 seconds
With zone tagging : 0.7 seconds-------------9.8 seconds

:alarm_clock: According to the documentation GetPartBoundsInBox() considers part Size as the object bounds instead of its actual geometry, which makes it quicker and more efficient. I decided to do the next experiment with GetPartsInPart() method, which considers part geometry
(note: i only tested with block parts which might have been a mistake)


(screenshot: sort fo like a worst case scenario, a baseplate jampacked with parts)

Exp. number 3:

  • Query method: GetPartsInPart()
  • Query Size: 50 x 10 x 50
  • Map size: (2048,8,2048) (Standard baseplate)
  • Total parts queried: 40K
  • Zone Size: 50

Results:
---------------------------------10K queries--------200K queries
Without zone tagging : 6.3 seconds--------136 seconds
With zone tagging : 0.5 seconds-------------18 seconds

Honestly, dont know what to really make of this one, apart from the 200K query times being approx. double the 100k query times of the last experiment which makes sense i guess

For this next experiment, i increased the map size to 10 000 x 10 000

Exp. number 4:

  • Query method: GetPartBoundsInBox()
  • Query Size: 50 x 10 x 50
  • Map size: (10 000, 8, 10 000)
  • Total parts queried: 40K
  • Zone Size: 50

Results:
---------------------------------100K queries
Without zone tagging : 1.5 seconds
With zone tagging : 0.4 seconds

Final experiment: increasing the Query size

Exp. number 5:

  • Query method: GetPartBoundsInBox()
  • Query Size: 200 x 10 x 200
  • Map size: (10 000, 8, 10 000)
  • Total parts queried: 40K
  • Zone Size: 50

Results:
---------------------------------100K queries
Without zone tagging : 12.6 seconds
With zone tagging : 2.9 seconds

:alarm_clock: I also tried increasing/decreasing the zone size (10 - 400) but that didnt impact the results at all
:alarm_clock: Teleporting parts around randomly and tweening didnt have any impact either

Usage

This system can not be used for precise zone detection (player entering a zone triggers, area effets,…) unless you decrease the tag update interval i guess (still dont think its a good idea)
But for “grabbing” parts in a certain area it can work well.

I have attached the module below. Here are the methods:

init(trackertag : string, zonesize : number, updateinterval : number)
All of the objects tagged with trackertag will be added to the array. Instances added / removed later will also be accounted for.

addobject(object) removeobject(object)
Manually add / remove a object to the array

start() quit()
*start / stop the zone tag update loop

getzonetag(position : Vector3) → string
PositionFromTag(tag : string) → Vector3

:bell: Can be used for BaseParts or Models!

ZoneTagging.lua (4.2 KB)

2 Likes

This is absolutely… amazing!!! WHAT!

DAMN!

I want to incorporate this as a new constructor in my module SimpleZone, using the zone tags as a way to group BVH’s so it does less searching for each one

Great job dude, suuper underrated resource.

1 Like

Hey man thank you for the kind words! Let me know how it goes!

1 Like