{"id":26387197,"url":"https://github.com/ven0maus/flowvitae","last_synced_at":"2025-03-17T08:30:21.237Z","repository":{"id":61066110,"uuid":"546812793","full_name":"Ven0maus/FlowVitae","owner":"Ven0maus","description":"Efficient library for managing 2D static and procedural grids in games.","archived":false,"fork":false,"pushed_at":"2024-04-25T20:40:36.000Z","size":615,"stargazers_count":7,"open_issues_count":3,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-25T21:39:16.995Z","etag":null,"topics":["2d","cells","chunked","chunking","chunks","flowvitae","game","games","generation","grid","infinite","library","memory-efficient","monogame","procedural","procgen","rendering","sadconsole","tiles"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ven0maus.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-10-06T17:31:14.000Z","updated_at":"2023-10-03T07:30:52.000Z","dependencies_parsed_at":"2024-01-17T22:56:58.353Z","dependency_job_id":"1a95ba63-9678-4eb7-81c6-5a8a5b77d974","html_url":"https://github.com/Ven0maus/FlowVitae","commit_stats":null,"previous_names":["venom0us/flowvitae"],"tags_count":26,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ven0maus%2FFlowVitae","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ven0maus%2FFlowVitae/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ven0maus%2FFlowVitae/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ven0maus%2FFlowVitae/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ven0maus","download_url":"https://codeload.github.com/Ven0maus/FlowVitae/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243999605,"owners_count":20381387,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["2d","cells","chunked","chunking","chunks","flowvitae","game","games","generation","grid","infinite","library","memory-efficient","monogame","procedural","procgen","rendering","sadconsole","tiles"],"created_at":"2025-03-17T08:30:13.233Z","updated_at":"2025-03-17T08:30:21.089Z","avatar_url":"https://github.com/Ven0maus.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FlowVitae\nFlowVitae is a memory and performance efficient 2D grid library designed for small to large scale procedural worlds.\nCan be easily integrated with most render engines.\n\nSupports:\n- net7.0\n- net6.0\n- netstandard2.1\n\nTested with:\n- SadConsole\n- MonoGame\n\n# Features\n\n**Different grid layouts**\n- Static grid\n- Static chunked grid\n- Procedural chunked grid\n\n**Infinite chunking terrain**\n- Chunking is automatically done\n- Viewport and chunk size is configurable\n- Method to center viewport on a coordinate, (this handles the chunking)\n- Procedural generation algorithm can be passed straight to the Grid\n\n**Easy to use**\n- Possible to configure custom Grid, Cell, ProceduralGeneration classes\n- Has some visualizer projects that serves as examples on how to integrate with a render engine, such as SadConsole or Unity.\n\n# Setup\n**FlowVitae grids use 2 generic types**\n- 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)\n- TCell is the real cell that represents the TCellType, it uses the ICell\u003cTCellType\u003e interface\n\n**FlowVitae provides some basic implementations already out of the box.**\n```csharp\nGrid\u003cTCellType, TCell\u003e\nCell\u003cTCellType\u003e\n```\n\n**Static Grid Creation**\n```csharp\nvar grid = new Grid\u003cint, Cell\u003cint\u003e\u003e(width, height);\n```\n\n**Static Chunked Grid Creation**\n```csharp\nvar staticGen = new StaticGenerator\u003cint, Cell\u003cint\u003e\u003e(baseMap, width, height, outOfBoundsCellType);\nvar grid = new Grid\u003cint, Cell\u003cint\u003e\u003e(width, height, chunkWidth, chunkHeight, staticGen, extraChunkRadius = 1);\n```\n\nThe baseMap here represents the full static grid.\n\n**Procedural Grid Creation**\n```csharp\nvar procGen = new ProceduralGenerator\u003cint, Cell\u003cint\u003e\u003e(Seed, GenerateChunkMethod);\nvar grid = new Grid\u003cint, Cell\u003cint\u003e\u003e(width, height, chunkWidth, chunkHeight, procGen, extraChunkRadius = 1);\n```\n\nGenerateChunkMethod can look something like this:\n```csharp\npublic void GenerateChunkMethod(Random random, int[] chunk, int width, int height, (int x, int y) chunkCoordinate)\n{\n\t// Every position contains default value of int (0) which could represent grass\n\tfor (int x = 0; x \u003c width; x++)\n\t{\n\t\tfor (int y = 0; y \u003c height; y++)\n\t\t{\n\t\t\t// Add a random chance for a cell to be a tree\n\t\t\tif (random.Next(0, 100) \u003c 10)\n\t\t\t\tchunk[y * width + x] = 1;\n\t\t}\n\t}\n}\n```\nThe random already has a unique seed based on the provided Seed in the ProceduralGenerator and the chunk coordinate.\nint[] chunk represent the chunk, int[] will be your TCellType[]\nand the chunkCoordinate is provided too, in case you want to sample noise based on coordinates.\n\nChunks are generated automatically and they will use this method as reference to build the chunk.\n\n# Setting custom chunk data\t\nIt is possible to set custom data, per chunk which can be directly retrieved from the grid.\nThis custom data, can be any class that implements IChunkData interface. An example implementation:\n```csharp\ninternal class TestChunkData : IChunkData\n{\n\tpublic int Seed { get; set; }\n\tpublic List\u003c(int x, int y)\u003e? Trees { get; set; }\n}\n// Custom chunk generation implementation\nFunc\u003cRandom, int[], int, int, TestChunkData\u003e chunkGenerationMethod = (random, chunk, width, height, chunkCoordinate) =\u003e\n{\n\t// Define custom chunk data\n\tvar chunkData = new TestChunkData\n\t{\n\t\tTrees = new List\u003c(int x, int y)\u003e()\n\t};\n\tfor (int x = 0; x \u003c width; x++)\n\t{\n\t\tfor (int y = 0; y \u003c height; y++)\n\t\t{\n\t\t\tchunk[y * width + x] = random.Next(-10, 10);\n\t\t\t// Every 0 value is a tree, lets keep it for easy pathfinding access\n\t\t\tif (chunk[y * width + x] == 0)\n\t\t\t\tchunkData.Trees.Add((x, y));\n\t\t}\n\t}\n\treturn chunkData;\n};\n\n// Initialize the custom implementations\nvar customProcGen = new ProceduralGenerator\u003cint, Cell\u003cint\u003e, TestChunkData\u003e(Seed, chunkGenerationMethod);\nvar customGrid = new Grid\u003cint, Cell\u003cint\u003e, TestChunkData\u003e(ViewPortWidth, ViewPortHeight, ChunkWidth, ChunkHeight, customProcGen, extraChunkRadius = 1);\n\n// Retrieve the chunk data, for the whole chunk where position (5, 5) resides in\nvar chunkData = customGrid.GetChunkData(5, 5);\nConsole.WriteLine(\"Trees in chunk: \" + chunkData.Trees != null ? chunkData.Trees.Count : 0);\n```\nYou can store the chunkdata within the internal cache buffer, which GetChunkData will then return instead\n```csharp\ncustomGrid.StoreChunkData(chunkData);\ncustomGrid.RemoveChunkData(chunkData, reloadChunk); // chunkdata only refreshes after chunk is reloaded\n```\n\nThis works similar for Static chunked grids, you can pass along a chunk data generation method to the constructor.\n```csharp\n// chunkGenerationMethod signature: (seed, baseMap, width, height, chunkCoordinate)\nnew StaticGenerator\u003cint, Cell\u003cint\u003e, TestChunkData\u003e(_baseMap, Grid.Width, Grid.Height, NullCell, chunkGenerationMethod, extraChunkRadius = 1)\n```\n\n# Rendering to a render engine\nFlowVitae provides an event that is raised when a cell on the viewport is updated\n```csharp\ngrid.OnCellUpdate += Grid_OnCellUpdate;\n\nprivate void Grid_OnCellUpdate(object? sender, CellUpdateArgs\u003cint, Cell\u003cint\u003e\u003e args)\n{\n  // Pseudo code\n  var screenGraphic = ConvertCellTypeToGraphic(args.Cell.CellType);\n  SomeRenderEngine.SetScreenGraphic(args.ScreenX, args.ScreenY, screenGraphic);\n}\n```\nThis event is by default only raised when the TCellType value on the viewport is changed during a SetCell/SetCells\nIf you want this event to always be raised when a TCell is set, (even if CellType doesn't change, but some properties do)\nThen we also provided this functionality. You can adjust it like so:\n```csharp\ngrid.RaiseOnlyOnCellTypeChange = false;\n```\n\n# Custom cell conversion\nThere are some ways to convert the underlying TCellType to TCell.\n- You can implement your own Grid class based on the GridBase\u003cTCellType, TCell\u003e and override the Convert method.\n- You can call the method SetCustomConverter(converter) on the Grid which is then used in Convert instead of default new() constructor\n\t\nHere is an example of the method:\n```csharp\n_grid.SetCustomConverter(ConvertCell);\n\t\npublic Cell\u003cint\u003e ConvertCell(int x, int y, int cellType)\n{\n\tswitch (cellType)\n\t{\n\t\tcase 0:\n\t\t\treturn new Cell\u003cint\u003e(x, y, cellType);\n\t\tcase 1:\n\t\t\treturn new Cell\u003cint\u003e(x, y, walkable: false, cellType);\n\t\tdefault:\n\t\t\treturn new Cell\u003cint\u003e(x, y, walkable: false, cellType);\n\t}\n}\n```\n\t\n# Creating your own Cell implementation\nIt can be easily done by inheriting from CellBase or ICell\u003cTCellType\u003e\n\t\nWhen you want your cell to be able to base of some render engine cell such as ColoredGlyph from Sadconsole,\nyou can easily do it by using ColoredGlyph, ICell\u003cTCellType\u003e as your inheritance.\n\t\nHere is an example of just a regular CellBase inheritance:\n```csharp\ninternal class VisualCell\u003cTCellType\u003e : CellBase\u003cTCellType\u003e\nwhere TCellType : struct\n{\n\tpublic bool Walkable { get; set; } = true;\n\tpublic bool BlocksFieldOfView { get; set; } // Some custom properties\n\tpublic bool HasLightSource { get; set; } // Some custom properties\n\n\tpublic VisualCell() { }\n\n\tpublic VisualCell(int x, int y, TCellType cellType)\n\t{\n\t\tX = x;\n\t\tY = y;\n\t\tCellType = cellType;\n\t}\n}\n```\n\n# Interaction with grids\n\n**Getting and setting cells**\n```csharp\nvar cell = grid.GetCell(x, y); // returns TCell\nvar cellType = grid.GetCelLType(x,y); // returns TCellType\nvar neighbors = grid.GetNeighbors(x, y, AdjacencyRule);\ngrid.SetCell(x, y, cellType, storeState);\ngrid.SetCell(cell, storeState);\nvar cells = grid.GetCells(new [] {(0,0), (1,1)}); // returns collection of TCell\ngrid.SetCells(cells, storeState);\ngrid.RemoveStoredCell(x, y);\ngrid.HasStoredCell(x, y);\n```\n\n**Center viewport on a coordinate for procedural grids**\n\t\nThis is especially useful when you want your player to always be centered in the middle of the screen.\nBut during movement, the viewport adjusts to show the right cells based on the position of the player\nFor this you can use the Center(x, y) method Grid provides. This method is also what controls the chunk loading.\n```csharp\n// Pseudo code (make sure player doesn't actually move, or you'll end up with desync)\nif (player.MovedTowards(x, y))\n    grid.Center(x, y);\n```\n\n**Retrieve all cells within the viewport**\n```csharp\n// Returns all world positions that are within the current viewport\ngrid.GetViewPortWorldCoordinates();\ngrid.GetViewPortWorldCoordinates(cellType =\u003e cellType == 1 || cellType == 2); // with custom criteria\n```\n\n**Checking bounds for static grids**\n```csharp\n// Returns true or false if the position is within the viewport\n// Works only for screen coordinates if you're using a chunked grid\nvar isInBounds = grid.InBounds(x, y);\n```\n\n**See if a cell is currently displayed on the viewport**\n```csharp\nvar isInViewPort = grid.IsWorldCoordinateOnViewPort(x,y);\n```\n\n**Reset grid state**\n\n```csharp\ngrid.ClearCache(); // Removes all stored cell data\ngrid.RemoveStoredCell(x, y);\n```\n\n**Resize grid viewport**\n\nThis will resize the surface of the screen, reinitialize all the chunks and send out render updates for the new screen surface.\n```csharp\ngrid.ResizeViewport(width, height);\n```\n\n**Be notified of main chunk loading/unloading**\n\nFollowing events will be raised when one of the chunks around the center chunk (center chunk included) gets loaded or unloaded.\n```csharp\nOnChunkLoad\nOnChunkUnload\n```\n\nSome chunk related methods: (x, y) is automatically converted to a chunk coordinate, so it can take any world position.\n```csharp\nGrid.GetChunkSeed(x, y);\nGrid.IsChunkLoaded(x, y);\nGrid.GetChunkCoordinate(x, y);\nGrid.GetChunkCellCoordinates(x, y);\nGrid.GetLoadedChunkCoordinates();\n```\n\n# Integration with SadConsole / MonoGame\n\nCheckout the SadConsoleVisualizer project, it is an example project that integrates the FlowVitae grid with SadConsole and MonoGame.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fven0maus%2Fflowvitae","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fven0maus%2Fflowvitae","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fven0maus%2Fflowvitae/lists"}