Venomaus.FlowVitae 1.2.0

.NET 5.0 .NET Standard 2.1
There is a newer version of this package available.
See the version list below for details.
dotnet add package Venomaus.FlowVitae --version 1.2.0
NuGet\Install-Package Venomaus.FlowVitae -Version 1.2.0
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Venomaus.FlowVitae" Version="1.2.0" />
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Venomaus.FlowVitae --version 1.2.0
#r "nuget: Venomaus.FlowVitae, 1.2.0"
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
// Install Venomaus.FlowVitae as a Cake Addin
#addin nuget:?package=Venomaus.FlowVitae&version=1.2.0

// Install Venomaus.FlowVitae as a Cake Tool
#tool nuget:?package=Venomaus.FlowVitae&version=1.2.0


FlowVitae is a memory and performance efficient 2D grid library designed for small to large scale procedural worlds. Can be easily integrated with most render engines.


  • net6.0
  • net5.0
  • netstandard2.1

Tested with:

  • SadConsole V9


Different grid layouts

  • Static grid (no chunking)
  • Procedural grid (chunking)

Infinite chunking terrain

  • Chunking is automatically done
  • Viewport and chunk size is configurable
  • Method to center viewport on a coordinate
  • Procedural generation algorithm can be passed straight to the Grid

Easy to use

  • Possible to configure custom Grid, Cell, ProceduralGeneration classes
  • Has a visualizer project that serves as an example on how to integrate with a render engine, such as SadConsole.


FlowVitae grids use 2 generic types

  • TCellType is constrained to a struct, and will represent the unique cell value kept in memory. (eg, an int or byte that points to the real cell id)
  • TCell is the real cell that represents the TCellType, it uses the ICell<TCellType> interface

FlowVitae provides some basic implementations already out of the box.

Grid<TCellType, TCell>

Static Grid Creation

var grid = new Grid<int, Cell<int>>(width, height);

Procedural Grid Creation

var procGen = new ProceduralGenerator<int, Cell<int>>(Seed, GenerateChunkMethod);
var grid = new Grid<int, Cell<int>>(width, height, chunkWidth, chunkHeight, procGen);

GenerateChunkMethod can look something like this:

public void GenerateChunkMethod(Random random, int[] chunk, int width, int height)
	// Every position contains default value of int (0) which could represent grass
	for (int x = 0; x < width; x++)
		for (int y = 0; y < height; y++)
			// Add a random chance for a cell to be a tree
			if (random.Next(0, 100) < 10)
				chunk[y * width + x] = 1;

The random already has a unique seed based on the provided Seed in the ProceduralGenerator and the chunk coordinate. int[] chunk represent the chunk, int[] will be your TCellType[]

Chunks are generated automatically and they will use this method as reference to build the chunk.

Setting custom chunk data

It is possible to set custom data, per chunk which can be directly retrieved from the grid. This custom data, can be any class that implements IChunkData interface. An example implementation:

internal class TestChunkData : IChunkData
	public int Seed { get; set; }
	public List<(int x, int y)>? Trees { get; set; }
// Custom chunk generation implementation
Func<Random, int[], int, int, TestChunkData> chunkGenerationMethod = (random, chunk, width, height) =>
	// Define custom chunk data
	var chunkData = new TestChunkData
		Trees = new List<(int x, int y)>()
	for (int x = 0; x < width; x++)
		for (int y = 0; y < height; y++)
			chunk[y * width + x] = random.Next(-10, 10);
			// Every 0 value is a tree, lets keep it for easy pathfinding access
			if (chunk[y * width + x] == 0)
				chunkData.Trees.Add((x, y));
	return chunkData;

// Initialize the custom implementations
var customProcGen = new ProceduralGenerator<int, Cell<int>, TestChunkData>(Seed, chunkGenerationMethod);
var customGrid = new Grid<int, Cell<int>, TestChunkData>(ViewPortWidth, ViewPortHeight, ChunkWidth, ChunkHeight, customProcGen);

// Retrieve the chunk data, for the whole chunk where position (5, 5) resides in
var chunkData = customGrid.GetChunkData(5, 5);
Console.WriteLine("Trees in chunk: " + chunkData.Trees != null ? chunkData.Trees.Count : 0);

You can store the chunkdata within the internal cache buffer, which GetChunkData will then return instead

customGrid.RemoveChunkData(chunkData, reloadChunk); // chunkdata only refreshes after chunk is reloaded

Rendering to a render engine

FlowVitae provides an event that is raised when a cell on the viewport is updated

grid.OnCellUpdate += Grid_OnCellUpdate;

private void Grid_OnCellUpdate(object? sender, CellUpdateArgs<int, Cell<int>> args)
  // Pseudo code
  var screenGraphic = ConvertCellTypeToGraphic(args.Cell.CellType);
  SomeRenderEngine.SetScreenGraphic(args.ScreenX, args.ScreenY, screenGraphic);

This event is by default only raised when the TCellType value on the viewport is changed during a SetCell/SetCells If you want this event to always be raised when a TCell is set, (even if CellType doesn't change, but some properties do) Then we also provided this functionality. You can adjust it like so:

grid.RaiseOnlyOnCellTypeChange = false;

Custom cell conversion

There are some ways to convert the underlying TCellType to TCell.

  • You can implement your own Grid class based on the GridBase<TCellType, TCell> and override the Convert method.
  • You can call the method SetCustomConverter(converter) on the Grid which is then used in Convert instead of default new() constructor

Here is an example of the method:

public Cell<int> ConvertCell(int x, int y, int cellType)
	switch (cellType)
		case 0:
			return new Cell<int>(x, y, cellType);
		case 1:
			return new Cell<int>(x, y, walkable: false, cellType);
			return new Cell<int>(x, y, walkable: false, cellType);

Creating your own Cell implementation

It can be easily done by inheriting from CellBase or ICell<TCellType>

When you want your cell to be able to base of some render engine cell such as ColoredGlyph from Sadconsole, you can easily do it by using ColoredGlyph, ICell<TCellType> as your inheritance.

Here is an example of just a regular CellBase inheritance:

internal class VisualCell<TCellType> : CellBase<TCellType>
where TCellType : struct
	public bool Walkable { get; set; } = true;
	public bool BlocksFieldOfView { get; set; } // Some custom properties
	public bool HasLightSource { get; set; } // Some custom properties

	public VisualCell() { }

	public VisualCell(int x, int y, TCellType cellType)
		X = x;
		Y = y;
		CellType = cellType;

Interaction with grids

Getting and setting cells

var cell = grid.GetCell(x, y); // returns TCell
var cellType = grid.GetCelLType(x,y); // returns TCellType
grid.SetCell(x, y, cellType, storeState);
grid.SetCell(cell, storeState);
var cells = grid.GetCells(new [] {(0,0), (1,1)}); // returns collection of TCell
grid.SetCells(cells, storeState);
grid.GetNeighbors(x, y, AdjacencyRule);

Center viewport on a coordinate for procedural grids

This is especially useful when you want your player to always be centered in the middle of the screen. But during movement, the viewport adjusts to show the right cells based on the position of the player For this you can use the Center(x, y) method Grid provides.

// Pseudo code (make sure player doesn't actually move, or you'll end up with desync)
if (player.MovedTowards(x, y))
    grid.Center(x, y);

Retrieve all cells within the viewport

// Returns all world positions that are within the current viewport
grid.GetViewPortWorldCoordinates(cellType => cellType == 1 || cellType == 2); // with custom criteria

Checking bounds for static grids

// Returns true or false if the position is within the viewport
// Works only for screen coordinates if you're using a chunked grid
var isInBounds = grid.InBounds(x, y);

See if a cell is currently displayed on the viewport

var isInViewPort = grid.IsWorldCoordinateOnViewPort(x,y);

Reset grid state

grid.ClearCache(); // Removes all stored cell data

Be notified of main chunk loading/unloading


Following events will be raised when one of the chunks around the center chunk (center chunk included) gets loaded or unloaded.

Grid.GetChunkSeed(x, y);

Returns the unique seed for the chunk where the specified cell coordinate is in.

Grid.IsChunkLoaded(x, y);

Returns true or false based on the chunk the coordinate resides in is loaded or not

Integration with SadConsole

Checkout the Visualizer project, it is an example project that integrates the FlowVitae grid with SadConsole V9 render engine.

Product Compatible and additional computed target framework versions.
.NET net5.0 is compatible.  net5.0-windows was computed.  net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed. 
.NET Core netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.1 is compatible. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Additional computed target framework(s)
Learn more about Target Frameworks and .NET Standard.
  • .NETStandard 2.1

    • No dependencies.
  • net5.0

    • No dependencies.
  • net6.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
1.3.2 50 9/27/2023
1.3.1 318 11/10/2022
1.3.0 283 11/9/2022
1.2.6 300 11/9/2022
1.2.5 287 11/8/2022
1.2.4 288 11/8/2022
1.2.3 341 10/17/2022
1.2.2 336 10/16/2022
1.2.1 340 10/16/2022
1.2.0 336 10/16/2022
1.1.0 340 10/15/2022