Creating a Grid Based Chunk Loading System

Hello, I have this game idea but I don’t know how to go about achieving it.

So basically, I want to create a grid loading system similar to this: The Graveyard [HEAVEN] - Roblox

(Play it and see the loading system to better understand what I’m trying to do.)

I want to have Stands which are loaded via an infinite grid based on the users current position. The user can claim any unclaimed stand, which is saved permanently via datastore, and that stand location is claimed permanently at that location forever.

My problem is, I don’t know how I would exactly:

  1. Attempt to code a grid system which persists infinitely
  2. Get the stands specific coordinates saved and loaded so its in that specific grid location each time

I’ve already figured out how to load all of a datastores data using Datastore:ListKeysAsync(), so I have the data loading covered I just don’t know how I would load that specific grid location.

Also do you think it would be better to fetch and store ALL of the datastore data in a single module script, or periodically request the data to be fetched from the server?

Any tips or help is massively appreciated!

anyone???

1 Like

So you’ll want to “create” the map client-side, so that you’re only ever having to render the grids immediately around the player and can delete grids outside of the player’s view.

Use a rounding function on the position, so if the Character’s Position is (X: 56 and Z: 46), and each “grid” is 5x5. You can divide and round the position components so that “graves” are built at (X: 55, Z: 45), and every multiple of 5 in every direction for +/- 50 (as an example, pick whatever render distance you want). Loop through the already-built “graves” and determine if their position is greater than 50 studs away from the character’s position, then you can delete graves once they’re out of view.

I believe DataStore keys have a size limit of 4MB, which means you should be more than fine just using one key for the entire grid’s data. You could consider storing as a dictionary with X/Z indexing:

local DatastoreData = {
    [1] = { -- X: 5
        [1] = {"User1", "Text on Grave"}; -- X: 5, Z: 5
        [2] = {"User2", "Text on Grave"}; -- X: 5, Z: 10
    };
    [2] = { -- X: 10
        [1] = {"User3", "Text on Grave"}; -- X: 10, Z: 5
    };
}

Then if you need to find the data of, for example, grid position X: 10, Z: 5, you can divide both values by the grid size per axis (i.e. 5) to get X: 2, Z: 1

local Data = DatastoreData[2][1]; -- {"User3", "Text on Grave"}

Your rounding function may look something like this:

local function round(Number, RoundTo)
	return math.ceil(Number / RoundTo) * RoundTo; -- Will round up to the nearest "RoundTo"
end

If you’re using one datastore key, then you can have a single server script that periodically updates a local variable with GetAsync every 10-20 seconds or so. The client can request the grid data for their immediate surroundings using a RemoteFunction that can be called every second or so (or every time you need to render a new grid). The server script can just ‘grab’ said data and return it to the client who will use it to render the grid.

Should look something like this… You’ll have to make this fit your code and keep in mind I myself have never got a chunk loading system to fully work out … good luck!
(oh have I gotten close however)

local DataStoreService = game:GetService("DataStoreService")
local StandDataStore = DataStoreService:GetDataStore("StandDataStore")
local GridSize = 50

function GetGridCoordinates(position)
    local x = math.floor(position.X / GridSize) * GridSize
    local y = math.floor(position.Z / GridSize) * GridSize
    return Vector3.new(x, position.Y, y)
end

function SaveStandPosition(player, standPosition)
    local gridPos = GetGridCoordinates(standPosition)
    local key = "stand_"..gridPos.X.."_"..gridPos.Z
    local success, errorMessage = pcall(function()
        StandDataStore:SetAsync(key, player.UserId)
    end)
end

function LoadStandPosition(standPosition)
    local gridPos = GetGridCoordinates(standPosition)
    local key = "stand_"..gridPos.X.."_"..gridPos.Z
    local success, result = pcall(function()
        return StandDataStore:GetAsync(key)
    end)
    if success and result then
        return result
    end
    return nil
end

function OnPlayerClaimStand(player, standPosition)
    if LoadStandPosition(standPosition) == nil then
        SaveStandPosition(player, standPosition)
    end
end

game.Players.PlayerAdded:Connect(function(player)
    local playerPosition = player.Character and player.Character.PrimaryPart.Position or Vector3.zero
    for x = -5, 5 do
        for y = -5, 5 do
            local offset = Vector3.new(x * GridSize, 0, y * GridSize)
            local standPosition = playerPosition + offset
            local owner = LoadStandPosition(standPosition)
            if owner then
                print("Stand at " .. standPosition .. " is owned by player " .. owner)
            end
        end
    end
end)

For some reason using one key didn’t even cross my mind that you so much for the help!

1 Like