Game Development Community

How to create a Save/load system

by Edwin Gomez · in Game Design and Creative Issues · 05/16/2007 (8:44 am) · 16 replies

I'm working over the starter.fps and i don't have any idea how to use or create a save and load system. Any help will be appreciated

#1
05/16/2007 (8:54 am)
Well I suppose it largely depends on what you want to save and load. For me I just wanted to save a different set of variables so I had all variables start with something like $profile::playerWeapon = ""; and have a gui that allows the player to alter the values of those variables. Then when he saves, it allows him to choose a name and it exports the file as a *.cs in much of the same manner used in main.cs to export prefs.

To load I just have a function for an list gui that sets up an array of *.cs files in that folder. I got a bit fancy and had it remove parts of the file name and add them on when needed for display purposes. If you want to prevent hackers in the manner I did you will have to add the delete file functionality found on some resource or forum here, I must admit I forget where I found it. Then I have it execute the file so it compiles to a DSO then immediately deletes the orginal *.cs file.
#2
05/16/2007 (5:36 pm)
Thanks for the answer but, do you have any *.cs example to understand clearly and thanks for your support.
#3
05/16/2007 (7:50 pm)
Hmm the problem with that Edwin is that your profile says you are not a Torque Engine owner. Sharing such things in a public area is prohibited last I heard, especially with those who haven't bought the product.

If an employee says different, then yes I will show what I did. Otherwise, sorry but thats all I can give.
#4
05/17/2007 (6:17 am)
Sharing C++ and engine information is prohibited. Sharing Torque Script information and help is allowed, which is why the Torque Game Engine Public area exists =).

Gotta help the new guys get their feet wet, so they know what they are getting into and feel like they can get help from our community.
#5
05/17/2007 (6:31 am)
What exactly are you trying to save? I've written a few save systems that go from saving what mission you're on to saving a large, dynamic world.
#6
05/17/2007 (6:36 am)
This resource is a great starting point: Save/Load System

It's script driven, so it should suit your needs. Right now, it only covers writing the character name, mission name, and a checkpoint. It can easily be expanded to cover ammo, weapons, and stats, as well as other critical variables you need saved out.
#7
05/17/2007 (7:56 am)
It would be nice if someone could write a good tutorial on the Save/Load system. I never good get to work right.
#8
05/17/2007 (10:15 am)
OK well my solution is not entirely script-based. I wanted mine to work in a way to prevent would-be game hackers from modifying their profile stats or even viewing them.

However, the code I used is in a public forum area, so this is not my fault.

www.garagegames.com/mg/forums/result.thread.php?qt=7555

I added Daniel Eden's code not script and then made my own.

I'll show what I did for an example to work from. Modify it as you see fit. Keep in mind as well I use bitmap buttons in my gui so you may need to change things if you are copying.

First, I needed a new set of saveable variables that didn't have anything to do with config.cs or pref.cs so I created two new .cs files and added their execution in the main.cs file you would find in starter.fps or racing

// Defaults console values
exec("./client/defaults.cs");
exec("./server/defaults.cs");
exec("./client/clientProfileDefaults.cs");


// Preferences (overide defaults)
exec("./client/prefs.cs");
exec("./client/clientProfilePrefs.cs");
exec("./server/prefs.cs");

Also, in the same file in the function onExit()
echo("Exporting client prefs");
   export("$pref::*", "./client/prefs.cs", False);
   export("$clProfPref::*", "~/client/clientProfilePrefs.cs", False);

Since these will compile into DSO files they should be fine. However that last step can be skipped if you would like, I just needed it for the purposes of my game since the varaibles are needed in my game at startup.


My variables in clientProfileDefaults.cs look like this:
$clProfPref::chassisDB = "Buggy";
$clProfPref::vehicleTrans = "true";
$clProfPref::vehicleFWheel = "BuggyFWheel3";
$clProfPref::vehicleRWheel = "BuggyRWheel3";
$clProfPref::vehicleSprings = "buggySpring1";
$clProfPref::wheelsPowered = 4;
$clProfPref::vehicleEngine = "buggyEngine1";

Now the file clientProfileDefaults.cs is kept in the start.fps/client/ directory.

Gonna break this up into separate posts.
#9
05/17/2007 (10:23 am)
Next, I have my functions in another .cs file called profileGui.cs (held in starter.fps/client/scripts) which you will have to add its execution to the init.cs like so:

exec("./scripts/profileGui.cs");

Just add it around the others .cs executions in the file.

Now for the profileGui.cs:

$saveVPName = "";
$vProfileDir = "main/client/profiles/*.dso";
$fileNameLd = "";
$fileNameDel = "";

function profileGui::onWake(%this)
{
   mainMenuGui.hideButtons();
}


function exportVProfile()
{
   echo("Export vehicle profile function called");
   if($saveVPName $= "")
   {
      echo("If Check successful");
      return;
   }
   export("$clProfPref::*", "main/client/profiles/" @ $saveVPName @ ".cs", False);

   eval(%pfFile = "main/client/profiles/" @ $saveVPName @ ".cs");
   exec(%pfFile);

   deleteFile(%pfFile);

   $saveVPName = "";

   Canvas.popDialog(saveVProfile);
}

//----------------------------------------
function loadVProfileGui::onWake()
{
   vehicleProfileList.displayByType();
}   

//----------------------------------------
function vehicleProfileList::displayByType()
{
   vehicleProfileList.clear();
   %i = 0;
   for(%file = findFirstFile($vProfileDir); %file !$= ""; %file = findNextFile($vProfileDir)) 
   { 
      %filetmp = Strreplace(%file, "main/client/profiles/", "");
      %file = Strreplace(%filetmp, ".cs.dso", "");
      vehicleProfileList.addRow(%i++, %file);
   }

   vehicleProfileList.sort(0);
   vehicleProfileList.scrollVisible(0);
}   
//----------------------------------------
function vehicleProfileList::onSelect(%this, %row, %text)
{
   $fileNameLd = "main/client/profiles/" @ %text @ ".cs";
   $fileNameDel = "main/client/profiles/" @ %text @ ".cs.dso";
}

function deleteVProfile()
{
   deleteFile($fileNameDEL);
   vehicleProfileList.displayByType();
}

function loadVProfile()
{
   exec($fileNameLd);
   Canvas.popDialog(loadVProfileGui);
   updateVehicle($mmThis);
}

function updateVehicle(%this)
{
   // Combination create player and drop him somewhere
   $mmPlayer.delete();
   $mmVehicle.delete();
   %spawnPoint = pickSpawnPoint();
   %this.createMMPlayer(%spawnPoint);
   %spawnPoint = pickVehicleSpawnPoint();
   %this.createMMVehicle(%spawnPoint);
   mmRotate();
}
#10
05/17/2007 (10:25 am)
Finally the guis. profileGui.gui is first. Remeber to add these to the init.cs file as well. Look at the existing gui executions as an example.

//--- OBJECT WRITE BEGIN ---
new GuiControl(profileGui) {
   canSaveDynamicFields = "1";
   Profile = "GuiModelessDialogProfile";
   HorizSizing = "right";
   VertSizing = "bottom";
   position = "0 0";
   Extent = "640 480";
   MinExtent = "8 2";
   canSave = "1";
   Visible = "1";
   hovertime = "1000";

   new GuiBitmapButtonTextCtrl(close) {
      canSaveDynamicFields = "0";
      Profile = "DTButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "528 444";
      Extent = "108 28";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      Command = "Canvas.popDialog(profileGui);   mainMenuGui.showButtons();";
      hovertime = "1000";
      text = "CLOSE";
      groupNum = "-1";
      buttonType = "PushButton";
      bitmap = "./blackblank";
   };
   new GuiTextEditCtrl() {
      canSaveDynamicFields = "0";
      Profile = "GuiTextEditProfile";
      HorizSizing = "right";
      VertSizing = "bottom";
      position = "394 450";
      Extent = "119 18";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      Variable = "$clProfPref::chassisDB";
      hovertime = "1000";
      maxLength = "1024";
      historySize = "0";
      password = "0";
      tabComplete = "0";
      sinkAllKeyEvents = "0";
      password = "0";
      passwordMask = "*";
   };
   new GuiBitmapButtonTextCtrl(saveVEProfile) {
      canSaveDynamicFields = "0";
      Profile = "DTButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "528 412";
      Extent = "108 28";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      Command = "Canvas.pushDialog(saveVProfile);";
      hovertime = "1000";
      text = "SAVE";
      groupNum = "-1";
      buttonType = "PushButton";
      bitmap = "./blackblank";
   };
   new GuiBitmapButtonTextCtrl(close) {
      canSaveDynamicFields = "0";
      Profile = "DTButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "528 348";
      Extent = "108 28";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      Command = "updateVehicle($mmThis);";
      hovertime = "1000";
      text = "APPLY";
      groupNum = "-1";
      buttonType = "PushButton";
      bitmap = "./blackblank";
   };
   new GuiBitmapButtonTextCtrl(loadVEProfile) {
      canSaveDynamicFields = "0";
      Profile = "DTButtonProfile";
      HorizSizing = "relative";
      VertSizing = "relative";
      position = "528 380";
      Extent = "108 28";
      MinExtent = "8 2";
      canSave = "1";
      Visible = "1";
      Command = "Canvas.pushDialog(loadVProfileGui);";
      hovertime = "1000";
      text = "LOAD";
      groupNum = "-1";
      buttonType = "PushButton";
      bitmap = "./blackblank";
   };
};
//--- OBJECT WRITE END ---
#11
05/17/2007 (10:26 am)
Then the saveVPProfile.gui:
//--- OBJECT WRITE BEGIN ---
new GuiControl(saveVProfile) {
   canSaveDynamicFields = "0";
   Profile = "GuiDefaultProfile";
   HorizSizing = "width";
   VertSizing = "height";
   position = "0 0";
   Extent = "640 480";
   MinExtent = "8 8";
   canSave = "1";
   Visible = "1";
   hovertime = "1000";

   new GuiWindowCtrl(profileManager) {
      canSaveDynamicFields = "0";
      Profile = "GuiWindowProfile";
      HorizSizing = "center";
      VertSizing = "center";
      position = "145 202";
      Extent = "349 75";
      MinExtent = "48 0";
      canSave = "1";
      Visible = "1";
      hovertime = "1000";
      text = "SAVE PROFILE";
      maxLength = "255";
      resizeWidth = "0";
      resizeHeight = "0";
      canMove = "0";
      canClose = "0";
      canMinimize = "0";
      canMaximize = "0";
      minSize = "50 50";

      new GuiTextCtrl() {
         canSaveDynamicFields = "0";
         Profile = "GuiTextProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         position = "9 38";
         Extent = "63 18";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         hovertime = "1000";
         text = "Profile Name:";
         maxLength = "255";
      };
      new GuiTextEditCtrl() {
         canSaveDynamicFields = "1";
         Profile = "GuiTextEditProfile";
         HorizSizing = "right";
         VertSizing = "bottom";
         position = "76 40";
         Extent = "119 18";
         MinExtent = "8 2";
         canSave = "1";
         Visible = "1";
         Variable = "$saveVPName";
         hovertime = "1000";
         maxLength = "255";
         historySize = "0";
         password = "0";
         tabComplete = "0";
         sinkAllKeyEvents = "0";
         password = "0";
         passwordMask = "*";
      };
      new GuiBitmapButtonTextCtrl() {
         canSaveDynamicFields = "0";
         Profile = "DTNmTextButtonProfile";
         HorizSizing = "right";
         VertSizing = "top";
         position = "199 38";
         Extent = "70 23";
         MinExtent = "8 8";
         canSave = "1";
         Visible = "1";
         Command = "exportVProfile();";
         hovertime = "1000";
         text = "SAVE";
         groupNum = "-1";
         buttonType = "PushButton";
         bitmap = "./blackblank";
      };
      new GuiBitmapButtonTextCtrl() {
         canSaveDynamicFields = "0";
         Profile = "DTNmTextButtonProfile";
         HorizSizing = "right";
         VertSizing = "top";
         position = "272 38";
         Extent = "70 23";
         MinExtent = "8 8";
         canSave = "1";
         Visible = "1";
         Command = "Canvas.popDialog(saveVProfile);";
         hovertime = "1000";
         text = "CANCEL";
         groupNum = "-1";
         buttonType = "PushButton";
         bitmap = "./blackblank";
      };
   };
};
//--- OBJECT WRITE END ---
#12
05/17/2007 (10:29 am)
Finally just add a button somewhere you want with the command assigned to it like:


Canvas.pushDialog(profileGui);

Now keep in mind this will not work exactly the way you want for your game needs, it is designed for my own and the fact that I use the mission in the main menu resource instead of the objectview gui for a much better effect. Laso my directory struction is much different than the exmples so if you see references to main/client directories, change it to suit your needs.

However, the script I have show here should give you a few examples of what to try.
#13
05/25/2007 (7:38 am)
Well thank you very much for all your help
#14
05/25/2007 (9:13 am)
Thanks for information.
#15
07/26/2007 (5:47 pm)
Thanks a ton
#16
07/26/2007 (8:46 pm)
NP, Its nice to be able to give advice for a change rather than asking for it all of the time.