H073.HxTiles 0.2.0

Prefix Reserved
dotnet add package H073.HxTiles --version 0.2.0
                    
NuGet\Install-Package H073.HxTiles -Version 0.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="H073.HxTiles" Version="0.2.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="H073.HxTiles" Version="0.2.0" />
                    
Directory.Packages.props
<PackageReference Include="H073.HxTiles" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add H073.HxTiles --version 0.2.0
                    
#r "nuget: H073.HxTiles, 0.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.
#:package H073.HxTiles@0.2.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=H073.HxTiles&version=0.2.0
                    
Install as a Cake Addin
#tool nuget:?package=H073.HxTiles&version=0.2.0
                    
Install as a Cake Tool

HxTiles — High-Performance Grid Engine for .NET

HxTiles is a fast, zero-allocation grid engine for .NET. Generic storage, coordinate layouts, spatial queries, pathfinding, chunked infinite worlds, and serialization — all in pure C# with zero dependencies.

Built for anything that lives on a grid: games, simulations, robotics, spatial analysis, data visualization, warehouse logistics, floor planning, or scientific computing.


Features

  • Generic GridsTileMap<T> and TileMap3D<T> with any struct as cell data
  • Multi-Layer — Stack multiple layers per map (terrain, objects, metadata)
  • Coordinate Layouts — Orthogonal, isometric, staggered, hexagonal (pointy & flat)
  • Spatial Queries — DDA raycast, flood fill, Bresenham lines, circle/diamond area queries
  • Pathfinding — A* with pluggable cost provider, diagonal support, interface for custom algorithms
  • Chunked Worlds — Infinite streaming grids with dual-radius load/unload, chunk pooling, dirty tracking
  • Serialization — Custom serializer interface + blittable fast-path (direct memory copy)
  • Auto-Tiling — Bitmask-based tile resolution for automatic neighbor-aware tile selection
  • 3D Grid Picking — Project any grid onto a plane in 3D space, pick tiles with view-projection matrix or ray
  • 2D Camera Picking — Simple screen-to-tile with camera offset + zoom, no matrix math needed
  • Frustum Culling — Compute visible tile bounds from 3D or 2D camera for efficient rendering
  • Zero Allocation — Ref struct enumerators, Span-based APIs, struct generics, pooled arrays
  • AOT Compatible — No reflection, works with Native AOT and trimming

Installation

dotnet add package H073.HxTiles
<PackageReference Include="H073.HxTiles" Version="0.2.0" />

Quick Start

using HxTiles;

// Define your cell type — any struct works
struct Cell
{
    public float Temperature;
    public bool Blocked;
}

// Create a 256x256 grid with 2 layers
var grid = new TileMap<Cell>(256, 256, layers: 2);

// Set and get by reference (zero-copy mutation)
grid.Set(10, 20, new Cell { Temperature = 36.5f });
ref var cell = ref grid.Get(10, 20);
cell.Temperature += 0.1f; // mutate in place

// Out-of-bounds behavior
grid.Mode = BoundsMode.Wrap;   // toroidal grid
grid.Mode = BoundsMode.Clamp;  // clamp to edges

Coordinate Layouts

Convert between grid coordinates and world/screen positions. All layouts are structs — the JIT devirtualizes and inlines the calls.

// Rectangular grid — UI dashboards, image processing, warehouse floors
var ortho = new OrthogonalLayout(16f, 16f);

// Diamond isometric — tactical maps, 2.5D visualization
var iso = new IsometricLayout(64f, 32f);

// Hexagonal — simulations, board games, territory mapping
var hex = new HexLayout(16f, HexOrientation.PointyTop);

// Staggered — classic map projections
var stag = new StaggeredLayout(32f, 16f);

// Convert between coordinate systems
ortho.TileToWorld(5, 3, out float wx, out float wy);
TileCoord tile = ortho.WorldToTile(wx, wy); // (5, 3)

// Get neighbors (4 cardinal, 8 with diagonals, 6 hex)
Span<TileCoord> neighbors = stackalloc TileCoord[6];
int count = hex.GetNeighbors(new TileCoord(3, 3), neighbors);

Spatial Queries

All queries are zero-allocation ref struct enumerators.

// DDA raycast — line of sight, sensor beams, collision detection
foreach (var hit in GridRaycast.Cast(originX, originY, dirX, dirY, maxSteps: 100))
{
    if (grid.Get(hit).Blocked) break;
}

// Raycast between two points
foreach (var hit in GridRaycast.CastBetween(sensorPos, targetPos))
    ProcessHit(hit);

// Flood fill — region detection, connected components, zone painting
var region = new List<TileCoord>();
FloodFill.Fill(grid, start, cell => !cell.Blocked, region);

// Bresenham line — draw paths, connect points, interpolate
foreach (var coord in LineDrawing.Line(from, to))
    grid.Set(coord, new Cell { Blocked = true });

// Area queries — range searches, blast radius, influence zones
foreach (var coord in AreaQuery.Circle(center, radius: 10))
    grid.Get(coord).Temperature += 5f;

foreach (var coord in AreaQuery.Diamond(center, radius: 3))
    Process(coord);

// Bounded rectangle query
var bounds = AreaQuery.Rectangle(minX: 10, minY: 10, width: 20, height: 20);

Pathfinding

Pluggable cost system — you define what "walkable" and "cost" mean for your data.

// Define cost provider as a struct (JIT inlines the calls)
struct MoveCost : IPathCostProvider<Cell>
{
    public bool IsWalkable(in Cell cell) => !cell.Blocked;
    public float GetCost(in Cell cell) => 1f;
}

// A* pathfinder
var pathfinder = new AStarPathfinder<Cell, MoveCost>(new MoveCost());
var result = pathfinder.FindPath(grid, start, goal);

if (result.Found)
{
    Console.WriteLine($"Path length: {result.Length}, cost: {result.TotalCost}");
    foreach (var step in result.Path)
        Console.WriteLine(step);
}

// Weighted costs — prefer roads, avoid rough terrain
struct TerrainCost : IPathCostProvider<Cell>
{
    public bool IsWalkable(in Cell cell) => !cell.Blocked;
    public float GetCost(in Cell cell) => cell.Temperature > 100f ? 5f : 1f;
}

// Custom algorithm? Implement the interface:
// class MyDijkstra : IPathfinder<Cell> { ... }

Chunked Worlds

Infinite grids backed by uniform chunks. Distance-based load/unload with dual radius to prevent thrashing. Thread-safe.

struct MyProvider : IChunkProvider<Cell>
{
    public void LoadChunk(ChunkCoord coord, Span<Cell> data, int chunkSize)
    {
        // Generate procedurally, load from disk, fetch from network...
        for (int i = 0; i < data.Length; i++)
            data[i] = new Cell { Temperature = 20f };
    }

    public void SaveChunk(ChunkCoord coord, ReadOnlySpan<Cell> data, int chunkSize)
    {
        // Persist to disk, database, cloud...
    }

    public void OnChunkUnloaded(ChunkCoord coord) { }
}

// Create infinite world (32x32 chunks, load radius 3, keep radius 5)
using var world = new ChunkedMap<Cell>(new MyProvider(), chunkSize: 32);

// Move the center — chunks load/unload automatically
world.UpdateCenter(new TileCoord(50000, 70000));

// Access tiles transparently by world coordinates
ref var cell = ref world.Get(50000, 70000);

// Persist dirty chunks
world.SaveAllDirty();

// Events
world.ChunkLoaded += coord => Console.WriteLine($"Loaded {coord}");
world.ChunkUnloaded += coord => Console.WriteLine($"Unloaded {coord}");

Serialization

Stream-based — you decide where data goes (file, network, memory).

// Custom serializer for full control
struct CellSerializer : ITileSerializer<Cell>
{
    public void Write(BinaryWriter w, in Cell c) { w.Write(c.Temperature); w.Write(c.Blocked); }
    public Cell Read(BinaryReader r) => new() { Temperature = r.ReadSingle(), Blocked = r.ReadBoolean() };
}

using var stream = File.Create("grid.hxt");
TileMapSerializer.Write(stream, grid, new CellSerializer());

// Blittable fast-path — direct memory copy, maximum speed
TileMapSerializer.WriteBlittable(stream, grid);
var loaded = TileMapSerializer.ReadBlittable<Cell>(stream);

Grid Picking & Projection

Place any tile grid in 3D space and pick tiles from screen coordinates. Works with all layouts — orthogonal, isometric, hex, staggered.

using System.Numerics;

// Define where the grid lives in 3D space
var plane = GridPlane.XZ();           // horizontal floor (most common)
var wall  = GridPlane.XY();           // vertical wall
var ramp  = new GridPlane(            // custom orientation
    origin: Vector3.Zero,
    axisX: Vector3.UnitX,
    axisY: Vector3.Normalize(new Vector3(0, 1, 1)));

// Create a projection — binds plane + layout + optional bounds
var grid = new GridProjection<OrthogonalLayout>(
    GridPlane.XZ(),
    new OrthogonalLayout(32f, 32f),
    map.Bounds);                      // omit for ChunkedMap (infinite)

// 3D picking — click screen, get tile (uses view-projection matrix)
PickResult result = grid.Pick(mouseX, mouseY, vpWidth, vpHeight, viewProjection);
if (result.Hit)
{
    TileCoord tile  = result.Tile;        // which tile
    float worldX    = result.WorldX;      // exact 2D position (sub-tile)
    float worldY    = result.WorldY;
    float distance  = result.Distance;    // for depth-sorting multiple grids
    Vector3 point3D = result.HitPoint3D;  // exact 3D world position
}

// Or pick from a ray (e.g. from a physics engine)
var result = grid.Pick(rayOrigin, rayDirection);

// 2D picking — no matrix, just camera offset + zoom
var result = grid.Pick(mouseX, mouseY, vpWidth, vpHeight, cameraX, cameraY, zoom: 2f);

Multiple Grids (Floors, Walls)

Each grid gets its own projection. Compare distances to find the closest hit.

var floor = new GridProjection<OrthogonalLayout>(GridPlane.XZ(0f), layout, floorMap.Bounds);
var shelf = new GridProjection<OrthogonalLayout>(GridPlane.XZ(3f), layout, shelfMap.Bounds);

var floorHit = floor.Pick(mouseX, mouseY, vpW, vpH, viewProj);
var shelfHit = shelf.Pick(mouseX, mouseY, vpW, vpH, viewProj);

// Closest hit wins
PickResult closest = (floorHit.Hit, shelfHit.Hit) switch
{
    (true, true) => shelfHit.Distance < floorHit.Distance ? shelfHit : floorHit,
    (true, false) => floorHit,
    (false, true) => shelfHit,
    _ => default
};

Visible Bounds (Frustum Culling)

Only render/process tiles that are on screen.

// 3D camera
if (grid.TryGetVisibleBounds(vpWidth, vpHeight, viewProjection, out GridBounds visible))
{
    foreach (var coord in visible)
        RenderTile(coord, map.Get(coord));
}

// 2D camera
GridBounds visible = grid.GetVisibleBounds(vpWidth, vpHeight, cameraX, cameraY, zoom);
foreach (var coord in visible)
    RenderTile(coord, map.Get(coord));

Auto-Tiling

Bitmask-based automatic tile resolution — handles neighbor-aware tile selection.

// Define what tiles match each other
struct WallMatcher : ITileMatcher
{
    public bool IsMatch(ushort center, ushort neighbor) => neighbor == center;
}

// Compute neighbor bitmask (4-bit cardinal or 8-bit with diagonals)
byte mask = AutoTiler.ComputeMask4(map, x, y, new WallMatcher());

// Build a rule set mapping bitmasks to output tile IDs
var rules = new AutoTileRuleSet(baseTileId: 1, fallbackId: 0);
// ... add rules for each mask pattern

// Resolve — returns the correct tile ID for a position
ushort tileId = AutoTiler.Resolve(map, x, y, rules, new WallMatcher());

Use Cases

Domain Example
Game Development Tile-based worlds, level editors, fog of war, AI navigation, 3D grid picking, multi-floor selection
Simulation Cellular automata, heat diffusion, agent-based models, traffic flow
Robotics Occupancy grids, path planning, SLAM, sensor coverage maps
GIS / Mapping Raster data, elevation grids, land use classification, hex binning
Data Visualization Heatmaps, density plots, grid-based dashboards
Logistics Warehouse layout, shelf mapping, route optimization
Architecture Floor plans, space allocation, facility management
Scientific Computing Finite difference grids, lattice models, discrete simulations

Data Structure

TileMap<T>
├── Width, Height, LayerCount
├── BoundsMode (Throw, Clamp, Wrap)
├── Layers[]
│   └── TileLayer<T> → flat T[] array, row-major
├── Get(x, y, layer) → ref T
├── Fill(value, bounds, layer)
└── GetLayerSpan(layer) → Span<T>

ChunkedMap<T>
├── ChunkSize (power of 2, default 32)
├── LoadRadius, KeepRadius
├── IChunkProvider<T> (user-implemented)
├── Chunks (ConcurrentDictionary)
│   └── Chunk<T> → T[] data, ChunkState, IsDirty
├── Get(worldX, worldY) → ref T
├── UpdateCenter(coord) → load/unload
└── SaveAllDirty()

API Overview

Module Key Types
Core TileMap<T>, TileMap3D<T>, TileCoord, TileCoord3D, GridBounds, BoundsMode
Layouts ITileLayout, OrthogonalLayout, IsometricLayout, StaggeredLayout, HexLayout
Queries GridRaycast, FloodFill, LineDrawing, AreaQuery, Neighbors
Pathfinding IPathfinder<T>, IPathCostProvider<T>, AStarPathfinder, PathResult
Chunked ChunkedMap<T>, Chunk<T>, ChunkCoord, IChunkProvider<T>, ChunkPool<T>
Serialization ITileSerializer<T>, TileMapSerializer, ChunkSerializer, FormatHeader
Picking GridProjection<TLayout>, GridPlane, PickResult
TileSets AutoTiler, AutoTileRuleSet, AutoTileRule, ITileMatcher

License

MIT

Contact

Discord: sameplayer

Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • net10.0

    • No dependencies.
  • net8.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
0.2.0 82 3/29/2026
0.1.0 84 3/23/2026