Free ChunkSystem module for easy 2D/3D spatial partitioning

Hello everyone :slight_smile: I made a resource today primarily for people that are newer to scripting and want to help optimize certain parts of their game.

Have you ever worked on a game and found yourself having to iterate through lots of entities at once?

  • Perhaps you’re making a game with AI entities that don’t move, but for client sided effects(sound,particles,animations, or even logic) you want to only consider entities that are close to your player’s camera. It can be pretty costly to iterate through every entity and calculate the distance to them. And kind of defeats the purpose!

  • Or perhaps you’re generating a bunch of points by choosing a random position within a field, but you need to make sure the points are more evenly distributed(aren’t too close to another existing point). Surely you don’t want to search through every point , for every candidate point.

Introducing my free little resource for you! ChunkSystem

It’s nothing fancy, but this is a pretty basic take on static spatial partitioning(dividing a space into more manageable segments basically).

There are other data structures to use, but I’ve personally found this to be useful in a variety of cases and I hope you find use with it too.

How does it work?

To create a ChunkSystem(a holder of chunks), you simply input whether you want 2 or 3 dimensions, and a Chunk Size(in studs)

local chunkSystem = ChunkSystem.new(3,100) -- Creates a holder of 100x100x100 chunks

You can then create chunks or get them with an input position. All this does is round your input position to get the corresponding chunk! It’s also fast enough to use often for most use cases.

local chunkAtCamera = ChunkSystem:GetChunk(camera.CFrame.p,true) -- The second variable will create a chunk if none is found

You can add objects to your chunk, and remove them

chunkAtCamera:AddObject(workspace.Entity);

You can get the objects added to a chunk, and also include their surrounding/adjacent chunks

local objects = chunkAtCamera:GetObjects(); -- Just gets the objects in this chunk

local objects = chunkAtCamera:GetObjects("Surrounding"); -- Includes objects in surrounding chunks

local objects = chunkAtCamera:GetObjects("Adjacent"); -- Includes objects in adjacent chunks

There’s more, but that about sums up the API.

API

API:

ChunkSystem:

ChunkSystem ChunkSystem.new(int Dimensions,int ChunkSize)

Creates a ChunkSystem with 2 or 3 dimensions, and a given ChunkSize

Chunk ChunkSystem:GetChunk(Vector3 position,boolean CreateIfNotFound)

Returns the Chunk at the given position

If a Chunk is not found, create if CreateIfNotFound is true

Chunk ChunkSystem:CreateChunk(Vector3 position)

Creates a Chunk at the given position if one does not exist

Returns the Chunk

void ChunkSystem:RemoveChunk(Vector3 position)

Removes the chunk at the given position

Chunk:

Table Chunk:GetObjects(String IncludeType)

Returns all of the objects added to this chunk

You can optionally pass in “Surrounding” or “Adjacent” for IncludeType.

This will include objects from either the adjacent(directly touching) or surrounding chunks.

Vector3 Chunk:GetPosition()

Returns the position of this chunk. For 2 dimensional chunks, y will always be 0

void Chunk:AddObject(Variant object)

Add this object to the chunk

You can only add tables or instances to chunks

void Chunk:AddObjects(Table objects)

Add multiple objects to this chunk

void Chunk:RemoveObject(Variant object)

Removes object from chunk, if it exists

boolean Chunk:ObjectExists(Variant object)

Returns whether or not the object exists in this chunk

Table Chunk:GetAdjacentChunks()

Returns a table of chunks that are directly touching this chunk

Table Chunk:GetSurroundingChunks()

Returns a table of chunks that are surrounding this chunk

And here are some example problems from before

Solving the example problems

Perhaps you’re making a game with AI entities that don’t move, but for client sided effects(sound,particles,animations, or even logic) you want to only consider entities that are close to your player’s camera. It can be pretty costly to iterate through every entity and calculate the distance to them. And kind of defeats the purpose!

local chunkSystem = ChunkSystem.new(3,50) -- 50 stud chunks
for i = 1,#entities do
    local entity = entities[i];
    if entity then
        chunkSystem:CreateChunk(entity.Position);
    end
end

-- On some loop
local chunkAtCamera = chunkSystem:GetChunk(camera.CFrame.p,true); -- Create if none found
local entitiesAroundCamera = chunkAtCamera:GetObjects("Surrounding");
for i = 1,#entitiesAroundCamera do
    local entity = entitiesAroundCamera[i];
    if entity then
        local dist = (cam.CFrame.p-entity.Position).magnitude;
        if dist < someThreshold then
            -- Do Logic!
        end
    end
end

Or perhaps you’re generating a bunch of points by choosing a random position within a field, but you need to make sure the points are more evenly distributed(aren’t too close to another existing point). Surely you don’t want to search through every point , for every candidate point.

local chunkSystem = ChunkSystem.new(2,50);
local Points = {};
local Num = 100; -- I want 100 random points that aren't too close to eachother

while #Points<Num do
       local index = RNG:NextInteger(1,#SpawnCandidates);
       local candidate = SpawnCandidates[index];
       if candidate then
           local TooCloseDist = 8; -- must be > 8 studs from another placed point!
           local candidatePosition = candidate.Position;
           local candidateChunk = chunkSystem:GetChunk(candidatePosition,true);
           local objectsFound = candidateChunk:GetObjects("Surrounding");
           
           local tooClose;
           for i = 1,#objectsFound do
                local objectFound = objectsFound[i];
                local dist = (objectFound.Position - candidatePosition).magnitude;
                if dist <= TooClose then
                    tooClose=true;
                   break;
               end
           end

           if not tooClose then  -- If they're not too close to another, place, add it to the chunk and repeat!
                candidateChunk:AddObject(candidate);
                table.remove(SpawnCandidates,index);
                table.insert(Points,candidate);
           end
       end
end

https://www.roblox.com/library/4508568715/ChunkSystem

If you’d like the source directly:

Chunk.lua (5.8 KB) ChunkSystem.lua (6.1 KB)

(Chunk must go under ChunkSystem)

65 Likes

I’m interested as to how this would respond to dynamic changes of chunks. (e.g. removing and re-creating multiple chunks when render settings are changed).

4 Likes

As long as chunks themselves aren’t supposed to move it should be fine just rewriting a new chunksystem with a larger size and re-creating the chunks.

Everything should be garbage collected automatically as long as there are no other references to oldchunksystem.Grid? : o

1 Like

how do i get this to function i have been trying but i am a bit confused where to make the chunks function. and where do i put the code that you had posted.

what about for ui???