I’m making a 2D farming (open-world type) game and i need a huge map for it…
Until now I was using the regular In-built Tilemap system in Unity. But, I needed to make it a grid-based map,
i.e. I want to be able to select a tile of the grid to plant and break stuff (I think i know how to do this, using Raycasts)
Here is where the problem arises, To make the grid i started instantiating ‘Tile’ objects like :
for (int x = 0; x < GridArray.GetLength(0); x++)
for (int y = 0; y < GridArray.GetLength(1); y++)
{
GameObject Tile = Instantiate(TileObject, this.transform);
SpriteRenderer TileSP = Tile.GetComponent<SpriteRenderer>();
float Rand = Random.value;
if (Rand > 0.97f)
{
Name = "Flower";
}
else if (Rand > 0.8f)
{
Name = "Grass";
}
else
{
Name = "Plain";
}
switch(Name)
{
case "Grass" :
TileSP.sprite = TileObjectSprites[Random.Range(5, 6)];
Tile.tag = "Grass";
break;
case "Flower" :
TileSP.sprite = TileObjectSprites[Random.Range(1, 4)];
Tile.tag = "Flower";
break;
case "Plain" :
TileSP.sprite = TileObjectSprites[0];
Tile.tag = "Plain";
break;
default :
TileSP.sprite = TileObjectSprites[0];
Tile.tag = "Grass";
break;
}
Tile.transform.position = new Vector3(x, y, 0f);
Tile.name = "Tile" + NameNumber;
NameNumber++;
}
This code alone takes around 1 minute just to process and load into the game, But when i try to instantiate trees and other objects along with the tiles it can anywhere between 5-15 minutes just to process
So, i wanted to know if there was a more efficient and less time consuming method of doing this…
Thanks in advance
EDIT : I checked out some videos on YouTube, but none of them really fit my problem
Let me list few possible answers to your question in order of difficulty to implement them:
Don’t instantiate objects you can’t see :T
You can pre-instantiate all those objects ahead of time (ObjectPool or something like that)
Split your game code into two separate layers:
DATA (actual game world state)
DATA VISUALIZATION (things you see on the screen)
This will allow you to run game world simulation cheaply on some kind of basic WorldCellData[]
that contains all the world grid cells with their data. And then, again, only spawn objects to visualize state of cells that be seen by the player and despawn things he can’t.
Go DOTS. 100% or just partially, i.e. player & enemies can stay being GameObject
s where the world is mostly entities.
If you weren’t aware, the tile assets that you use to paint a tilemap are ScriptableObjects
(link). I believe the tilemap uses these to achieve something similar to the ‘level 3’ suggestion from @andrew-lukasik.
You can extend the Tile
(link) or TileBase
classes to add custom behavior to your tiles. You wouldn’t need to instantiate all of those objects because the unity engine already handles initialization for the tilemap. One important thing to know is that copies of the same tile on the map are not individual instances of a scriptable object. You would still need to make a list of objects that contain the data specific to each tile on the map. When you plant or break things on the farm, you can just grab a reference to the custom tile at that position and give it the data container tied to that location, maybe as a parameter to a method call. Methods in the custom tile can modify the data or use the data according to whatever your goals may be.
Obviously, your gameplay is going to require frequent visual changes. You can override GetTileData in order to define how a tile decides which sprite to use. An implementation of this method could fetch the data object for its location and choose a sprite with that information. If a tile needs to change behavior, you can switch it with a new type of custom tile.
Here is a code snippet I use in all of my ScriptableObject implementations. Line 9 adds a menu item under Assets > Create according to the path that you define (As shown in the example, it would be Assets > Create > Custom Tile). An instance of your scriptable object is created and saved into a “.asset” file. These files can be dragged and dropped into serialized fields in the inspector so that they can be referenced in your code. If you want to paint custom tiles onto your tilemap, just drag and drop the file into your tile palette and use the tile brushes as you normally would.
public class YourCustomTile : Tile
{
public void YourTileBehavior(YourTileDataClass data)
{
//do farming stuff
}
#if UNITY_EDITOR
[UnityEditor.MenuItem("Assets/Create/Custom Tile")]
public static void Create()
{
var name = "Default File Name";
var path = UnityEditor.EditorUtility.SaveFilePanelInProject(
$"Save {name}", name, "asset", $"Save {name}", "Assets/YourFilePath");
if (path == "") return;
UnityEditor.AssetDatabase.CreateAsset(CreateInstance<YourCustomTile>(), path);
}
#endif
}