Learn how to load and save Unity tilemaps using scriptable objects.
Alternatively, I'll show you how to convert tilemaps into JSON or a compressed string so that you can easily save them to a database and allow players to create their own levels to share.
Extra: Learn how to add buttons to your unity editor scripts.
0:30 Base tile script
2:30 Tilemap Manager
3:45 Create the level scriptable object
5:00 Saving the map
11:00 Adding buttons to the editor
14:00 Clearing the map
15:45 Loading the map
21:00 Saving to JSON
22:40 Manual serialization for compression
okay so a question i often see asked is how do i save and load my generated tile maps uh both in the editor and at runtime so i'm going to show you how to do that as well as showing you two separate ways to actually serialize and save your maps depending on what type of game you're making so to start i've just got my grid and then i've got a ground map and a unit map for separation okay and to make your life easier in this process i always recommend creating a base level tile script and i'll show you how that works basically it's just a class that derives from the unity provided tile class okay and then i create just a little uh asset menu create asset menu uh attribute here and what that does is it just allows me to right click create 2d tiles and just a new level tile so i could call that tree or something if i wanted a tree tile okay and then in here i've just got a enum about what what the tile is so i'm separating it here by the actual map type so here i've got grass i could add water lava sand you know whatever i want and then units and i always recommend assigning an integer value to your enums because you could come in here and you could add you know a few more things like this and then later on down the track you could remove this like that and if you have not set a unique integer to these uh c-sharp will do it for you and if you remove this this will actually uh shift the indexes and in the uh editor in the unity editor you'll actually your enum values that were previously set will change so that's uh that's something to avoid so always set integers and as you can see here i'm jumping up a thousand that's just so that uh i give myself enough growth enough expansion growth to have a hundred ground tiles before before i run out it's just a nice way to keep it organized all right so once you've got that sorted and you've created your tiles and in your palette let's start actually painting so i'm gonna grab my grass tile a little island get my unit the snorlax there and then maybe a harlequin there so how do we save it so what we'll do is we will create a tile map manager and i've got a tarmac manager object here so i'm just going to slide that on there and then open it up so what do we need in here we need a few functions we need a save map function we'll have a clear map function just for ease of use and then we'll also have a load so let's work on saving first what we will do is we will grab two references and that will be to our tile maps whoops tile map and it will be our ground map then we've got a unit you'll add additional maps depending on what you need and we'll have a integer here that will tell us what the level index is that we're trying to to save so we might be saving level one or level two now we need actually something to save so let's create a new script and it will be a scriptable object let's call this scriptable level and open that up we can delete this stuff let's turn this into a scriptable object and we need a few things here we will have an integer and this will be our level index we will have a list of tiles but we don't want to be saving just the tile because we need to tell unity where the actual tile has been saved right so we're going to create a new object here and it's going to be a public class saved tile to keep it nice and simple and this will have a vector 3 inch because that's what the tilemap system uses and this will be a position also this will have a level tile okay so basically all we're saying here is this is our tile and this is where we want you to save it over here we can say a save tile and this will be our ground tiles like that and then we'll just copy that and these will be our unit tiles okay so nice and simple and then across at our tile map manager we want to create a new scriptable level and we can do that by script usually you'll create like a asset menu thing here in the same way that i did here and then you manually create these scriptable objects in the editor but what we're going to do is we're going to create it via script okay so new level and we're going to be invoking the actual factory method of scriptable objects called create instance and it will be of type scriptable level okay so we've just created in memory a version of this scriptable level and then we need to assign uh stuff to it so okay so level index we will assign that to our level index which is right here which you'll be setting in the editor will also whoops let me level will also set its name and let's call it something like level and level index as that seems to make sense next up is to set our actual tiles so to do this we need to loop over our tile map check to see if there's a tile in that position check what type of tile it is and then we need to create a new saved tile object so that we can place it in the list here okay so let's just comment that for now and we'll create a local function here that we can use for all the older maps okay so this will return this will be an ieee numerable and it will return a save child get tiles from that okay and this will take in a tile map now to grab all the tiles from a map unity does it a little bit weird um so i'll show you how to do that we go map cell bounce and all positions within and basically this is just a vector three inch i believe yeah so basically it's taking all the positions regardless of if the tile has been filled or not and it's just returning a position so it's gonna it's gonna calculate the bounds and um just iterate through them so what we need to do first is we need to say if map has tile and then we send in this if it does will bananas so now that we know that there is a tile let's actually grab the tile so this will be a level tile because we're going to cast to it map sketch tile and we'll use this generic one so that we can actually pass it to a level tile and we'll just send in the position there so now that we've got our level tile we can actually say okay well what sort of type it is all right and we can grab whatever information like right right now we've just got this and it's absolutely bare basic so in the future you'll add additional things here like additional logic you know is it rotated all this kind of stuff um so but for now we've just got that now that we've got our tile we can return it so yield return new save tile and the position will be equal to buzz and the tile will be equal to level tile okay so now that we've got this function we can now say our new level dot ground tiles equals get tiles from map and we'll send in our ground map and this needs a list to properly be serialized um next we'll do our new level dot unit tiles equals get files from map this time we'll send in our unit map and to list all right so now we've got a scriptable object we're setting its index its name and the tiles so now we need to actually save the object and i'll show you how to do that what we're going to do is we're going to create a little class that should only be in the editor and that's because it won't actually work in a built product so what we need to do is set a conditional statement here um if we're in the unity what is it unity editor we will run this and in here we will create a public class and we'll call this um descriptible object utility something like that and let's actually make that static all right next we need a little function that will actually save the assets so let's call that level save level file and this will take in the scriptable level objects and first we're going to say asset database oops i said database create asset this will be the level and the path will be something like uh it will be assets for such resources and i'll show you why i put it in the resources folder levels and here we will have the level dot name dot okay so that's that's the path that we're going to be saving this physical file to next we'll say asset database save assets and then we'll give it a refresh as well force a refresh and make that static it's in a static class okay so now we can use that let's just grab that and then down here we'll say save level file and then we'll send in the new level oops this will need the scriptable object utility that we just created and there we go so that should save the file now we need a way to actually uh trigger this from inside the editor so let's create a another script let's go back to the editor and actually create a folder called editor and this is required to make custom utility drawers for this script here i'd like to put some buttons here so that we can easily click it from within the editor so let's create a new script call this tile map manager editor okay open that up and we'll remove this and this will be deriving from editor scripts and then this here will be a new attribute custom editor and then here we'll say type of and it will be of a timer manager and then here we'll be overriding on inspector gui okay in here we will say default inspector which will just draw the uh inspector as it is already with you know the serialized objects we will grab a reference to our actual timeout manager script so let's just say the scripts and here we'll cast this to automat manager just target so target is the actual script object that it's attached to and here we'll say if gui layouts button and then here is just the name of the button so this will be save map like that and from here we can say script save map and that will actually call the attach tile manager script uh and run this function here so let's try that out all right as you can see we've got our save map button here i'm going to put in my ground map and my unit map let's make this level 1 and save map and parent directory must be created all right so what we need to do is we need to create our resources folder and then we need to create oops a level folder levels and now that should work save map there we go so now we've got our level and it is not serializing properly because i have not made this serializable okay so that will force this class to be serializable in the editor and let's try to redo that so let's delete that level comma manager save and there we go so we've got our ground tiles and their positions okay so now that we've saved it let's make a convenient way to clear it so with that we can just say let's grab all of our tile maps equals find objects of type tile map and then forage um maps clear all tasks then in our editor again we can copy this and this one will be clear map this one we'll call the clear map function command manager clear map and there we go so now let's work on actually bringing that level back so in time manager load map so basically how this is going to work is it's just going to load whatever the level index um we've got here so this this is going to be used for saving and loading just for brevity okay first we need to grab the levels so let's say level equals resources dot load and we'll use the generic method and we'll say that it's a scriptable level and then in here we will say it's in the levels this will automatically look in the resources folder by the way and then it will be in the levels folder level and then the level index now this can actually be null uh if you entered a level index that doesn't exist so let's actually check for that if level equals null and say debug log say debug error and we'll say level model index because not exist and then just return as we don't want to continue right now we know our level is not null uh let's just clear the map just in case there's any stragglers so now we actually need to assemble our maps so let's start iterating over our tiles so this will be of level ground tiles and this will be a saved tile and then this will be a in here i'll have a switch statement and it will be a type save tile file type and then in here i'm just going to generate what i've got so these two here are not applicable as they are my unit types so right now i've just got grass type tile so what i want to do is just lay the tile in the position that the save tile is declaring so what i'll do is i'll say um ground map set tile then this will be at position save tile dot position and it will be of type save tile tile now just bear with me for a moment and i'll explain why i do it in this kind of like convoluted way they grab that and now let's do it for our unit tiles and now in our unit tiles we'll have uh quinn and then we've also got a snorlax don't okay so the reason i do it this way iterating over the tiles and then actually specifically uh checking what tile it is to do something um i know in this scenario right now we're literally just doing the same thing just setting down the tile but for example in a in in your fleshed out game when you summon an enemy you might actually want to add this enemy to a list in your game manager or something so you would first set the tile and then here you would say you know game manager dot uh let's uh this this is not going to work or game manager dot add enemy or whatever and then add the enemy in or for example in your lava tile you might not just want to set the tile you might also want to spawn a particle system on the lava or something um so this is a a way to give you that granular control over each tile um but as you can see here like i've doubled up so if if you've got all enemies or if you if you've got like you know 10 different ground types and they don't specifically do anything uh special you can just stack them on top of each other like this and they'll all just hit this case and then i'll do the same thing so this is just how i do it you can uh you can do it however you want but this works well for me okay and this should actually already do what we want it to do and and load it all onto the the map so let's create a click for this in our tilemap editor we'll do this and this will be load map and this will just call the loan function so it will say load map see if the level exists clear any stragglers and then actually generate the map so let's give that a crack so timeout manager level index one and load map and i did something wrong here which is here i'm actually setting our monsters to the ground type um and i actually don't like repeating this code so let's create a little function here let's just say our own version accept file and this will actually take in the tile map and then save tile like that and we'll grab this and we'll say map and this will just set the tile like that and then here we can just say this will be the unit map and we want to send in the tile okay so let's try that out again so tile map load and there we go it loads up to our correct tile map we can clear the map again and then let's say we want to create level two and i'll just draw something crazy at a snorlax summer map save map and now we can say level one let's load map two load map and there you go it's that easy uh loading your level in game so see how i do this here just grab the level like that you could have in your level manager uh load level and then send in an institute value or at what level is this uh what level is this player at and then just grab it from the resources folder like that and then assemble the map just like this and then there you go you've loaded your level okay so i've shown you how to serialize them into a scriptable object but what if you wanted to offer a level editor uh in-game to your players like if they wanted to create their own levels like mario maker or you know whatever uh they can't exactly create actual uh files on the file system and then share those with other people it's just not it's just not a logical way to do it so what you could do is instead of creating a scriptable object you could actually just save the data into a json object and then just store the string in a database which can be shared to other players quite easily it's also a really easy way to push new content to your game without necessarily updating the game and sending these new scriptable objects into the new build so it's actually quite easy to do and i wouldn't actually recommend serializing this scriptable level object into json uh but i'm just going to show you how to do it uh using this object um so we could just say json equals and now that we've set you know we've set all of our parameters here all the tiles and the name and stuff we'll just say whoops we'll say we'll use unity is built in json utility and we'll say to and just new level so i'll just show you how that looks attach a little break point touch to unity and i will save this map now back in unity we can see this big ass serialization now this is not an efficient efficient way to serialize your object if you're wanting to save it um at the very least you wouldn't use a scriptable object you would just use like a struct or something and that will serialize down a little bit better than this but and then also because there's a lot of repeat words you could even compress the string an even better idea would be to create your own serialization methods so for example instead of this whole thing here there's one tile you could say all right so this is tile type 1 and then here it is just the x and y because um you don't need to actually show all that you don't even need to show the this is x this is why this is z you can just say uh this is at position one two like that and that could be your full tile serialization right and then the next one will be you know the next one and you could you could do a whole map of just that and it will just be this tiny little string which you could potentially compress even further all right and then you would send that off to uh the database if it's mario maker um or save it locally if they're just still editing it um and then ultimately it's super easy for other players to go through a list of levels that someone else has made and easily just pull down a level uh with hardly any data transfer at all really actually while i'm at it i'll just quickly show you how i would tackle uh doing this so i'll just do it at the bottom here so let's say instead of the scriptable level object you have your own little structure and let's actually say that it's got the same stuff as the scriptable level okay and in here it will have a function which will return the actual serialized string and it will be called serialize now i will be using a string builder that's the most efficient way to do it when you're constantly appending the strings now the trick to this is you are setting little keys uh for larger words to basically uh compress it so for example first we're going to be doing the ground tiles so on my builder i'm going to append let's say uh let's say g okay and now and this will be also an open bracket let's say so this will this is going to be g the the ground tiles so now i'm going to say for each ground tile say builder dot append and this will be for example the tile type so the ground tile oops ground tile tile type and we can serialize that down to its actual integer values which will be you know this will be 0 for example for the grass and then directly after it it could be here we'll have our ground tile position x and our ground whoops and another one of those comma grand tile position okay and then at the end there of the ground tiles we can just say all right that's the end of the ground tiles so then you do that for your units and then other other whatever else you've got in your level and then just simply return builder dot to string and there you go you've got your level so now instead of creating these scriptable levels like this you would just create a new level which is this object assign the things like you did up there and then instead of using this scriptable object utility to actually save it you would just say level new level dot serialize which is this function here and it would return the compressed string which you can then send off to your database and save so anyway uh i hope this helped you i know it was a bit convoluted and i i took a few tangents here and there just to teach you a few things but uh this is how i do it and it's worked quite well for me in the past so if you liked it and you learned something leave a like subscribe to the channel and i will see you in the next one you