Saving TileMap Data?
Monkey Forums/Monkey Programming/Saving TileMap Data?
| ||
What would be the best method of saving my TileMap Data to file? I noticed Load/SaveString but alas I'm at a loss on how to proceed. I'm not using any frameworks just simple coding it it to suit my current game. So far here is what I have, just the mere simple basics: for anyone interested. Strict Import mojo Function Main:Int() New SimpleMap() Return 1 End Function Class SimpleMap Extends App Global tiles:Image Global tileArray:Int[][] Global OffX:Float Global offY:Float Global Tilesize:Int Global currentTile:Int Method OnCreate:Int() OffX = 10 offY = 10 currentTile = 0 Tilesize = 64 tiles = LoadImage("tiles.png", 32, 32, 10, Image.DefaultFlags) tileArray = AllocateArray(12, 10) For Local x:Int = 0 until 12 For Local y:Int = 0 until 10 tileArray[x][y] = -1 Next Next SetUpdateRate(60) Return 1 End Method Method OnUpdate:Int() If KeyHit(KEY_SPACE) Then SimpleMap.currentTile+=1 If SimpleMap.currentTile > 9 Then SimpleMap.currentTile = 0 Return 1 End Method Method OnRender:Int() Cls DrawText("CurrentTile="+SimpleMap.currentTile, 0, 0, 0, 0) For Local x:Int = 0 until 12 For Local y:Int = 0 until 10 If tileArray[x][y] = 0 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 0) If tileArray[x][y] = 1 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 1) If tileArray[x][y] = 2 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 2) If tileArray[x][y] = 3 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 3) If tileArray[x][y] = 4 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 4) If tileArray[x][y] = 5 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 5) If tileArray[x][y] = 6 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 6) If tileArray[x][y] = 7 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 7) If tileArray[x][y] = 8 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 8) If tileArray[x][y] = 9 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 9) Next Next PlaceTile() Return 1 End Method End Class Function PlaceTile:Int() If MouseDown(MOUSE_LEFT) If MouseX() - SimpleMap.OffX > 0 And MouseX() - SimpleMap.OffX < SimpleMap.Tilesize * 12 - 32 If MouseY() - SimpleMap.offY > 0 And MouseY() - SimpleMap.offY < SimpleMap.Tilesize * 10 - 32 SimpleMap.tileArray[MouseX() / SimpleMap.Tilesize][MouseY() / SimpleMap.Tilesize] = SimpleMap.currentTile EndIf EndIf EndIf If MouseDown(MOUSE_RIGHT) Then SimpleMap.tileArray[MouseX() / SimpleMap.Tilesize][MouseY() / SimpleMap.Tilesize] = -1 Return 0 End Function Function AllocateArray:Int[][]( i:Int, j:Int) Local arr:Int[][] = New Int[i][] For Local ind:Int = 0 Until i arr[ind] = New Int[j] Next Return arr End |
| ||
I just added saving to my game jam game last weekend and added some things to help with that to my minigame framework. Here's some code from my project that may help you if you adapt it.Method SaveLevel() #If TARGET="glfw" Then Local levelString:TFileString = New TFileString For Local block:TBlock = Eachin level.blockList levelString.AddParam("tag",block.tag) levelString.AddParam("x",Int(block.x)) levelString.AddParam("y",Int(block.y)) levelString.AddLineBreak() Next Print levelString.output os.SaveString(levelString.output,ccGetSourceDataPath()+levelNumber+".txt") #End End Method Method Load:Void(levelNumber:Int) Local levelString:TFileString = New TFileString #If TARGET="glfw" Then 'No need to add loading protection as it fails safely. If DEBUG Then 'Use source data folder as that's what the Editor writes to and reads from. levelString.output=os.LoadString(ccGetSourceDataPath()+levelNumber+".txt") Else levelString.output=os.LoadString(ccGetDataPath()+levelNumber+".txt") Endif #Else 'This will load from the data folder instead (I believe it gets compiled into the exe) levelString.output=app.LoadString(levelNumber+".txt") #End levelString.RemoveLineBreaks() End Method Function ccGetSourceDataPath:String() 'Returns the source data path e.g. mygame.data. This is not the data path in the final build. #If HOST = "winnt" Then Return RealPath(ExtractDir(AppPath())+"/../../../../"+APP_NAME+".data/")+"/" #Elseif HOST = "macos" Then ' Return RealPath(ExtractDir(AppPath())+"/../../../../../../../../"+os.StripAll(AppPath())+".data/") 'This doesn't work because the exe is called monkeygame Return RealPath(ExtractDir(AppPath())+"/../../../../../../../../"+APP_NAME+".data/")+"/" 'APP_NAME is a constant I have defined #End Return "" End Function Function ccGetDataPath:String() 'Returns the data path of the final build #If HOST = "winnt" Then Return ExtractDir(AppPath())+"/data/" #Elseif HOST = "macos" Then Return ExtractDir(AppPath())+"/../Resources/data/" #End Return "" End Function '///////////////////////////////////////////////// 'TFileString '///////////////////////////////////////////////// Class TFileString Public Field output:String Field delimiter:String = "," Public Method AddParam(paramName:String, paramValue:String) output+=paramName+"="+paramValue+delimiter End Method Method AddLineBreak() output+=String.FromChars([13,10]) End Method Method GetParam:String() 'Throw away the param name and = sign. output = output[output.Find("=")+1..] Local pos:Int = output.Find(delimiter) Local result:String = Left(output, pos) output = output[pos+1..] Return result End Method Method RemoveLineBreaks:Void() output = output.Replace(String.FromChars([13,10]) , "") End Method End Class |
| ||
I should mention several things: - You can't save/load data to folders on your computer with HTML5/Flash so when I use my editor I compile a GLFW build. - The Editor in my game is only available in Debug mode and then it loads and saves directly from the source data folder. The one named mygame.data. This way whenever I make a new Flash/HTML 5 build the data folder is already up-to-date with the latest levels. - However when the game is running in release mode it just loads data in from the build's data folder which is the correct place to load data from. |
| ||
Don't get it. :) I mean I get some of it. Seeing my code in the OP, how would I implement it in that? After testing for a while I got as far as nowhere which is near can't get it to work... :) [edit]Specifically I got SaveString working but it only saves the first tile. It most definitely does not work the same way as BMAX. |
| ||
The code you've provided doesn't seem to try saving anything. If you post what you've got then maybe someone can spot where you're going wrong. |
| ||
Here is the updated code with my SaveString hack implemented:Strict Import mojo Import os Function Main:Int() New SimpleMap() Return 1 End Function Class SimpleMap Extends App Global tiles:Image Global tileArray:Int[][] Global OffX:Float Global offY:Float Global Tilesize:Int Global currentTile:Int Method OnCreate:Int() OffX = 10 offY = 10 currentTile = 0 Tilesize = 64 tiles = LoadImage("tiles.png", 32, 32, 10, Image.DefaultFlags) tileArray = AllocateArray(12, 10) For Local x:Int = 0 until 12 For Local y:Int = 0 until 10 tileArray[x][y] = -1 Next Next SetUpdateRate(60) Return 1 End Method Method OnUpdate:Int() If KeyHit(KEY_SPACE) Then SimpleMap.currentTile+=1 If SimpleMap.currentTile > 9 Then SimpleMap.currentTile = 0 SaveMap() Return 1 End Method Method OnRender:Int() Cls DrawText("CurrentTile="+SimpleMap.currentTile, 0, 0, 0, 0) For Local x:Int = 0 until 12 For Local y:Int = 0 until 10 If tileArray[x][y] = 0 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 0) If tileArray[x][y] = 1 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 1) If tileArray[x][y] = 2 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 2) If tileArray[x][y] = 3 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 3) If tileArray[x][y] = 4 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 4) If tileArray[x][y] = 5 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 5) If tileArray[x][y] = 6 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 6) If tileArray[x][y] = 7 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 7) If tileArray[x][y] = 8 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 8) If tileArray[x][y] = 9 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 9) Next Next PlaceTile() Return 1 End Method End Class Function SaveMap:Int() If KeyHit(KEY_F1) Local tempArr:Int[][] Local Fileout:String Local tempData:String Local level:Int level = -1 Fileout = "map" tempArr = AllocateArray(12, 10) For Local x:Int = 0 until 12 For Local y:Int = 0 until 10 tempArr[x][y] = SimpleMap.tileArray[x][y] Next Next For Local x2:Int = 0 until 12 For Local y2:Int = 0 until 10 tempData = String(tempArr[x2][y2]) Next Next SaveString( tempData, ExtractDir(AppPath()+"/data/"+Fileout+""+level+".txt")) EndIf Return 0 End Function Function PlaceTile:Int() If MouseDown(MOUSE_LEFT) If MouseX() - SimpleMap.OffX > 0 And MouseX() - SimpleMap.OffX < SimpleMap.Tilesize * 12 - 32 If MouseY() - SimpleMap.offY > 0 And MouseY() - SimpleMap.offY < SimpleMap.Tilesize * 10 - 32 SimpleMap.tileArray[MouseX() / SimpleMap.Tilesize][MouseY() / SimpleMap.Tilesize] = SimpleMap.currentTile EndIf EndIf EndIf If MouseDown(MOUSE_RIGHT) Then SimpleMap.tileArray[MouseX() / SimpleMap.Tilesize][MouseY() / SimpleMap.Tilesize] = -1 Return 0 End Function Function AllocateArray:Int[][]( i:Int, j:Int) Local arr:Int[][] = New Int[i][] For Local ind:Int = 0 Until i arr[ind] = New Int[j] Next Return arr End |
| ||
From a brief glance, this:For Local x2:Int = 0 until 12 For Local y2:Int = 0 until 10 tempData = String(tempArr[x2][y2]) Next Next ...is going to always only include one cell because you're just replacing the previous tempData value every time. If you use "tempData += " you'll append the values. |
| ||
Yep that seems to have solved it. Thanks! I put another line after the first tempData: tempData+="," so that each value of the Array is separated by a comma. I've got it working nicely now and can proceed. Thanks Again! |
| ||
Is there a non confusing way of loading .txt files from the data directory? Looking at the example Grey posted it's too confusing for me to figure out. |
| ||
Local aString:String = LoadString("file.txt") Then do what ever is needed with the string. :) |
| ||
Also, for what it's worth... the following part of code could be quicker.If tileArray[x][y] = 0 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 0) If tileArray[x][y] = 1 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 1) If tileArray[x][y] = 2 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 2) If tileArray[x][y] = 3 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 3) If tileArray[x][y] = 4 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 4) If tileArray[x][y] = 5 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 5) If tileArray[x][y] = 6 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 6) If tileArray[x][y] = 7 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 7) If tileArray[x][y] = 8 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 8) If tileArray[x][y] = 9 Then DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 9) you could either do a select statement so it's... Select tileArray[x][y] Case 0 DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 0) Case 1 DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, 1) ... End or unless things change drastically, just do... DrawImage(tiles, OffX + x * Tilesize, offY + y * Tilesize, 0, 2, 2, tileArray[x][y]) |
| ||
Sorry Amon. It was confusing for me too when I was figuring it out :-) |
| ||
possibly this will help. The code bellow loads my Terminal 2 projects level's into the level array, the save code is kinda the same just saving out the data as a comma delimited file. I would show you that as well, but it's not written in monkey, I did the editor in Max. The file itself is just 213,123,1,23,12,3,1,31,23, with each number representing a tile for the level. It's kinda crude, does not store any additional information about each tile, or collisions, but then I didnt need it to so. Anyway. hope it helps. [monkeycode] #Rem sumary:Load Stage Loads the given stage, parsing in wave data, and loading in the correct tile sets and tile data. #End Method LoadStage(stage:Int=1) CacheAliens(stage) Self.StageSpeed=.7 Local tmpImage:Image Self.offsetx=-16 Local LevelIN:String LevelIN = LoadString("Stage_"+String(stage)+".txt") Local SpawnIN:String SpawnIN = LoadString("Stage_"+String(stage)+"_wave.txt") Self.BackGround = game.images.LoadAnim("backgrounds/stage_"+stage+".png", 480,200,6,tmpImage,True) Self.BackGroundOffset = 0 Self.BackGroundStep = 0 game.images.LoadAnim("StageTiles_"+stage+".png", 128, 128, 80, tmpImage,True) Self.StageTiles = game.images.Find("StageTiles_"+String(stage)) Local loop:Int = 0 For Local NewGrid:String = Eachin LevelIN.Split(",") If loop<500 Self.grid[loop]=Int(NewGrid) loop+=1 End If Next Local bit:Int=1 Local id:Int=0 Local trig:Int=0 Local cnt:Int=0 Local unit:Int =0 For Local value:String = Eachin SpawnIN.Split(",") Print "Bit ="+bit Select bit Case 1 unit=Int(value) Case 2 id=Int(value) Case 3 trig=Int(value) Case 4 cnt=Int(value) End Select If bit=4 Then AddWave(unit,id,trig,cnt) Print "Adding Wave ["+id+","+trig+","+cnt+"]" bit=0 End If bit=bit+1 bit = bit Mod 5 Next Local loop2:Int = 0 For Local NewGrid:String = Eachin LevelIN.Split(";") 'gets a line ended by a ; 'split by , to get each spawn data. Next End Method [/monkeycode] EDIT, just fixing the bbocde and switching it to monkeycode. |