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. |