Saving a scene . . .
by Kevin James · in Torque Game Builder · 03/07/2008 (3:43 pm) · 16 replies
Hey guys,
I'm having some trouble saving the exact status of a level to a scene file. There is a bunch of ships that you can move around, and I want them to be persistent: you close the game and open it, and they are in the same place.
To try and accomplish this, I tried using this method of the scenegraph:
That line doesn't work and yields this error:
I tried looking at the FileIOTest game but all the scripts are Greek to me. The notes on FileI/O for version 1.6 are of no help to me either. It doesn't matter what string I pass in to saveScene -- it never works and always gives me the "Unable to open file" error. Could someone let me know what I'm doing wrong with this pesky method? I'd love to hear any other ideas on persistent levels as well!
Thanks,
Kevin
I'm having some trouble saving the exact status of a level to a scene file. There is a bunch of ships that you can move around, and I want them to be persistent: you close the game and open it, and they are in the same place.
To try and accomplish this, I tried using this method of the scenegraph:
sceneWindow2D.getSceneGraph().saveScene("game/data/levels/test.t2d");That line doesn't work and yields this error:
t2dSceneGraph::saveScene() - Unable to open file 'game/data/levels/test.t2d' for Scene-Save
I tried looking at the FileIOTest game but all the scripts are Greek to me. The notes on FileI/O for version 1.6 are of no help to me either. It doesn't matter what string I pass in to saveScene -- it never works and always gives me the "Unable to open file" error. Could someone let me know what I'm doing wrong with this pesky method? I'd love to hear any other ideas on persistent levels as well!
Thanks,
Kevin
About the author
Computer security, digital forensics, and platform jumper enthusiast. shells.myw3b.net/~syreal/
#2
Since the error says that it is "unable to open" the file, I was digging through the newest TGB reference doc and found a FileStreamObject. I used this method:
That successfully deletes everything in the level file -- but the error in with the saveScene method persists. I am wondering if saveScene works at all. I am also wondering how the LevelBuilder saves levels. In 1.1.3 you can actually look at the GUI of the level builder in the GUI editor, but I've been quite unsuccessful in uncovering anything yet. For some reason I'm actually having fun being perplexed with TGB. Its mostly anticipation of actually getting the cotton picking thing to work!
03/07/2008 (5:42 pm)
Thanks for the effort Phil. ;) I was about to be scared because I already tried that on my computer and it didn't work!Since the error says that it is "unable to open" the file, I was digging through the newest TGB reference doc and found a FileStreamObject. I used this method:
%fileStreamObject.open("game/data/levels/test.t2d", "Write");That successfully deletes everything in the level file -- but the error in with the saveScene method persists. I am wondering if saveScene works at all. I am also wondering how the LevelBuilder saves levels. In 1.1.3 you can actually look at the GUI of the level builder in the GUI editor, but I've been quite unsuccessful in uncovering anything yet. For some reason I'm actually having fun being perplexed with TGB. Its mostly anticipation of actually getting the cotton picking thing to work!
#3
I figured it out. It took me 5 hours or so, but I have a "persistent" level now. I'm beat and its pretty late; I'll post the details of how I figured it out, as well as the solution, tomorrow.
All glory to God. I don't know how I figure these sorts of things out.
Peace out,
Kevin
03/07/2008 (8:36 pm)
Phil and anyone else reading,I figured it out. It took me 5 hours or so, but I have a "persistent" level now. I'm beat and its pretty late; I'll post the details of how I figured it out, as well as the solution, tomorrow.
All glory to God. I don't know how I figure these sorts of things out.
Peace out,
Kevin
#4
I also looked at how the level builder saves levels, and it was extremely convoluted and confusing just like every script that the level builder utilizes. What I got from it was that you can save the contents of a level by using the writeObject method of the file object on your action scenegraph.
Even with that step figured out, one problem still remained: the file being written was like a ghost, and actually had no bearing on the level file itself. I knew that FileStreamObjects could effectively change files, so I tried using that. Eventually, I came up with this solution where $pref::levelFile is the path to my level - in my case it is "game/data/levels/test.t2d".
I'll probably be posting a resource with this in it. Also, I'll be posting a bug report - it really seems like you should not have to even call a function like "readAndWriteLevelFile" - writeObject should be enough. At least there is a work around!
03/08/2008 (6:23 am)
Despite my prior comments on not being able to understand the examples or the documentation on file I/O, those elements were actually indispensable in figuring out how to save levels.I also looked at how the level builder saves levels, and it was extremely convoluted and confusing just like every script that the level builder utilizes. What I got from it was that you can save the contents of a level by using the writeObject method of the file object on your action scenegraph.
Even with that step figured out, one problem still remained: the file being written was like a ghost, and actually had no bearing on the level file itself. I knew that FileStreamObjects could effectively change files, so I tried using that. Eventually, I came up with this solution where $pref::levelFile is the path to my level - in my case it is "game/data/levels/test.t2d".
function doSave()
{
%sceneGraph = sceneWindow2D.getSceneGraph();
// Save the Level
if(isWriteableFileName($pref::levelFile))
{
%fo = new FileObject();
if(%fo.openForWrite($pref::levelFile))
{
%fo.writeObject(%scenegraph, "%levelContent = ");
%fo.close();
}
%fo.delete();
readAndWriteLevelFile($pref::levelFile);
}
}
function readLevelFile(%level)
{
%fo = new FileObject();
if(!%fo.openForRead(%level))
{
%fo.delete();
return "Failed to open file for read after successful write!";
}
echo("Successfully re-opened the file for Read");
echo("Dumping the contents of the below :");
echo("Begin File Text ------------------- ");
while(!%fo.isEOF())
{
%line = %fo.readLine();
echo(%line);
}
echo("End File Text ------------------- ");
%fo.delete();
}
function readAndWriteLevelFile(%level)
{
%fso = new FileStreamObject();
if(!%fso.open(%level, "Write"))
{
%fso.delete();
return "Failure to open level file for writing file stream object.";
}
%fo = new FileObject();
if(!%fo.openForRead(%level))
{
%fo.delete();
return "Failure to open level file for reading as file object.";
}
warn("//File opened for write");
warn("//These lines being written to level file:");
warn("//Begin File Text ------------------- ");
while(!%fo.isEOF())
{
%line = %fo.readLine();
%fso.writeLine(%line);
warn(%line);
}
warn("//End File Text ------------------- ");
%fo.delete();
%fso.delete();
}Calling "doSave();" will save you level as it is. The "readLevelFile" is unnecessary in saving levels, I just had it for testing the scripting.I'll probably be posting a resource with this in it. Also, I'll be posting a bug report - it really seems like you should not have to even call a function like "readAndWriteLevelFile" - writeObject should be enough. At least there is a work around!
#5
Thank you, I was overly confused on how to do this as well, I'll try this out as soon as I get home!
03/10/2008 (5:58 pm)
Kevin, Thank you, I was overly confused on how to do this as well, I'll try this out as soon as I get home!
#6
Glad to help! This code could actually be streamlined a bit. I'll posted a revised version once I write it. (Lots of stuff going on, you know!)
03/10/2008 (6:39 pm)
Acy,Glad to help! This code could actually be streamlined a bit. I'll posted a revised version once I write it. (Lots of stuff going on, you know!)
#7
First off, thanks for the heads up on this! Very good stuff!
I've used your above code, modified a little to accommodate my own situation, and resulting in a decent effect. It didn't immediately dawn on me that what was being saved was not actually an object, but a snapshot of an object. It makes for a good start but a snapshot will only go so far.
Take this as an example:
Now imagine we went through your outlined save procedure, saving "someObj" to file. We'd find that the "MyMembers" SimSet would save with an ID (say 1383) instead of the actual SimSet. There would be no sign of MemberOne, even though it was an object stored in MyMembers.
So, we'd have to iterate our save over both someObj and someObj.MyMembers in order to get it to "really" save, rather than just save with a snapshot holding object IDs that it used in an earlier instance.
Take it a step further. Imagine that MemberOne also contains a SimSet. The result would require us to continue to go deeper, making for potentially complex code just to save an "actual" copy of an object, as opposed to just a snapshot of it as it was running at some time in the past.
Maybe I'm just daft (which could seriously be the case) but I guess what I'm looking to accomplish is more of a clone of an object and not merely a snapshot copy.
Does that make sense?
Not that what you have here isn't good--its great! It could very well work for what I need though it means that I need to plan and design to a much deeper extent to account for how objects can be (or are) saved in TGB/TS. That might be the issue, at heart, for me, as I thought that saving objects would be more ... comprehensive, I suppose you might say.
I'm forced to ponder that there must be a better way. When you saved your own scenegraphs, did none of your objects have SimSets or references to objects outside of the scenegraph (as another example)?
03/14/2008 (5:57 am)
Hi Kevin,First off, thanks for the heads up on this! Very good stuff!
I've used your above code, modified a little to accommodate my own situation, and resulting in a decent effect. It didn't immediately dawn on me that what was being saved was not actually an object, but a snapshot of an object. It makes for a good start but a snapshot will only go so far.
Take this as an example:
new t2dSceneObject(someObj)
{
MyMembers = new SimSet();
};
new t2dSceneObject(MemberOne)
{
Class = "SomeClass";
};
someObj.MyMembers.Add(MemberOne);Now imagine we went through your outlined save procedure, saving "someObj" to file. We'd find that the "MyMembers" SimSet would save with an ID (say 1383) instead of the actual SimSet. There would be no sign of MemberOne, even though it was an object stored in MyMembers.
So, we'd have to iterate our save over both someObj and someObj.MyMembers in order to get it to "really" save, rather than just save with a snapshot holding object IDs that it used in an earlier instance.
Take it a step further. Imagine that MemberOne also contains a SimSet. The result would require us to continue to go deeper, making for potentially complex code just to save an "actual" copy of an object, as opposed to just a snapshot of it as it was running at some time in the past.
Maybe I'm just daft (which could seriously be the case) but I guess what I'm looking to accomplish is more of a clone of an object and not merely a snapshot copy.
Does that make sense?
Not that what you have here isn't good--its great! It could very well work for what I need though it means that I need to plan and design to a much deeper extent to account for how objects can be (or are) saved in TGB/TS. That might be the issue, at heart, for me, as I thought that saving objects would be more ... comprehensive, I suppose you might say.
I'm forced to ponder that there must be a better way. When you saved your own scenegraphs, did none of your objects have SimSets or references to objects outside of the scenegraph (as another example)?
#8
Run that and then change the blurb that writes the object to this:
Run it again and you'll see that we've actually maintained everything. This is a simple example, however. I have to think that it could become convoluted and very complex when trying to save a more sophisticated object. Maybe it just appears that way on the face of things and isn't so bad as I suspect?
03/14/2008 (6:38 am)
My example above might not be solid and I wanted to double check myself to make sure I wasn't in error. Copy this code into the game.cs file of a new project and run it to see what I mean:new t2dSceneObject(someObj)
{
scenegraph = sceneWindow2D.getScenegraph();
};
someObj.MyMembers = new Simset(TestSet);
new t2dSceneObject(MemberOne)
{
Class = "SomeClass";
scenegraph = sceneWindow2D.getScenegraph();
};
someObj.MyMembers.Add(MemberOne);
%aFile = expandFilename("/testing.txt");
// write someObj to file
if(isWriteableFilename(%aFile))
{
%fo = new FileObject();
if(%fo.openForWrite(%aFile))
{
%fo.writeObject(someObj, "$someNewObj = ");
%fo.close();
}
%fo.delete();
}
// as a sanity check, lets see what we have ...
echo("---------------------");
echo("Count of MyMembers SimSet objects: " @ someObj.MyMembers.getCount());
echo("Listing MyMembers SimSet objects: " );
someObj.MyMembers.listObjects();
echo("MemberOne.class = " @ MemberOne.Class);
echo("---------------------");
// now that we've saved, get rid of those objects so
// we know what gets loaded hereafter is from the file
memberOne.delete();
someObj.delete();
// start our read file process
%fo = new FileObject();
if(!%fo.openForRead(%aFile))
{
%fo.delete();
error("Error with read.");
return;
}
echo("Start read.");
while(!%fo.isEOF())
{
%line = %fo.readLine();
echo(%line);
%tmpObj = %tmpObj @ %line;
}
eval(%tmpObj);
%fo.close();
%fo.delete();
// as a sanity check, lets see what we have now ...
echo("---------------------");
echo("Count of MyMembers SimSet objects: " @ $someNewObj.MyMembers.getCount());
echo("Listing MyMembers SimSet objects: " );
$someNewObj.MyMembers.listObjects();
echo("MemberOne.class = " @ MemberOne.Class);
echo("---------------------");Run that and then change the blurb that writes the object to this:
if(%fo.openForWrite(%aFile))
{
%fo.writeObject(someObj, "$someNewObj = ");
%fo.writeObject(someObj.MyMembers, "$someNewObj.MyMembers = ");
%fo.writeObject(MemberOne, "$NewMemberOne = ");
%fo.close();
}Run it again and you'll see that we've actually maintained everything. This is a simple example, however. I have to think that it could become convoluted and very complex when trying to save a more sophisticated object. Maybe it just appears that way on the face of things and isn't so bad as I suspect?
#9
Thanks for the encouragement! Yes, the test level I was running didn't use any SimSets. Basically, my goal was to save a level file that would be easily accessible for my friends and I so that we could email different phases of the level to each other. This code was for an email "board" game that we're working on. In the next few weeks I'll probably be incorporating SimSets into the game -- I'll keep this code in mind, it appears that you have some good ideas here!
03/14/2008 (1:05 pm)
Derelict,Thanks for the encouragement! Yes, the test level I was running didn't use any SimSets. Basically, my goal was to save a level file that would be easily accessible for my friends and I so that we could email different phases of the level to each other. This code was for an email "board" game that we're working on. In the next few weeks I'll probably be incorporating SimSets into the game -- I'll keep this code in mind, it appears that you have some good ideas here!
#10
I just wanted to drop in to give a follow-up on my own experience with this "saving" and "loading" bit.
Insofar as the saving of "nested" objects go, I believe I can come up with a fairly elegant bit of code that'll cover such concerns and I'll post it when I get a chance to finish scribbling it out--I've been working on other things and haven't had a chance to dive back into this part as of yet.
In the interim, I'd mention that it appears SimGroups may at least partially resolve it because SimGroups *may* iterate not only over their member objects during a save, but through any objects nested within those members. I can't confirm that as true just yet, or whether this potential recursion during a save goes deeper than just a single nesting, if at all. I'll be testing it out soon, I hope.
I'd add that using SimGroups may be more ideal in some (many?) circumstances than SimSets due to the availability of FindObjectByInternalName() (and therefore the InternalName property swaps from near useless for objects within a SimSet to very useful within a SimGroup).
As a result, SimGroups could provide a means to maintain game and object state in a more meaningful way while providing much the same functionality as a SimSet; they are, after all, derived from SimSet as it is. Something to consider, in any case ...
03/16/2008 (9:20 am)
Hello again Kevin,I just wanted to drop in to give a follow-up on my own experience with this "saving" and "loading" bit.
Insofar as the saving of "nested" objects go, I believe I can come up with a fairly elegant bit of code that'll cover such concerns and I'll post it when I get a chance to finish scribbling it out--I've been working on other things and haven't had a chance to dive back into this part as of yet.
In the interim, I'd mention that it appears SimGroups may at least partially resolve it because SimGroups *may* iterate not only over their member objects during a save, but through any objects nested within those members. I can't confirm that as true just yet, or whether this potential recursion during a save goes deeper than just a single nesting, if at all. I'll be testing it out soon, I hope.
I'd add that using SimGroups may be more ideal in some (many?) circumstances than SimSets due to the availability of FindObjectByInternalName() (and therefore the InternalName property swaps from near useless for objects within a SimSet to very useful within a SimGroup).
As a result, SimGroups could provide a means to maintain game and object state in a more meaningful way while providing much the same functionality as a SimSet; they are, after all, derived from SimSet as it is. Something to consider, in any case ...
#11
03/22/2008 (6:11 am)
I was researching a writeObject function for the StreamObject and FileStreamObject, but found none, so unfortunately, I can't take out the FileObject step in this process.
#12
03/23/2008 (6:16 am)
Just curious, what operating system is this being tested under. Has this been verified to work under Vista?
#13
03/23/2008 (3:35 pm)
No. I haven't ever tested it on Vista, but it most likely doesn't work. I'm on an XP machine, so the OS still allows writing files to the Program Files. There's a chance that the FileStreamObject bypasses Vista's useless security, but we'd have to test it to be sure.
#14
03/25/2008 (3:31 am)
This helps me a lot. Would this be able to work with changing scenes and not simply quitting? It's late and i've been pulling my hair out with this IO stuff.
#15
Clay: What do you mean by "changing scenes"? Can you elaborate or am I just being daft (which would still call for you to elaborate *grin*)?
03/25/2008 (8:33 am)
Stanley: I have used this on Vista--not via a build, solely via the editor/dev environment--and it does work. It writes the resulting file to an AppData/Roaming/ locale. (I believe it appeared within a directory that was created named "Independent". Not GarageGames or anything of that ilk.) Clay: What do you mean by "changing scenes"? Can you elaborate or am I just being daft (which would still call for you to elaborate *grin*)?
#16
The code I included above just saves a scene, it doesn't deal with quiting anything. If you just want to change scenes, then you just load another level.
Everyone,
The above code automatically saves the level in your game directory's level file, but another option would be to save it in the AppsData windows folder using the FileObject then just load it from there as well. I might work on that.
Cheers
03/26/2008 (6:43 pm)
Clay,The code I included above just saves a scene, it doesn't deal with quiting anything. If you just want to change scenes, then you just load another level.
Everyone,
The above code automatically saves the level in your game directory's level file, but another option would be to save it in the AppsData windows folder using the FileObject then just load it from there as well. I might work on that.
Cheers
Associate Phillip O'Shea
Violent Tulip
Edit: I just checked this, doesn't work either. In my defence, it works for everything else damnit!