Posted April 26, 2025 by Angir
While developing the multiplayer board game Dice & Domination, I wanted each tile on the board to be fully data-driven. Instead of hardcoding tile properties, I decided to use an external CSV file to control names, colors, prices, rent, and other attributes. This made updates faster and allowed designers to edit data without touching code.
Our team’s designer provided the initial data in an Excel spreadsheet. I cleaned up the formatting (e.g. removing inconsistent text and special characters), then exported it to CSV format. This CSV was then imported into Unity and parsed using a custom script.
Each row in the file defines one tile and includes data:
Number, TileName, BuyingPrice, Rent, RentWithSet, SetSize, SellingPrice, Description, PowerUp, Effect, ...
🔗File: TileSheet
To support this workflow, I split the system into three main components:
TilesData.cs
A plain data class. Each CSV row is converted into a TilesData object containing static values like tile number, color, and rent.
TileCSVReader.cs
A singleton responsible for loading the CSV at runtime using Resources.Load<TextAsset>(). It parses and stores all tile data in a global list.
TileVisualizer.cs
Attached to each tile GameObject. This component uses its tile number to find the correct TilesData, then updates visuals and stores runtime states (like ownership).
TilesData:
TileCSVReader:
For example:
3,2859b3,Little Great Wall,200,...,"It's... kinda defensive, but cute",...
A simple Split(',') would break the description incorrectly:
["3", "2859b3", ..., "\"It's... kinda defensive", " but cute\"", ...]
Once split and cleaned, each row is mapped into a TilesData object. During this conversion, numerical fields such as price and rent are parsed using TryParseInt(), colors are interpreted through HexToColor(), and power-up types are processed via ParsePowerUp().
TileVisualizer:
When the game starts, it checks tile's assigned tileNumber and looks up matching data from TileCSVReader.Instance.
The Reset() method tries to detect the tile number automatically from the GameObject’s name. If the name contains a number, it parses it and assigns it to tileNumber.This helps speed up setup when many tiles are added into the scene at once.
Display of tile data after game starts(left) and route hierarchy (right)
In Start() method it checks if the tile’s material is already a unique instance. If not, it clones a new material. This is important because otherwise, all tiles would share the same material, and changing one tile’s color would accidentally change all others.
The main function UpdateTileVisual() handles loading the tile's data from the parsed tilesdata list. It first looks up the TilesData entry that matches the current tileNumber. Once the data is found, it sets the tile’s color by modifying its material color. Also, if the tile is owned by a player, it also displays a clan icon above the tile, using the player's assigned clan symbol from their Photon custom properties. If no owner is assigned, or if the owner has no clan icon set, the icon stays hidden.
The GetPlayerById() method searches through the Photon player list to find the player who owns a specific tile. It matches based on the player's ActorNumber, which is consistent across the network.
Changing tile’s color according to its tile data
Place an ownership icon on the tile and synchronize it over the network
Icon visualization
Problem | Solution |
Parsing errors due to commas in text fields | Switched from |
All tiles changed color together | Instantiated a new Material at runtime for each tile to prevent color overlap. |
Default tileNumber was 0 and caused warnings | Updated default to 1 and added a validation check during parsing. |
Gerenated ownership icon images. Source: ChatGPT