DOTS Grid-based generation question

Hello, i am currently working on a grid-based (just code, not object) dots procedural generation project and i need some help. I want to have a system where the game “activates” grid cells(from now on “chunks”) around players and then generates them. I already have the generation done, but i need some help with the chosing what chunk it is. I dont know if cycling through the entire grid is a good idea and i want it to do this:


  1. Know which grid cells to activate (the coordinates of the chunk)
  2. Keep track which chunks are already activated so it doesnt activate those
  3. Deactivate chunks that are far away

So as i said i already have code for genereation that will activate with the activation of each chunk and i can easily add code to deactivating etc. but i dont know how to do the system that decides what is active or not and what to activate or deactivate. I thought of using native containers to store the data which chunks are active and add/remove at run time. or maybe an simple array that just changes the coordinates that are active when you move, without actually making active chunks special. Any help, tips or ideas are welcomed. Also sorry for any grammatic errors.

players moving around, loaded cells are highlighted:
players moving around

load distance:
load distance

point to cell index, cell index to point conversion:
cell index-origin conversions

Here is a code that shows general idea how you can do this (not dots-specific). It uses my NativeGrid package to simplify index finding

(you may want to install it (see Installation readme notes) OR deep copy this specific function (more work imo)).

using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
  
[ExecuteAlways]
public class GridTester : MonoBehaviour
{
    [SerializeField] float2 _gridWorldSize = new float2{ x=10 , y=10 };
    [SerializeField] int2 _gridResolution = new int2{ x=16 , y=16 };
    [SerializeField] Transform[] _players = new Transform[0];
    [SerializeField][Range(0,10)] int _loadDistance = 1;
    
    HashSet<int2> _active = new HashSet<int2>();// cells we want to be loaded
    HashSet<int2> _loaded = new HashSet<int2>();// cells that are loaded
    HashSet<int2> _redundant = new HashSet<int2>();// cells to unload
  
    void Update ()
    {
        // create list of active cells:
        _active.Clear();
        foreach( Transform player in _players )
        {
            int2 playerI2 = PointToIndex2d( player.position );
            for( int oy=-_loadDistance ; oy<=_loadDistance ; oy++ )
            for( int ox=-_loadDistance ; ox<=_loadDistance ; ox++ )
            {
                int2 i2 = playerI2 + new int2{ x=ox , y=oy };
                if( i2.x>=0 && i2.x<_gridResolution.x && i2.y>=0 && i2.y<_gridResolution.y )// bounds checks
                    _active.Add( i2 );
            }
        }
  
        // load active cells
        foreach( int2 i2 in _active )
        {
            if( !_loaded.Contains(i2) )
            {
                /*
                    insert code that loads cell's content here
                */
                _loaded.Add(i2);
            }
        }
        
        // unload redundant cells:
        _redundant.Clear();
        foreach( int2 i2 in _loaded )
            if( !_active.Contains(i2) )
                _redundant.Add(i2);
        foreach( int2 i2 in _redundant )
        {
            /*
                insert code that unloads cell's content here
            */
            _loaded.Remove(i2);
        }
    }
  
    #if UNITY_EDITOR
    void OnDrawGizmos ()
    {
        float3 cellSize = new float3{
            x = _gridWorldSize.x / _gridResolution.x ,
            y = 0f ,
            z = _gridWorldSize.y / _gridResolution.y
        };
  
        // draw grid:
        Gizmos.color = new Color{ r=1 , g=1 , b=1 , a=0.2f };
        for( int Y=0 ; Y<_gridResolution.y ; Y++ )
        for( int X=0 ; X<_gridResolution.x ; X++ )
        {
            float3 cellOriginLocal = new float3{
                x = X * cellSize.x ,
                y = 0f ,
                z = Y * cellSize.z
            };
            float3 cellCenterLocal = cellOriginLocal + cellSize*0.5f;
            Gizmos.DrawWireCube( cellCenterLocal , cellSize );
        }
        
        // draw loaded cells:
        Gizmos.color = new Color{ r=0 , g=1 , b=1 , a=0.2f };
        foreach( int2 i2 in _loaded )
        {
            float3 cellOriginLocal = new float3{
                x = i2.x * cellSize.x ,
                y = 0f ,
                z = i2.y * cellSize.z
            };
            float3 cellCenterLocal = cellOriginLocal + cellSize*0.5f;
            Gizmos.DrawCube( cellCenterLocal , cellSize );
        }
  
        // draw player positions:
        Gizmos.color = Color.cyan;
        float sphereRadius = math.min(cellSize.x,cellSize.z)*0.25f;
        foreach( Transform player in _players )
        {
            float3 pos = player.position;
            Gizmos.DrawLine( pos , new float3{ x=pos.x , y=0 , z=pos.z } );
            Gizmos.DrawSphere( pos , sphereRadius );
        }
  
        // highlight grid cell under mouse pointer:
        int2 i2Highlighted = int2.zero;
        {
            Ray ray = UnityEditor.HandleUtility.GUIPointToWorldRay( Event.current.mousePosition );
            new Plane( inNormal:transform.up , inPoint:transform.position ).Raycast( ray , out float dist );
            float3 hit = ray.origin + ray.direction*dist;
  
            Gizmos.color = new Color{ r=1 , g=1 , b=1 , a=0.35f };
            Gizmos.DrawWireSphere( hit , sphereRadius );
  
            int2 i2 = PointToIndex2d( hit );
            float3 cellOriginLocal = new float3{
                x = i2.x * cellSize.x ,
                y = 0f ,
                z = i2.y * cellSize.z
            };
            float3 cellCenterLocal = cellOriginLocal + cellSize*0.5f;
            Gizmos.DrawCube( cellCenterLocal , cellSize );
            Gizmos.DrawLine( hit , cellOriginLocal );
  
            i2Highlighted = i2;
        }
  
        // any i2 to cell origin, conversion example:
        {
            float3 cellOriginLocal = Index2dToPoint( i2Highlighted );
            Gizmos.color = Color.magenta;
            Gizmos.DrawWireSphere( cellOriginLocal , sphereRadius*0.25f );
        }
  
    }
    #endif
  
    /// <returns> Cell index2d </returns>
    public int2 PointToIndex2d ( FLOAT3 worldPoint )
    {
        return NativeGrid.PointToIndex2d(
            // point:      worldPoint.XZ()-((FLOAT3)transform.position).XZ() ,
            point:      worldPoint.XZ() ,
            worldSize:  _gridWorldSize ,
            width:      _gridResolution.x ,
            height:     _gridResolution.y
        );
    }
  
    /// <returns> Cell origin point </returns>
    public float3 Index2dToPoint ( INT2 i2 )
    {
        float2 point2d = NativeGrid.Index2dToPoint( i2:i2 , step:_gridWorldSize/_gridResolution );
        return new float3{ x=point2d.x , y=0 , z=point2d.y };
    }
  
}