Showing posts with label file. Show all posts
Showing posts with label file. Show all posts

Saturday, September 29, 2012

Tutorial 18 - Saving and Loading Complicated Tables



18.1 Recap

   
Tutorial 18 brings together elements from a number of the previous Tutorials. It is an example of where asking the right question makes finding the answer much simpler. Our aim is to be able to load and save level data from our Tower Defence level generator. Most of the data we need is stored in a table. The tricky part is that Codea only allows us to save data as strings at the moment (some smart folks over at the Codea Forum have worked out how to jam data into image files and save those).
  
Tutorial 16 examined a technique for saving simple table structures like a one dimensional array. Initially we thought that saving a more complicated table wouldn't be that tough. All we needed was one of the existing Lua table to string parsers like Pickle / UnPickle. WRONG!
    

18.2 Limitations of Table Serialisation Parsers

  
We discussed Lua tables in Interlude 6. They are simple in concept but capable of representing very complicated structures. Tables can contain all the simple Lua types (booleans, strings, nil, and number), functions, other tables, closures, etc. The most complete table to string parser that we could find was Data Dumper. But even Data Dumper can not handle tables that contain:
  • Both light and full userdata
  • Coroutines
  • C functions
And of course our level data contains userdata (e.g. vec2 is represented using userdata). So where to from here?
    

18.3 The Modified Cell Class

   
This is where asking the right question makes all the difference. Looking at which data we actually need to save, it becomes apparent that we don't need to save the entire table (including functions) as this can all be reconstituted. All we really need from the Grid table is what is contained in each cell. So rather than trying to convert a two dimensional array of cell objects (which contain userdata) into a string, we extend the cell class to provides its contents (state) as a string. We also provide the inverse function which sets the cell state from a string so we can load the level back in. The extended cell class is shown below.
  
  
Once we can get the grid cell contents out as a string it is easy to write a function to create a simple two dimensional table holding each of these strings. Data Dumper makes short work of converting this table to a string which we can save to global data. Even better, Data Dumper saves the table in a format that loadstring can use to rebuild the original table. The updated Main class for dGenerator details these save and load functions.
  

We updated the File Manager class from Tutorial 17 to allow us to select the file to be loaded. As part of this update we got rid of the submenus because we didn't need them and they meant that the user had to do an extra tap to load. In addition, Delete was right next to Load which is poor design. One slip of the finger would be potentially disastrous (particularly because there is no confirmation for delete and no undo). The other improvement to this version of File Manager is we truncate keys and values which are larger than the ListScroll widths. You can see the upgraded version in the screenshot below. The About menu item doesn't do anything at this stage.
     

18.4 Loading & Saving Data

   
Tapping the Save button on the main screen of dGenerator will call the saveLevel() function in Main(). The function starts off by saving the simplified table data in saveGrid. It then generates the key which contains a header "dGen" (to indicate when loading if it is the right data type), the game name and level number, a boolean indicating if the start cell has been selected and its co-ordinates and then a boolean indicating if the end cell has been selected and its co-ordinates. Finally we use Data Dumper to convert saveGrid to a string and save the key and value to global data.
  
Loading a level is the reverse. Tapping the Load button on the dGenerator main screen will call the loadLevel() function in Main. This prints out some debug data but its only compulsory action is to set the App state to stateFileManager. This will draw the File Manager instead of the main screen and handle its touches.
  
Once the user has navigated to global data and selected an appropriate key containing level data, tapping load on the File Manager menu bar will call the loadFile() function in Main. This function splits the key back into its component parts using the explode() helper function and then uses loadstring() to create a function which rebuilds the data table. The createGrid() function was updated so that it can be loaded using this data table.
  
And that is all there is to it. You can use this link to download the entire code including the updated classes.
  
Next up we will look at adding creeps to your levels in a number of waves.

Thursday, September 20, 2012

Tutorial 17 - A Simple File Manager for Codea



17.1 The Main Class

         
Tutorial 16 explained the various ways that data can be stored using Codea. In order to facilitate the loading and storing of data for our level generator we created a simple file management class.
    
This version handles Local Data, Project Data, Project Information and Global Data. Remember that all of this data is stored in string format using a pList which is accessed by a unique string called the key.
    
The File Manager is constructed using two ListScroll objects (a class written by sanit over on the Codea Forum), a text box showing the value of the selected key and a MenuBar object (a class written by dave1707 also from the Codea Forum).
   
The Main class is used to load some test data into the four data stores and then instantiate the FileManager class. We have provided stubs for the functions called when the MenuBar items are tapped. The only menu item which has been implemented is delete file. WARNING - file deletion can NOT be undone and there is no confirmation required to delete a file so back up your data and use this class with care (or delete this functionality). Feel free to delete the test data which is loaded in setup(), this will be recreated every time you run the App.
  
The data you need to take particular care with is Global Data as this may have been stored by another Codea App. For example, if you have used Spritely (the sprite creation example program which comes with Codea) then any sprites that you have created will be stored in global data.

The delete function has not been implemented for Project Info because there is no way to determine what keys are in there (i.e. there is no listProjectInfo function).

To delete a file we just assign the value of the selected key to nil. We will provide examples of the load and save function when we incorporate FileManager into our Tower Defence Level Generator (dGenerator).
   
The line FileManager:init() will setup your FileManager class and prefetch the keys for each persistent data store.
       
The class handles both portrait and landscape orientation. The orientationChanged() function adjusts the FileManager position when the iPad is rotated. It works but not very well as ListScroll wasn't designed to handle this. You may have to drag the ListScroll box to show the contents. We haven't had a chance to modify this.
      

17.2 The FileManager Class

             
Most of the hard work is done in this class. We start off by defining variables to represent the four supported Directory Types to make the code easier to read. The init() function creates the two ListScrolls (one for the list of Directories called directoryList and one for the keys it contains called dataKeyList).
     
We then pre-load the keys for Project Data, Local Data and Global Data (we already know the Project Info keys since we loaded them). We set the current directory to Project Info and set the current key to the first one. There is a variable called valueString which we use to contain the value of the selected string. Only the first 150 characters of the saved value are displayed. The last thing we do in init() is setup the MenuBar.
     
One of the modifications we had to make to the MenuBar class was to prevent touches on the ListScroll when a sub menu is displayed (otherwise you could accidentally delete the wrong file).
       

17.3 Download the Code

          
You can download all of the code for this program using the following links:
  1. FileManager Complete Code v1.1 - All of the code in one file.
  2. Main.lua - The Main tab class only.
  3. FileManager.lua - The FileManager v1.1 class only.
  4. ListScroll.lua - The ListScroll class from sanit.
  5. TextItem.lua - The TextItem class used in ListScroll from sanit.
  6. MenuBar.lua - The MenuBar class v1.1 from dave1707 (modified) .
Next up we will incorporate FileManager into our Tower Defence Level Generator.