About
Create and paint hexagonal sphere planets with tiles, props, rivers, and terrain deformation.
A complete planetary generation system for strategy games and simulations.
Key Features
- Tileset System: Define materials, pathfinding costs, and terrain types (land, water)
- Props: Create prop palettes containing all the props you'll be using for your planet. Place props using the painter tool and choose optional randomization such as scale variation, jitter, and rotation. Each prop can have multiple prefabs assigned to it and the painter tool will randomize the variant used on generation
- Rivers: Draw rivers between tiles. If using the included pathfinding capability, you can have a movement modifier to crossing rivers
- Height Editing: Deform your planet's surface with vertex-based height displacement
- Territories: A system capable of painting territories on land to show land control
- Pathfinding: A pathfinding script is included that includes Land, Air, and Sea capabilities. Factors that can affect pathfinding costs are tile type, props, cliffs, water, rivers
- A demo scene with a few starter props and a tileset, and a runtime user interface to see them in action
- Currently supports planets up to 100,000 tiles
Technical Details
- Hexagonal Sphere Generation
- Tileset Editor
- Tile Painting
- Prop System
- River Tool
- Height Modification
- Territory System
- Pathfinding Support
- Planet Save/Load System
- Template System for quicker planet creation times
- Planets with up to 100K tiles
Getting Started Guide
Creating Your First Planet
- Create a Tileset: Go to Window → Planet → Tileset Editor. Create new tilesets or edit existing ones to define your terrain types.
- Generate Planet: In the scene hierarchy, go to Create → 3D Object → Planet. The frequency slider controls subdivision and tile count (formula: Total tiles = 10 × frequency² + 2).
- Paint Your Planet: Open Window → Planet → Planet Painter to apply tiles, place props, add rivers, and deform terrain. Note: After recompile, click "Regenerate Mesh" to reload tile information.
Working with Props
- Create a Prop Type from Create → Planet → Prop Type
- Prop types (e.g., trees) can have multiple prefabs for randomized generation
- Create Prop Palettes from Create → Planet → Prop Palette or in the Tileset Editor
- Prop sizing is relative to tile size, ensuring correct scaling across all planet sizes
- Important: For planets with 10K+ props, update the registry via Tools → Planet → Build Prop Type Registry for optimized loading in builds
Rivers and Pathfinding
- Rivers require a river material to use
- Rivers have pathfinding modifiers (configurable in Pathfinding.cs)
- Maximum slope for pathfinding: 40°
Editor Tips
- View Rotation: Select planet, press F to focus, hold Alt + Left Click to rotate, Alt + Right Click to zoom
- Templates: For frequencies >50, use templates (Tools → Planet → Generate Icosphere Templates) to dramatically reduce load times. Templates range from a few MB to 10MB+ each.
- Planet Loader: The planet loader is necessary even for scene-loaded planets, as it initializes everything on play
- Limitation: Does not support multiple planets in the scene
Planet Architecture Guide
Icosahedron Generator Overview
The planet mesh generation follows a two-step process:
- Subdivision: Start with a base icosahedron (20-sided polyhedron) and subdivide each triangular face based on frequency. Higher frequency = more subdivisions = more triangles.
- Dual Mesh Generation: Generate a dual mesh by computing centroids of triangles. These centroids become vertices of the tile mesh, creating hexagonal tiles (with 12 pentagons at original vertices) covering the sphere uniformly.
Raycast to Tile Lookup
When clicking or hovering over the planet:
- Raycast: TileRaycaster performs Physics.Raycast from camera through mouse position
- Find TileManager: Search hierarchy to locate TileManager
- World to Local: Convert world hit point to local planet space
- Spatial Lookup: Use spatial grid to quickly find nearby tiles (O(1) vs O(n))
- Tile Found: Return closest Tile object with all data (id, type, neighbors, props, etc.)
Key Data Structures
baseUnitDualVertices - Master vertex array with two parts:
- Part 1 [0 to centroidVertexCount-1]: Centroids forming tile corners/edges (shared between neighbors)
- Part 2 [centroidVertexCount onwards]: Center vertices (one per tile) for fan triangulation
Tile Class Properties:
- id - Unique identifier
- vertexIndices - Boundary vertices
- centerVertexIndex - Center vertex index
- triangleIndices - Visual mesh triangles
- neighborIds - Adjacent tile IDs
- tileTypeGuid - Painted tile type
- prop - Optional PropInstance
- riverIds - Rivers along edges
The Tile class can be expanded for game-specific logic: structures, resources, units, fog of war, etc.
Spawning Structures on Tiles
IcosahedronGenerator provides these methods:
- GetAverageTileSize(): Returns typical tile edge length for scaling structure footprints
- GetTileCenterPosition(tile): Gets deformed center position with height applied
- GetSurfacePositionWithinTile(tile, position): Projects position onto tile surface with exact 3D coordinates (used for jittered prop placement)
- SampleNormalAtUnitDir(unitDir): Gets interpolated surface normal for rotation alignment
Pathfinding System
Pathfinding.cs - Core A* Algorithm
- Non-MonoBehaviour pathfinding logic class
- Implements A* algorithm for optimal paths between tiles
- Calculates movement costs based on terrain type, props, rivers, and slopes (40° max)
- Handles agent-type restrictions (Land/Water/Air)
TilePathfinder.cs - MonoBehaviour Wrapper
- Component attached to planet GameObject
- Creates and manages Pathfinding instance
- Configurable max search iterations (performance safety)
Core API:
- FindPath(startTile, endTile) - Basic path
- FindPath(startTileId, endTileId) - Path by ID
- FindPath(tile, tile, AgentType) - Agent-specific pathfinding
- IsTilePassable(tile, AgentType) - Check walkability
- GetNeighbors(tile) - Get adjacent tiles
PathAgent.cs - Movement Execution
- Component that moves GameObject along calculated path
- Follows deformed terrain surface using triangle interpolation
- Auto-enables/disables Update() when moving/stopped
- Fires events at milestones
Core API:
- SetPath(path, planet) - Start moving along path
- TilesPerSecond - Speed control (auto-scales with planet size)
- OnTileEntered - Event fired when entering each tile
- OnPathComplete - Event fired when reaching destination
Territory System
Creating Territories
Auto-assign materials (random cycling):
Territory t = territories.CreateTerritory(tiles);
Automatically assigns materials from the pool using random selection. Tiles parameter can be null for dynamic addition later.
Custom materials:
Material borderMat = /* your material */;
Material fillMat = /* your material */;
Territory t = territories.CreateTerritory(tiles, borderMat, fillMat);
Managing Territory Tiles
Add tiles incrementally:
territory.AddTile(tileId);
territories.RefreshTerritory(territory);
Remove tiles incrementally:
territory.RemoveTile(tileId);
territories.RefreshTerritory(territory);
Replace all tiles at once:
territories.SetTerritoryTiles(territoryId, newTileList);
Remove tile and update lookup:
territories.RemoveTileFromTerritory(territory, tileId);
Querying Territories
// Find which territory contains a tile
Territory t = territories.GetTerritoryForTile(tileId); // Returns Territory or null
int id = territories.GetTerritoryIdForTile(tileId); // Returns ID or -1
// Get territory by ID
Territory t = territories.GetTerritory(territoryId);
// Get all territories
List<Territory> allTerritories = territories.GetAllTerritories();
// Check if territory contains tile
bool contains = territory.ContainsTile(tileId);
Visual Control & Cleanup
// Show/hide territory
territory.Show();
territory.Hide();
// Destroy single territory
territories.DestroyTerritory(territory);
// Clear all territories
territories.ClearAllTerritories();
Setup Requirements
Place material pairs in Resources/Materials/ folder with sequential naming (gap-free):
- TerritoryBorder + TerritoryFill (required - first pair)
- TerritoryBorder1 + TerritoryFill1
- TerritoryBorder2 + TerritoryFill2
- etc.
Works with any number of material pairs (minimum 1 required). System stops loading at first missing pair.