How to Create Custom Tilesets for Unciv Maps: A Complete Guide to Visual Mods
To create a custom tileset for Unciv, package your images in Images/Tilesets/<Name>/, define a JSON configuration in jsons/Tilesets/<Name>.json, and register the mod as a permanent visual mod in ModOptions.json so the TileSetCache can load and render your graphics.
Unciv treats tilesets as visual mods, allowing you to completely customize map appearance through custom graphics and configuration files. When the game initializes, the TileSetCache class reads your tileset's JSON configuration and lazily loads assets from your mod's dedicated folder. This guide walks through the complete process of creating custom tilesets for Unciv maps based on the actual source code implementation in the yairm210/Unciv repository.
Understanding the Tileset Architecture
Before creating assets, you need to understand how Unciv processes tilesets internally. The rendering pipeline relies on several key classes working together to resolve images and apply visual rules.
How TileSetCache Loads Your Assets
The TileSetCache class, located in core/src/com/unciv/models/tilesets/TileSetCache.kt, serves as the central registry for all tileset data. When the game starts, this cache reads the tileset's JSON configuration via TileSetConfig.kt and constructs a TileSet object containing set-specific options including fallback settings, scaling rules, and rule variants.
The cache expects to find graphic assets in the folder Images/Tilesets/<TilesetName>/. If the game requests an image that your tileset does not provide, the cache automatically falls back to the tileset specified in the fallbackTileSet field (defaulting to "FantasyHex") up to a configurable recursion depth.
The Role of ImageGetter and TileSetStrings
Whenever a map tile needs a texture, the ImageGetter class (core/src/com/unciv/ui/images/ImageGetter.kt) queries the cache for the appropriate image. The TileSetStrings class (core/src/com/unciv/ui/components/tilegroups/TileSetStrings.kt) wraps the current tileset name and handles fallback logic for UI components. This separation ensures that missing assets gracefully degrade to the fallback tileset without breaking the rendering pipeline.
Setting Up Your Custom Tileset
Creating a functional tileset requires both graphic assets and a properly structured configuration file.
Required Folder Structure
Your mod must follow a specific directory layout so TileSetCache can discover and load your assets:
MyMod/
├─ Images/
│ └─ Tilesets/
│ └─ MyCoolTileset/
│ ├─ Grassland.png
│ ├─ Forest.png
│ ├─ Edges/
│ │ └─ Cliff-Hills-Coast-Top.png
│ └─ Hexagon.png
├─ jsons/
│ └─ Tilesets/
│ └─ MyCoolTileset.json
└─ ModOptions.json
Place your PNG images in Images/Tilesets/<TilesetName>/. The optional Edges/ subdirectory contains directional edge graphics for features like cliffs and coastlines.
Creating the Tileset Configuration JSON
The jsons/Tilesets/<TilesetName>.json file defines your tileset's behavior. This configuration maps directly to the TileSetConfig data class in core/src/com/unciv/models/tilesets/TileSetConfig.kt.
{
"useColorAsBaseTerrain": false,
"useSummaryImages": true,
"unexploredTileColor": {"r":0.2,"g":0.2,"b":0.2,"a":1},
"fogOfWarColor": {"r":0,"g":0,"b":0,"a":1},
"fallbackTileSet": "FantasyHex",
"tileScale": 1.0,
"tileScales": {
"City center": 1.2,
"Citadel": 1.5
},
"ruleVariants": {
"Grassland+Jungle+Dyes+Trading post": [
"Grassland",
"JungleForGrasslandBack",
"Dyes+Trading post",
"JungleForGrasslandFront"
]
}
}
Set fallbackTileSet to ensure missing graphics resolve to a base tileset. The useColorAsBaseTerrain option, when enabled, draws plain hexagons using the base terrain's RGB values instead of images.
Rule Variants and Rendering Logic
Rule variants give you precise control over how the engine composites tile layers. Defined in the ruleVariants object, these mappings translate a tile signature string into an ordered list of image names. The TileLayerTerrain.kt class interprets these rules when resolving tile appearances through ImageGetter.
For example, a jungle with dyes and a trading post can render in a specific layer order: base terrain first, then back jungle layer, then improvements, then front jungle layer.
Advanced Customization Options
Beyond basic image replacement, Unciv supports several advanced rendering features controlled through your JSON configuration.
Base Terrain Colors and Summary Images
When useColorAsBaseTerrain is true, the engine uses Hexagon.png as a base and tints it according to the terrain's defined color. This reduces asset count but requires the Hexagon.png file in your tileset folder.
Setting useSummaryImages to true enables summary images, allowing a single graphic like "NaturalWonder" to replace multiple individual images. The TileLayer.kt class processes these summaries to simplify complex terrain rendering.
Edge Graphics and Scaling
Place optional edge images in Images/Tilesets/<TilesetName>/Edges/ to render directional transitions between terrain types. The TileLayerTerrain.kt loads these based on neighbor direction, allowing custom cliff faces or coastlines.
The tileScale field applies uniform scaling to all sprites, while tileScales allows per-terrain-type overrides. For example, the Minimal tileset uses this to enlarge city centers and citadels specifically.
Enabling and Testing Your Tileset
Once your files are structured correctly, you must register the tileset with the game engine.
Mod Registration and Options Menu
To make your tileset selectable, register your mod as a permanent visual mod in the Mod Manager. In your ModOptions.json, set the tileset field to your tileset name:
{
"tileset": "MyCoolTileset"
}
When enabled, the game parses jsons/Tilesets/<TilesetName>.json, merges it into TileSetCache, and exposes the tileset in the Options menu (core/src/com/unciv/ui/popups/options/DisplayTab.kt). Changing the selection triggers a UI reload, forcing the map to re-render with your custom graphics.
Programmatic Loading (for Testing)
For automated testing or development scripts, you can load and activate tilesets programmatically:
// Load the mod (already placed in the mod folder)
UncivGame.loadMods(listOf("MyMod"))
// Switch to the new tileset
UncivGame.settings.tileSet = "MyCoolTileset"
UncivGame.settings.save()
UncivGame.reloadWorld() // forces a map redraw with the new tileset
This approach bypasses the UI and is useful for verifying that your ruleVariants and image paths resolve correctly.
Summary
- Unciv loads custom tilesets through
TileSetCacheincore/src/com/unciv/models/tilesets/TileSetCache.kt, which reads JSON configurations and graphic assets fromImages/Tilesets/<Name>/. - The
ImageGetterclass resolves textures using fallback logic defined in your config'sfallbackTileSetfield. - Create a
jsons/Tilesets/<Name>.jsonfile definingruleVariants, scaling options, and rendering flags likeuseColorAsBaseTerrain. - Register your tileset in
ModOptions.jsonas a permanent visual mod to make it selectable in the Options menu. - Use
ruleVariantsto control the exact layering order of tile components, and place edge images in theEdges/subdirectory for directional terrain transitions.
Frequently Asked Questions
What image format should I use for Unciv tilesets?
Unciv expects PNG images for tileset graphics. Place these in your Images/Tilesets/<TilesetName>/ directory. The ImageGetter class handles texture loading and supports transparency, so use PNG files with alpha channels for overlays and complex terrain features.
How does the fallback tileset system work?
The fallbackTileSet field in your JSON configuration specifies which tileset to query when your custom tileset lacks a specific image. According to the source code in TileSetCache.kt, the engine recursively checks the fallback tileset up to a configurable depth, defaulting to "FantasyHex" if unspecified. This ensures your tileset displays correctly even if you haven't created art for every terrain type.
Can I combine multiple mods with custom tilesets?
Only one tileset can be active at a time, selected via the Options menu in DisplayTab.kt. However, you can create a single mod that references assets from multiple sources or uses extensive ruleVariants to mix styles. The TileSetCache merges configurations from enabled permanent visual mods, but the last loaded tileset takes precedence.
Where do I place edge graphics for cliffs and coastlines?
Place edge images in Images/Tilesets/<TilesetName>/Edges/. The TileLayerTerrain.kt class loads these based on directional detection between neighboring tiles. Name your edge files according to the terrain transitions they represent, such as Cliff-Hills-Coast-Top.png, and the engine will apply them automatically when rendering adjacent terrain types.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →