Saving Games
Blitz3D Forums/Blitz3D Programming/Saving Games
| ||
Okay I have some quite large levels I am working on. I am using entities, types and various other variables. Is there a way to save the level as is and reload. Examples would be nice. Any help appreciated. Thanks in advance |
| ||
You'll have to loop trough all your entities (with types or arrays) and save all their positions and other data to a file with the write functions (writeline/writestring/writeint/etc). There is no built-in way of doing this automatically. |
| ||
First, make sure you know what criteria and details/parameters about your objects in the world need to be saved. perhaps your world is popuated with trees. You may need to know Tree Size Tree No. of Branches Tree Colour Tree Leaf Shape Tree Location Once you have all this data, as fall_x suggests, you should store this data in an array or a Type. For instance Type Tree Field Size Field Branches Field Color$ Field Leaf$ Field Loc_X Field Loc_Y Field Loc_Z End Type When it comes to Saving, Im not 100% on this, because I havent used the file commands for a while, but the general gist is: [code] Filename=OPenfile$("Saved_Folder\Savedgame.blah") For Saver.Tree = Each Tree WriteLine(Filename)=Saver\Size WriteLine(Filename)=Saver\Branches WriteLine(Filename)=Saver\Colour$ WriteLine(Filename)=Saver\Leaf WriteLine(Filename)=Saver\Loc_X WriteLine(Filename)=Saver\Loc_Y WriteLine(Filename)=Saver\Loc_Z Next CloseFile(Filename) [/codebox] So at any time, levels can then be loaded in with the similar instructions (again, sorry if the filehandling syntax isnt right!): |
| ||
WriteLine and ReadLine would probably be better suited to be something like WriteInt/ReadInt or WriteFloat/ReadFloat#. That would also create a smaller file size. A problem with Malices code is that it can only load in trees. You would benefit from doing something like this: Filename=WriteFile("Saved_Folder\Savedgame.blah") For Saver.Rock = Each Rock WriteInt(Filename, 1) WriteInt(Filename, Saver\Type) WriteInt(Filename, Saver\Size) WriteString(Filename, Saver\Colour$) WriteFloat(Filename, Saver\Loc_X) WriteFloat(Filename, Saver\Loc_Y) WriteFloat(Filename, Saver\Loc_Z) Next For Saver.Tree = Each Tree WriteInt(Filename, 2) WriteInt(Filename, Saver\Size) WriteInt(Filename, Saver\Branches) WriteString(Filename, Saver\Colour$) WriteInt(Filename, Saver\Leaf) WriteFloat(Filename, Saver\Loc_X) WriteFloat(Filename, Saver\Loc_Y) WriteFloat(Filename, Saver\Loc_Z) Next ;etc. CloseFile(Filename)Where the first WriteInt is saving the type of object being saved such as 1=rock, 2=tree, 3=house, 4=lightpost, etc. This way, you know how to load it later. And then to load it you would: Filename=ReadFile("Saved_Folder\Savedgame.blah") Repeat objecttype = ReadInt(Filename) Select objecttype Case 1 ;load in rock Case 2 Loader.Tree = New Tree Loader\Size=ReadInt(Filename) Loader\Branches=ReadInt(Filename) Loader\Colour$=ReadString$(Filename) Loader\Leaf=ReadInt(Filename) Loader\Loc_X=ReadFloat#(Filename) Loader\Loc_Y=ReadFloat#(Filename) Loader\Loc_Z=ReadFloat#(Filename) Case 3 ;load in house Case 4 ;load in lightpost Case Default RunTimeError "Unknown object encountered" End Select Until Eof (Filename) I can't help outdoing someone elses code... ;) |
| ||
Heh I was just doing the basics, and I didnt have much time, but Im happy in the knowledge I was on the right track ;) |
| ||
It is a monster job if you have a large and complex game. And not easy to saved and restore your game EXACTLY as it was - especially when using tokamak for example... What I wouldn't give for a ReadType/WriteType function or command... for t.tree = each tree WriteType t nextAnd a simple routine to load it back again like: While not eof(x) t.tree = new tree ReadType t wendOr even better, just: WriteType Each Tree WriteType Each House WriteType Each Everything*sigh* |
| ||
Save-Game is a thing that should be implemented from beginning on. It's very hard to add it to a game that has already a certain size. BTW Cash, did you get my mail? |
| ||
Save-Game is a thing that should be implemented from beginning on. It's very hard to add it to a game that has already a certain size. I agree. That, and making sure you have some code that deletes current entities and types from memory, for instance before loading your game or before switching levels you will need to clear the current level, all enemies, power-ups, ... It's a real pain to look trough your code for anything you could have missed that could cause a memory leak. |
| ||
All absolutely true. But if you work on a big game for let's say a year, I would have to change and update my loading & saving routines nearly every other day or so. Too bad there isn't a way where you can just 'save the chunk of memory' wich has all that in there.... Coredump() |
| ||
Efficient programming would mean that only certain flags would need to be recorded. These flags, when re-loaded, would describe the exact conditions apparent in the world, so the state of enemies, the weather, the number of raindrops left to fall etc. could all be calculated from the flags' status. |
| ||
Half of the job could be done for you using Str$(type) which will create a printout of all fields in a type. You would then only need to create a loader for each type in your game and read the values back in from a file. Some people advocate storing everything in types anyway, so this might not be a bad idea. Your loader would have to parse the type again... see the example below: ;------------------------------------------------------------------ ;---- demo of saving and reloading game data using Str$() ---- ;---- Blackjumper - Jan 2005 ---- ;------------------------------------------------------------------ Type test Field x Field y Field name$ End Type For count = 1 To 4 n.test = New test n\x = Rand(10) n\y = Rand(10)+100 n\name$ = Chr(Rand(26)+65) +Chr(Rand(26)+65) +Chr(Rand(26)+65) Next fileout = WriteFile("C:\storedgame.txt") For n.test = Each test Print Str$(n) WriteString (fileout, Str$(n)) Next CloseFile( fileout ) Print "game data written to file... press any key to continue" Print WaitKey Print "deleting all instances of type 'test'..." For n.test = Each test Delete n Next Print "printing all type information... Print "________________________________" For n = Each test Print Str$(n) Next Print "--------------------------------" WaitKey Print Print "... now reading from disk..." filein = ReadFile("C:\storedgame.txt") While Not Eof(filein) Read1$ = ReadString$( filein ) RestoreTestInfo(Read1$) Wend Print "printing all reloaded type information... Print "------------------------" For n.test = Each test Print Str$(n) Next Print "------------------------" WaitKey End Function RestoreTestInfo( SavedString$ ) Print "Read from file --> " + SavedString$ SavedString$ = Mid$( SavedString$, 2, Len(SavedString$)-2) ; remove end square brackets firstcomma = Instr(SavedString$, ",") firstvalue% = Left$(SavedString$, firstcomma-1) ; convert first value (up to comma) to an int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to 1st comma firstcomma = Instr(SavedString$, ",") secondvalue% = Left$(SavedString$, firstcomma-1) ; convert up to new first comma to another int SavedString$ = Mid$( SavedString$, firstcomma+1, Len(SavedString$)-firstcomma+1) ; eat up to new 1st comma ThirdString$ = Mid$( SavedString$, 2, Len(SavedString$)-2) ; remove quotes from string reloaded.test = New test ; make a new type reloaded\x = firstvalue% ; and assign the reloaded\y = secondvalue% ; reloaded values reloaded\name$ = ThirdString$ ; to the fields End Function You could investigate add's value parser from the Code Archives to deal with floats, etc... http://www.blitzbasic.com/codearcs/codearcs.php?code=161 |
| ||
Super cool - thanks for that BlackJumper, I didn't know Str$() did that !!! This is a BIG help! cheers, Danny |
| ||
Hmm me too! That helps A LOT!!! Thanks BJ |
| ||
OK, I guess I will stick this in the Code Archives then. |
| ||
I was thinking about implementing a system where any changes to the objects in my game would be written to a log. Then, to save the level I just need to save the log. When I reload the level, I load it as it was designed and replay the saved log. Each object in the level has a unique ID which should make this pretty easy to implement. Things like new objects being created would be logged as well. |
| ||
That's why using Types is so much easier, because unlike arrays, you can add/subtract to types without having to predetermine any specific dimensions. |
| ||
I think it has not to be so complicated. You can do like in console games like Zelda (GameCube), in that game only the player status (many variables, easy to save and load) are saved, and it only stores the keys,items,objects,etc you have got in the game but the scenery is always the same. I donīt know if I have explain it well but I think that this system is much easier. |
| ||
I sort of agree with both sides on this, you need to have save-game capabilities early in a project, but it's also painstaking when you have to change data within the saved files every so often. What I've found to work best is to have file versions. They don't have to necessarily be huge changes between versions, but just have some way to keep track of them. I used this a whole lot in my Cubix 3D map editor, and it worked very well. Just keep a single integer telling you which version this file is, and keep your old routines around to handle those older versions. You might have to fill in the blanks of older files to catch up with new versions. Then making changes to your files is only a matter of copying your loading & saving code for a new version, and making the necessary changes. Then you have a better file format, while still being able to load older saved games (and maps, etc.). As far as figuring out exactly what to save, and how to load it again, you'll have to figure that one out on your own. Just figure out what all data you need, and try to reproduce the rest. Good luck. |
| ||
I wanted to post a question about the Str$-command, about what would happen if there was a pointer to another type inside that type, but I tried it myself first. And the result was interesting: Type test Field x Field t2.test2 End Type Type test2 Field y End Type t.test = New test t\t2 = New test2 t\x = Rand(1, 100) t\t2\y = Rand(200, 500) Print t\x Print t\t2\y Print Print Str$(t) WaitKey() This actually printed: 36 425 [36,[425]] Blitz sees that field "t2" inside "t" is actually a pointer to another type, and therefore it prints the contents of that sub-type in an extra pair of brackets. Didn't know this could be done. |
| ||
Unfortunately it DOES NOT support arrays within types!! If you have something like FIELD things[5] Then STR$() will print it as [???] |