Dear Community,
In this tutorial I show you the basic structure that I've used to be able to:
1. save dynamically created levels to hard disk
2. save in-game playing state of the entire 3D game world (including flying projectiles and moving platforms), and save/reload as often as wanted
3. be able to even turn the computer off, restart it, and then load the user-created level, and then load the in-game save state from a previous gaming session within that level before computer was turned off.
All of this I do with just two Unrealscript functions at the core of it all:
~~~
How to Save Dynamically Generated Levels
The biggest probem I encountered when trying to make the save system for my game was that my levels are dynamically generated by the user, and I can never know how many objects the user is creating. I make new levels all the time using my game engine, and add to them spontaneously, and using my current save system, I can permanently save my dynamically generated / updated levels. I show this in the video where I add a moving platform to a level from a pervious video of mine.
So there are two main problems I had to tackle.
1. I never know how many objects the user will create for a levels, so how I can I generate a save file for unknown amount of data?
2. BasicSaveObject cannot store dynamically generated objects, only simple variable types and structures.
Here's what a sample save file for one of my levels might look like:
Please note, your actual save file should be a CLASS, all by itself, composed ONLY of variable declarations.
Then write two other classes, one to load data and one to save data for that simple save file of just variable declarations.
Notice I am storing a huge amount of custom struct variable space.
My save file for my actual game engine is storing space for over 15,000 custom struct variables.
But this is not a problem, because it is just a big storage bank.
Not all of the variables need to get accessed by my game engine.
And the save and load classes will ONLY access the data needed, so saving 5 walls vs. 3000 IS much faster, because the save engine I wrote knows to stop after 5 walls are done and not check all 3000 variables.
So the actual save file of variables can be huge, but then you write separate classes for save and load engines to make things efficient.
Note I am only setting aside data for 3003 walls, which are the dynamically generated user-created objects in-game
So there is a limit in this way for my way of doing saving, because the user cannot create more than 3000 walls for example, or currently more than 200 3dscripting triggers.
But for my game that is a reasonable limit.
You could easily increase the number to 6000 or 18000 walls depending on your game's needs, but I am just letting you know:
It's okay to have a huuuge amount of data available to be saved to in your game's save file.
As you saw in my video, saving and loading can be VERY fast if you do the code well.
So how big are the save files for my game engine?
~~~
My Current Save File Size on Disk
Well!
Take a look at this!
The first time I was testing my save engine and saw how big my save files were, even after 15,000 custom struct variables, I was SHOCKED.
This is the file size on my hard drive for the the first level you see me play in the video.
Please understand, this is a fixed file save size, it would not grow if I added more complexity to the level, because I am ALREADY setting aside the space for 3000 walls and 200 3dscripting triggers etc.
File size for level “d5” from the video:

So the entire level you see me load in the game engine, and the save games you saw me make of the ball shooting various parts of the wall structure to pieces, all of that entire data in 3D engine is stored in mere KBs! Less than 1 mb ! per level with all data!
In other words, the file size for my entire dynamically generated levels, for 1 level, is TINY.
Insignificant file size these days.
Also note, that the level was originally created on the 11th, around the time of that earlier tutorial, but now it has been updated with today's date. You really can use unreal engine to store level and save data over weeks or years.
And so the method I am showing you in this tutorial is very viable, for the following
1. speed of loading and saving while in game
2. quantity of data stored (can be easily increase)
3. saving of dynamically generated content including playthrough saves
4. file size is tiny
So once I verified all this in my testing I went full speed ahead with my save system, the basic of which I am showing you in this tutorial
~~~
How to Save Dynamically Generated Objects
Since BasicSaveObject cannot save dynamically generated objects, you must save all the critical data as a Struct composed only of basic variable types
BasicSaveObject CAN save each of these variables:
BasicSaveObject CANNOT save these types
The above does not work, you cannot save dynamically generated class data like that, only simple variables.
~~~
Saving Material and Mesh Data as Strings
In the wallData struct, notice I save the material and mesh as strings, and then you can use DynamicLoadObject to rebuild the level wall's entire state including user-specified mesh and material.
To GET the file path / location of a user-specified material or mesh as a string you can use something like:
PathName yields a string (thank you for telling me this Spoof), and since a string is a simple variable type, you can easily save that in your savefile for your game.
For meshes you can use:
~~~
So Why Use Structs?
So let's suppose you create a really complicated object where you need to store 17 different types of data PER object.
In your save file, remember, the critical fact is:
BasicSaveObject CAN save Structs of infinite simple variable types
so you can store alll your required data in a single struct
and to copy the data from your save file into your dynamically generated in-game object, when you are re-creating the object after loading, it as simple as
you've just compied all the values of all 17 variables.
And note that vectors are structs by themselves, so you are actually copying 15 x 3 + 2 = 47 simple variable types of information, 45 floats and 2 bools.
And all of that can be copied with 1 line:
where data is your custom made Struct.
So my secret key to storing vasts amounts of data to recreate a 3D game world with instant saving and loading is this awesome fact:
UnrealEngine's BasicSaveObject can store custom-made structs of composed of infinite simple variable types.
~~~
So How Does This All Come Together?
So here's what you need to be able to do:
1. Store all required data about a 3D dynamically generated object into a single struct.
2. set aside space in your save file for a sufficient number of structs of this custom struct type.
3. Write a function to recreate your dynamically generated object from the struct after accessing the saved file data.
So for each of my classes of objects in my game engine I use this format:
So notice, each of my classes has a custom struct of ONLY simple variable types that stores all required data.
And each class has a single variable of that struct type, the actual instance of the struct definition.
And each class has an updateStruct() function to ensure all data is up to date when the game is being saved.
~~~
Turning a Custom Struct of simple Variables Back Into an Object
So once you can save all important data of a custom object class as a series of simple variable types like vectors and floats in a custom Struct for just that class.
Now in the main game engine, your game engine needs to know how to turn a struct of that special type back into a dynamic object after loading.
Notice the critical line:
Using a custom struct of any number of simple variable types, with the one line above, you just saved yourself so much work and utlized the power of Unreal' BasicSaveObject. You could have had 300 vectors and rotators in that struct.
But the command
copies all the data from struct2 to struct1 as long as each member of both structs is a simple variable type or another struct, like a vector, and of course both structs have the same type.
So this is complete and total data copying.
Combine this with fact that BasicSaveObject can save custom structs of any length, and you can see how I was able to write a save/load engine for completely dynamically generated levels with custom object types.
~~~
How to Use BasicSave and BasicLoad Object
So now that you understand how to
1. store all required data as simple variable types in a single struct
2. update that struct prior to saving
3. recreate an object from saved struct data
Now we can talk about actually saving and loading files.
You need 3 classes
~~~
First Class: The save file
This should only contain the variable declarations, nothing else, and a sufficient number of variables for each type of object in your level, as per my example earlier.
So using the code in this tutorial:
Let's say you need to store a maximum of 3000 objects of this class:
In your save file you would only have the following:
Notice, you are not storing the class, you are storing the STRUCT type (3DObjectType1Data), which is composed only of other simple variable type structs (like vector) or simple variables.
The reason I did it this way is because BasicSaveObject can only store data as complicated as this format that I am describing.
But for you the programmer it is easy! 1 custom struct per dynamic object that you want to save
~~~
Class 2, the SaveEngine
Now you need to write a save engine, to actually make an instance of your savefile type and copy the required data
levelArrayObjectType1 is a dynamic array that has stored all the objects created dynamically during game time.
Dynamic lists cannot be saved so we must split the dynamic array of expected max size 3000 into 3 arrays of size 1001, because the max static array size is 1024.
The extra 1 in 1001 should never get used, it's just a precaution on my part to avoid crashes.
~~~
Class 3, Loading
For loading you do something similar to saving, but you are calling that function createObjFromStruct and passing in the structs that were saved from
~~~
Instancing Save and Load Engine
When your game engine first starts from the command prompt, you need only make a single instance of your save and load engine to then call their functions.
making them separate objects is mainly an organizational feature, to avoid enormous confusions between save and load functions and when to save and load the object etc etc
~~~
Summary
In this tutorial I”ve covered all the core concepts of how I got my in-game save feature as well as my level saving feature working using only Basic Save Object
Core concepts:
1. Unreal BasicSave/Load Object can only store simple variable types
2. You can make structs of an infinite number of simple variable types for simple copying of data.
3. Write a function to transform simple variable data back into your dynamic object
4. Make a save file storing only your custom struct variable types.
5. Write save/load engines to utlize the save file and save dynamic objects to simple data or convert simple data back into dynamic objects
That's the basics of how I created my turn-the-computer-off-for-a-year and the restart it had load a level you made a long time ago and even load an in-game save that occurred during game-time!
♥
Rama
In this tutorial I show you the basic structure that I've used to be able to:
1. save dynamically created levels to hard disk
2. save in-game playing state of the entire 3D game world (including flying projectiles and moving platforms), and save/reload as often as wanted
3. be able to even turn the computer off, restart it, and then load the user-created level, and then load the in-game save state from a previous gaming session within that level before computer was turned off.
All of this I do with just two Unrealscript functions at the core of it all:
Code:
class'Engine'.static.BasicLoadObject(loadedFile, filename, true, 1); class'Engine'.static.BasicSaveObject(saveFile, filename, true, 1);
How to Save Dynamically Generated Levels
The biggest probem I encountered when trying to make the save system for my game was that my levels are dynamically generated by the user, and I can never know how many objects the user is creating. I make new levels all the time using my game engine, and add to them spontaneously, and using my current save system, I can permanently save my dynamically generated / updated levels. I show this in the video where I add a moving platform to a level from a pervious video of mine.
So there are two main problems I had to tackle.
1. I never know how many objects the user will create for a levels, so how I can I generate a save file for unknown amount of data?
2. BasicSaveObject cannot store dynamically generated objects, only simple variable types and structures.
Here's what a sample save file for one of my levels might look like:
Code:
class saveFile extends object; var bool levelUsesSun; var wallDataStruct walls[1001]; var wallDataStruct walls2[1001]; var wallDataStruct walls3[1001]; var 3DScriptingStructData scripts[200];
Then write two other classes, one to load data and one to save data for that simple save file of just variable declarations.
Notice I am storing a huge amount of custom struct variable space.
My save file for my actual game engine is storing space for over 15,000 custom struct variables.
But this is not a problem, because it is just a big storage bank.
Not all of the variables need to get accessed by my game engine.
And the save and load classes will ONLY access the data needed, so saving 5 walls vs. 3000 IS much faster, because the save engine I wrote knows to stop after 5 walls are done and not check all 3000 variables.
So the actual save file of variables can be huge, but then you write separate classes for save and load engines to make things efficient.
Note I am only setting aside data for 3003 walls, which are the dynamically generated user-created objects in-game
Code:
var wallDataStruct walls[1001]; var wallDataStruct walls2[1001]; var wallDataStruct walls3[1001];
But for my game that is a reasonable limit.
You could easily increase the number to 6000 or 18000 walls depending on your game's needs, but I am just letting you know:
It's okay to have a huuuge amount of data available to be saved to in your game's save file.
As you saw in my video, saving and loading can be VERY fast if you do the code well.
So how big are the save files for my game engine?
~~~
My Current Save File Size on Disk
Well!
Take a look at this!
The first time I was testing my save engine and saw how big my save files were, even after 15,000 custom struct variables, I was SHOCKED.
This is the file size on my hard drive for the the first level you see me play in the video.
Please understand, this is a fixed file save size, it would not grow if I added more complexity to the level, because I am ALREADY setting aside the space for 3000 walls and 200 3dscripting triggers etc.
File size for level “d5” from the video:

So the entire level you see me load in the game engine, and the save games you saw me make of the ball shooting various parts of the wall structure to pieces, all of that entire data in 3D engine is stored in mere KBs! Less than 1 mb ! per level with all data!
In other words, the file size for my entire dynamically generated levels, for 1 level, is TINY.
Insignificant file size these days.
Also note, that the level was originally created on the 11th, around the time of that earlier tutorial, but now it has been updated with today's date. You really can use unreal engine to store level and save data over weeks or years.
And so the method I am showing you in this tutorial is very viable, for the following
1. speed of loading and saving while in game
2. quantity of data stored (can be easily increase)
3. saving of dynamically generated content including playthrough saves
4. file size is tiny
So once I verified all this in my testing I went full speed ahead with my save system, the basic of which I am showing you in this tutorial
~~~
How to Save Dynamically Generated Objects
Since BasicSaveObject cannot save dynamically generated objects, you must save all the critical data as a Struct composed only of basic variable types
BasicSaveObject CAN save each of these variables:
Code:
var bool aBool; var vector v; var rotator r; var string s; struct wallData{ var vector location; var rotator rotation; var string curMaterial; var string curMesh; }; var wallData wall1Data;
Code:
var myObjClass newobj; newobj = spawn(class'myObjClass'...); savefile.obj1 = newObj;
~~~
Saving Material and Mesh Data as Strings
Code:
struct wallData{ var vector location; var rotator rotation; var string curMaterial; var string curMesh; };
Code:
RecreatedWall.StaticMeshComponent.SetMaterial(0,
Material(DynamicLoadObject(
thisIsaStringPointingtoMaterialLocation, class'Material')));
Code:
local string materialPath; materialPath = PathName(preSaveWall.StaticMeshComponent.GetMaterial(0));
For meshes you can use:
Code:
local string meshPath; meshPath = PathName(StaticMeshActor(theActor).StaticMeshComponent.staticmesh);
So Why Use Structs?
So let's suppose you create a really complicated object where you need to store 17 different types of data PER object.
Code:
Struct complicatedStructData { //15 vectors var vector v1; … var vector v15; var bool isOn; var bool IsExcited; }; var complicatedStructData data;
BasicSaveObject CAN save Structs of infinite simple variable types
so you can store alll your required data in a single struct
and to copy the data from your save file into your dynamically generated in-game object, when you are re-creating the object after loading, it as simple as
Code:
recreatedObject.data = loadfile.data1;
And note that vectors are structs by themselves, so you are actually copying 15 x 3 + 2 = 47 simple variable types of information, 45 floats and 2 bools.
And all of that can be copied with 1 line:
Code:
recreatedObject.data = loadfile.data1;
So my secret key to storing vasts amounts of data to recreate a 3D game world with instant saving and loading is this awesome fact:
UnrealEngine's BasicSaveObject can store custom-made structs of composed of infinite simple variable types.
~~~
So How Does This All Come Together?
So here's what you need to be able to do:
1. Store all required data about a 3D dynamically generated object into a single struct.
2. set aside space in your save file for a sufficient number of structs of this custom struct type.
3. Write a function to recreate your dynamically generated object from the struct after accessing the saved file data.
So for each of my classes of objects in my game engine I use this format:
Code:
class 3DObjectType1 extends Actor; struct 3DObjectType1Data { var vector location; var rotator rotation; var rotator startingRotation; var vector starting Location; var string meshPath; var string materialPath; var float customData1; var bool startsOn; //…. //etc, only simple variable typles }; var 3DObjectType1Data data; //called just prior to saving //to make sure all data is accurate //to 3D world updateStruct(){ data.location = self.Location; data.rotation = self.Rotation; data.customData1 = whatever happened ingame prior to saving; } other functions(){} defaultproperties{};
So notice, each of my classes has a custom struct of ONLY simple variable types that stores all required data.
And each class has a single variable of that struct type, the actual instance of the struct definition.
And each class has an updateStruct() function to ensure all data is up to date when the game is being saved.
~~~
Turning a Custom Struct of simple Variables Back Into an Object
So once you can save all important data of a custom object class as a series of simple variable types like vectors and floats in a custom Struct for just that class.
Now in the main game engine, your game engine needs to know how to turn a struct of that special type back into a dynamic object after loading.
Code:
Function createObjFromStruct(3DObjectType1Data loadedData){ local newObj 3DObjectType1; newObj = Spawn(class'3DObjectType1',,,loadedData.Location, loadedData.Rotation, ,true ); //CRITICAL LINE and so simple newObj.data = loadedData; //set material newObj.StaticMeshComponent.SetMaterial( 0, Material(DynamicLoadObject( loadedData.materialPath, class'Material'))); //set mesh newObj.StaticMeshComponent.SetStaticMes h( StaticMesh(DynamicLoadObject( loadedData.meshPath, class'StaticMesh'))); //do any other important things //with struct data //during object re-creation process }
Code:
newObj.data = loadedData;
But the command
Code:
struct1 = struct2
So this is complete and total data copying.
Combine this with fact that BasicSaveObject can save custom structs of any length, and you can see how I was able to write a save/load engine for completely dynamically generated levels with custom object types.
~~~
How to Use BasicSave and BasicLoad Object
So now that you understand how to
1. store all required data as simple variable types in a single struct
2. update that struct prior to saving
3. recreate an object from saved struct data
Now we can talk about actually saving and loading files.
You need 3 classes
~~~
First Class: The save file
This should only contain the variable declarations, nothing else, and a sufficient number of variables for each type of object in your level, as per my example earlier.
So using the code in this tutorial:
Let's say you need to store a maximum of 3000 objects of this class:
Code:
class 3DObjectType1 extends Actor; struct 3DObjectType1Data { var vector location; var rotator rotation; var rotator startingRotation; var vector starting Location; var string meshPath; var string materialPath; var float customData1; var bool startsOn; //…. //etc, only simple variable typles }; var 3DObjectType1Data data;
Code:
class saveFile extends object; //very important var int totalObjectType1s; var 3DObjectType1Data objs[1001]; var 3DObjectType1Data objs2[1001]; var 3DObjectType1Data objs3[1001];
Notice, you are not storing the class, you are storing the STRUCT type (3DObjectType1Data), which is composed only of other simple variable type structs (like vector) or simple variables.
The reason I did it this way is because BasicSaveObject can only store data as complicated as this format that I am describing.
But for you the programmer it is easy! 1 custom struct per dynamic object that you want to save
~~~
Class 2, the SaveEngine
Now you need to write a save engine, to actually make an instance of your savefile type and copy the required data
Code:
class saveEngine extends Object; var saveFile saveFileObj; //make an instance of all the variable data //only some of which will be used function init(){ saveFileObj = new class'saveFile'; } function saveObjectType1(){ local int v; saveFileObj.totalObjectType1s = levelArrayObjectType1.length; //Walls if(levelArrayObjectType1.length> 3000){ `log("too many walls, > 3000"); return; } for(v=0; v < levelArrayObjectType1.length; v++ ){ if(v >= 1000) break; levelArrayObjectType1[v].updateStruct(); saveFileObj.objs[v] = levelArrayObjectType1[v].data; } if (saveCount >= 1000){ for(v=0; v < levelArrayObjectType1.length; v++ ){ if(v >= 1000) break; levelArrayObjectType1[v].updateStruct(); saveFileObj.objs2[v] =levelArrayObjectType1[v].data; } } if (saveCount >= 2000){ for(v=0; v < levelArrayObjectType1.length; v++ ){ if(v >= 1000) break; levelArrayObjectType1[v].updateStruct(); saveFileObj.objs3[v] = levelArrayObjectType1[v].data; } } } function finalSave(string filename){ //save file at very end of all loops //just before finishing //make sure to saaaave class'Engine'.static.BasicSaveObject(saveFile, filename, true, 1); }
Dynamic lists cannot be saved so we must split the dynamic array of expected max size 3000 into 3 arrays of size 1001, because the max static array size is 1024.
The extra 1 in 1001 should never get used, it's just a precaution on my part to avoid crashes.
~~~
Class 3, Loading
For loading you do something similar to saving, but you are calling that function createObjFromStruct and passing in the structs that were saved from
Code:
class LoadEngine extends Object; var saveFile loadFileObj; function init(string filename){ loadFileObj = new class'saveFile'; if (!class'Engine'.static.BasicLoadObject(loadedFile, filename, true, 1)){ `log(“LoadEngine:>> Invalid Filename”); } } function loadObjectType1s(){ for(v = 0;v < loadFileObj.totalObjectType1s; v++){ if(v >= 1000) break; createObjFromStruct(loadFileObj.objs[v]); } if(loadFileObj.totalObjectType1s >= 1000){ for(v = 0;v < loadFileObj.totalObjectType1s; v++){ if(v >= 1000) break; createObjFromStruct(loadFileObj.objs2[v]); } } if(loadFileObj.totalObjectType1s >= 2000){ for(v = 0;v < loadFileObj.totalObjectType1s; v++){ if(v >= 1000) break; createObjFromStruct(loadFileObj.objs3[v]); } } }
Instancing Save and Load Engine
When your game engine first starts from the command prompt, you need only make a single instance of your save and load engine to then call their functions.
making them separate objects is mainly an organizational feature, to avoid enormous confusions between save and load functions and when to save and load the object etc etc
Code:
//after your game .exe runs //maybe in playercontroller class var LoadEngine LoadEngineOBJ; var SaveEngine SaveEngineOBJ; //maybe your dynamic array is stored //in your controller class //adjust save/load engine code accordingly var array<3DObjectType1> levelArrayObjectType1; Simulated Event PostBeginPlay() { //no forget this one plz super.postbeginplay(); LoadEngineOBJ = new class'LoadEngine'; SaveEngineOBJ = new class'SaveEngine'; } //and call your engines like this: load(string s){ LoadEngineOBJ.init(s); LoadengineOBJ.loadObjectType1s(); } save(string s){ SaveEngineOBJ.init(); SaveEngineOBJ.saveObjectType1(); //other save functions //please remember this line //and keep it as always LAST SaveEngineOBJ.FinalSave(s); }
Summary
In this tutorial I”ve covered all the core concepts of how I got my in-game save feature as well as my level saving feature working using only Basic Save Object
Core concepts:
1. Unreal BasicSave/Load Object can only store simple variable types
2. You can make structs of an infinite number of simple variable types for simple copying of data.
3. Write a function to transform simple variable data back into your dynamic object
4. Make a save file storing only your custom struct variable types.
5. Write save/load engines to utlize the save file and save dynamic objects to simple data or convert simple data back into dynamic objects
That's the basics of how I created my turn-the-computer-off-for-a-year and the restart it had load a level you made a long time ago and even load an in-game save that occurred during game-time!
♥
Rama
Comment