- Home /
How to correctly store tile and terrain type data?
Greetings everyone!
I am building a hex tile game where hex is a piece of terrain and can be of different type, e.g. "Sand", "Dirt", "Water". Therefore each hex can have different sprite and probably different properties, say Water is not walkable and Dirt gives -1 to speed.
I am a bit confused of correct storing the information about terrain types. Below I will show what I've come up with at the moment.
Tile game object
Has Sprite Renderer component attached
Has "Tile.cs" script attached with public property "Terrain Type"
Tile.cs calls helper class to set correct terrain sprite to Sprite Renderer's "sprite" property
Terrain.cs helper class
Has public enum TerrainType defined => {Sand, Water, Dirt}
Has public class TerrainDefinition for simple mapping of TerrainType and its Sprite
Class Terrain contains List which is filled in constructor
The code is below.
Terrain.cs
// Possible terrain types
public enum TerrainType
{
Sand,
Water,
Dirt
}
// Structure to map terrain type with its corresponding sprite
public class TerrainDefinition
{
public TerrainType type;
public Sprite sprite;
public TerrainDefinition(TerrainType t, Sprite s)
{
type = t;
sprite = s;
}
}
// Implementation of the type => sprite mapping
// Logic to retrieve a sprite by type and other stuff like getting terrain properties...
public class Terrain
{
private List<TerrainDefinition> terrainList = new List<TerrainDefinition>();
public Terrain()
{
terrainList.Add(new TerrainDefinition(
TerrainType.Sand,
Resources.Load<Sprite>("Sprites/Sand")
)
);
terrainList.Add(new TerrainDefinition(
TerrainType.Dirt,
Resources.Load<Sprite>("Sprites/Dirt")
)
);
}
public Sprite GetSpriteByType(TerrainType t)
{
for (int i=0; i < terrainList.Count; i++)
{
if (terrainList[i].type == t)
{
return terrainList[i].sprite;
}
}
return null;
}
}
Tile.cs
public class Tile : MonoBehaviour
{
public TerrainType terrainType;
public void Start()
{
SetTerrainType(terrainType);
}
public void SetTerrainType(TerrainType t)
{
Terrain trn = new Terrain();
GetComponent<SpriteRenderer>().sprite = trn.GetSpriteByType(t);
terrainType = t;
}
}
What I am not happy with is that Terrain data is moved outside the Tile.cs. I think that each Tile should be aware of its possible types and its sprite representation without having to call other classes. The same for handling additional logic like "whether the tile is walkable", "what speed this tile can be passed" and bla-bla-bla.
The reason I moved the Terrain data outside the Tile.cs is to have only one place where sprites are physically loaded into memory, assuming that I will probably have a lot of tile and each Tile object will have duplicates of sprite images loaded, which is not very good.
My questions are about corret data structure in this case:
1) Should Tile class contain all info about its possible properties and images associated with these properties? Or properties info and logic should be moved outside the Tile class?
2) Storing sprites inside Tile class will lead to duplicating images in the memory for each object? Or it will work like "references" to single memory space?
3) I use Resources.Load to load images and map the with "terrain type", is this OK? I read that using Unity IDE to set resource references by drag and drop is better way, should I avoid using Resource.Load?
Thanks for answers and happy conding!
First of all: Having seperate GameObjects for each tile leads to performance problems very quickly for all but the smallest of maps. Generally, tiles are made from mech chunks or texture chunks which are generated on runtime.
Anyway, if each of your terrain tiles can only be of a certain pre-defined type (grass, road etc.), then you could just give each one a simple ID or name and use that to look up a dictionary or similar collection where t he stats for that terrain type are stored. On the other hand, if each tile can change any stat at any time, then you only need the tile catalogue to set up tile stats initially, but each tile still has to be a full instance of the terrain type class (the data would be copied from the catalogue to the tile).
As for Resources.Load, I wonder what yu mean by "better way"? Performancy shouldn'T be an issue because dragging / dropping is only done in edit mode anyway.
@Cherno, thanks for the reply!
Performance problems wil appear even if I don't use physics calculation and even Update function? Could you point me where to get a good example of building mesh and texture chunks on the runtime? And also could you help me with correct sample code to implement "look up a dictionary"?leads to performance problems very quickly
Unity can't handle thousands of GameObjects in the same scene, no matter how powerful the hardware is. If you have a map that is 50x50 tiles big, that's already 2,500 GOs, and 125,000 if you go into the third dimension. Each Tile Object would have it's own Update function that is called, even if it's empty, and colliders etc. makes it even more of a resource hog.
A dictionary is a collection, like an array or list but each element is a value that belongs to a key, so you pass the key and get the value in return.
Here is a nice overview/guide for using collections:
Choosing the right collection type
Note that you need to be "using System.Collection.Generic" to access collections like Lists and Dictionaries.
Another overview, a bit more technical in nature:
C#/.NET Fundamentals: Choosing the Right Collection Class
As for procedurally generating meshes, this has been done (and written down) a hundred times before so you should find plenty of learning material with a simple Google search. Here is a YT tutorial to get you started: Unity 3d: Tile$$anonymous$$aps - Part 1 - Theory + The "Wrong" Way
Same goes for 2d tilemaps.
Any advice on optimal GameObjects amount for the scene?Unity can't handle thousands of GameObjects in the same scene
You can test it yourself by just adding GOs until the framerate starts to drop.
Note that open-world games like Fallout 3 etc. never have all the objects like items, trees, buildings and characters present at all times; things are loaded into and out of memory constantly when the player moves through the world, a process also known as strea$$anonymous$$g. So you see, even AAA games built for high-end hardware have to deal with certain limitations.
Answer by ShadyProductions · Jan 29, 2016 at 09:35 PM
http://studentgamedev.blogspot.com.au/2013/08/unity-voxel-tutorial-part-1-generating.html Here's an interesting alternative to making a tile sort of game. By using a mesh to make voxels.
Answer by J0hn4n · Oct 18, 2017 at 07:51 PM
i dont know but my own preference its store maps like ints or enums arrays fields, where those values represent what you want. like 0 to none, 1 wall, 2 floor , 3 door etc.... i think its a lot easier that way so just parse that data to your map. also you can map minimaps quickly using these values.