Can't use negative x or z values

The reason here is that the grid is positioned at the origin, so when you calculate the grid position from world position, your world position needs to be in the positive range. You could introduce an ‘offset’ that is added or subtracted to and from the world position to ‘translate’ the coordinates back and forth.

For example, you could have a 10x10 grid with an offset of (-5, -5). This essentially then means the grid is centered on the origin (somewhat) of the world, and all world positions from (-5,-5) to (5,5) would be valid (barring some rounding errors). You’d also need to take this into account when checking positions, etc. because, while the grid’s width and height may be 10, a world position of (8, 8) will no longer be valid in this scenario. A grid position of (0, 0) will now be at (-5, -5) in the world as opposed to the (0, 0) world position in the current system.

For the GridSystem you may have an overloaded constructor that takes an offset (optional) and store it (All this code is adapted from the Turn-Based Strategy course’s code)

private Vector3 offset;

public GridSystem(int width, int height, float cellSize, Func<GridSystem<TGridObject>, GridPosition, TGridObject> createGridObject)
    : this(width, height, cellSize, Vector3.zero, createGridObject) { } // pass Vector3.zero as the defaul offset

public GridSystem(int width, int height, float cellSize, Vector3 offset, Func<GridSystem<TGridObject>, GridPosition, TGridObject> createGridObject)
{
    this.offset = offset;
    // all the normal grid constructor code
}

This offset adjusts the calculation only - the grid’s indices still start at 0. What those indices are in relation to the world is just calculated differently now.

    public Vector3 GetWorldPosition(GridPosition gridPosition)
    {
        // calculate as normal, then translate to 'offset'
        return new Vector3(gridPosition.x, 0, gridPosition.z) * cellSize + offset;
    }

    public GridPosition GetGridPosition(Vector3 worldPosition)
    {
        // translate back to 0 (remove offset) before calculating as normal
        Vector3 translated = worldPosition - offset;
        return new GridPosition(
            Mathf.RoundToInt(translated.x / cellSize),
            Mathf.RoundToInt(translated.z / cellSize)
        );
    }