Overview:
Context API Reference:
SaveContext Class
LoadContext Class
TableSerializer Classes
Customizing SaveIt:
Customize Saving/Loading Objects
Overview:
Context API Reference:
SaveContext Class
LoadContext Class
TableSerializer Classes
Customizing SaveIt:
Customize Saving/Loading Objects
When saving an object all what the SaveContext of SaveIt does is to store it into an internal TableEntry. The real magic comes when saving a class with a Save method or a class with an associated Serializer, this article describes both:
First, the Save and the Load method.
When saving this class:
1 2 3 4 5 | public class Foo { public int myValue; public string myOtherValue; } |
SaveIt will use a ClassSerializer which basically serialized/deserializes all members of the class, which means that SaveIt will store the myValue and myOtherValue member. Sometimes this is not what you want, for this there are several possibilities, the first one is the usage of the Load and Save Method, for this we extend our existing class:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Foo { public int myValue; public string myOtherValue; void Save(SaveContext context, ref bool allowAutoSerialization) { } void Load(LoadContext context, ref bool allowAutoDeserialization) { } } |
SaveIt will call those methods when serializing or deserializing an instance of the Foo class and you can now specify what should happen when those are called.
Now we can save everything on our way, for this example we save only our int myValue and ignore the string myOtherValue:
1 2 3 4 | void Save(SaveContext context, ref bool allowAutoSerialization) { context.Save(myValue); } |
Thats it, we stored the value myValue, BUT SaveIt will still serialize everything else like it will be done without this Save method.
To change this we can set the bool allowAutoSerialization argument to false. SaveIt will then skip all other automatically performed actions.
1 2 3 4 5 | void Save(SaveContext context, ref bool allowAutoSerialization) { allowAutoSerialization = false; context.Save(myValue); } |
Loading the data inside the Load method, is equally simple:
1 2 3 4 5 6 | void Load(LoadContext context, ref bool allowAutoDeserialization) { allowAutoDeserialization = false; myValue = context.Load<int>(myValue); // or: context.Load(out myValue); } |
The other option to specify custom serialization is by using a type serializer, type serializers inherit from the interface ITypeSerializer:
1 2 3 4 5 6 | public interface ITypeSerializer { bool IsUsable(Type type); void Serialize(object what, SaveContext context); void Deserialize(Type type, LoadContext context); } |
But in most cases you want to use the SaveIt.TypeSerializer.ClassSerializer class to start with, as it performs most operations needed and allows full control on what to serialize/deserialize. But for the next example we will create a fresh Serializer without using ClassSerializer.
Here an example type serializer for the Foo class which does exactly the same thing we did earlier with the Load/Save methods in our Foo class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | public class FooSerializer : ITypeSerializer { public bool IsUsable(Type type) { // We only want that this serializer works for the type Foo return type == typeof(Foo); } public void Serialize(object what, SaveContext context) { // This is needed in case our class we want to serialize can contain the Save method if (context.RunObjectSaveMethod()) { return; } Foo instance = (Foo)what; context.Save(instance.myValue); } public void Deserialize(Type type, LoadContext context) { // First we need to create the instance of Foo we want to fill with our data Foo instance = new Foo(); // Store the new created instance into the context, so SaveIt knows about the created instance and wont create it anew context.CurrentInstance = instance; // This is needed in case our class we want to deserialize can contain the Load method if (context.RunObjectLoadMethod()) { return; } instance.myValue = context.Load<int>(myValue); } } |
After the TypeSerializer has been created, you need to store it into a static list of TypeSerializers SaveIt may use:
1 | ContextBase.TypeSerializers.Add(new FooSerializer()); |
ContextBase is the base class from the LoadContext and SaveContext class in the SaveIt namespace.
Namespace: SaveIt.TableSerializer
Use these to serialize and deserialize the stored data from the Save and LoadContexts.
SaveIt provides some basic TableSerializers, all based upon the BinaryTableSerializer class.
All inhertied classes are able to compress and uncompress the serialized/deserialized data by setting the UseCompression property to true.
Following BinaryTableSerializers exist:
File
Loads and Saves the data to a file specified in the constructor.
Leaving away a file extension delimiter (a simple dot) will add .saveIt as file extension.
PlayerPrefs
Loads and Saves the data to the PlayerPrefs entry with the given name.
Example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | using SaveIt; ... // This will load/save the data from/to the file "MySaveGame.dat" TableSerializer.File tableSerializer = new TableSerializer.File("MySaveGame.dat"); // This will load/save the data from/to the PlayerPrefs string entry "SaveData" TableSerializer.PlayerPrefs tableSerializer = new TableSerializer.PlayerPrefs("SaveData"); ... SaveContext saveContext = new SaveContext(tableSerializer); saveContext.Save(currentLevel, "currentLevel"); saveContext.Save(score, "score"); saveContext.Flush(); // NEVER FORGET THIS! ... LoadContext loadContext = new LoadContext(tableSerializer); currentLevel = loadContext.Load<int>("currentLevel"); score = loadContext.Save<int>("score"); |
Namespace: SaveIt
LoadContext(ITableSerializer tableSerializer = null, ResourceEntry[] resourceEntries = null)
Parameters:
Creates a new LoadContext instance. When no TableSerializer is provided a default one will be used, which loads from the file: “default.saveIt”.
C#:
LoadContext context = new LoadContext();
LoadContext context = new LoadContext(new TableSerializer.File("SaveItData"));
LoadContext context = new LoadContext(new TableSerializer.File("SaveItData"), myResourceEntries);
bool IsDeferredType(Type type)
Parameters:
Returns:
Returns wether the given type is a type which needs to be loaded with special means, for example all Components are deferred types. these types need
to be loaded with the Load methods who are providing a callbackMethod or with the WaitForLoad methods.
In most cases you know that without using this method.
C#:
if (context.IsDeferredType(typeof(Foo))) { context.WaitForLoad<Foo>("myFooValue", loadedFoo => myFooValue = loadedFoo); } else { myFooValue = context.Load<Foo>("myFooValue"); }
bool IsDeferredValue(string name)
Parameters:
Returns:
Returns wether the value stored at the given name in the currently serializing object is a type which needs to be loaded with special means, for example all Components are deferred types. these types need
to be loaded with the Load methods who are providing a callbackMethod or with the WaitForLoad methods.
In most cases you know that without using this method.
C#:
if (context.IsDeferredValue("myFooValue")) { context.WaitForLoad<Foo>("myFooValue", loadedFoo => myFooValue = loadedFoo); } else { myFooValue = context.Load<Foo>("myFooValue"); }
void Load(Action<object> onLoaded)
void Load<T>(Action<T> onLoaded)
Parameters:
This loads the next available slot with the generated name: “Slot.” + nextSlotIndex + “]” and calls the callBack onLoaded when finished.
This method is usefull when loading values you know not if these types are deferred or not and you didnt provide any name when you stored it with the SaveContext.
C#:
context.Load(loadedObject => { myComponent = (MyComponent)loadedObject; });
context.Load<MyComponent>(loadedComponent => { myComponent = loadedComponent; });
Load(string name, Action<object> onLoaded)
Load<T>(string name, Action<T> onLoaded)
Parameters:
This loads value stored at the given name and calls the callBack onLoaded when finished.
This method is usefull when loading values you know not if these types are deferred or not.
C#:
context.Load("healthPoints", loadedValue => { healthPoints = (int)loadedValue; });
context.Load<int>("healthPoints", loadedInteger => { healthPoints = loadedInteger; });
object Load()
T Load()
Parameters:
Returns:
This loads the next available slot with the generated name: “Slot.” + nextSlotIndex + “]” and returns the loaded result.
C#:
int healtPoints = (int)context.Load();
int healtPoints = context.Load<int>();
object Load(string name)
T Load(string name)
Parameters:
Returns:
This loads value stored at the given name and returns the loaded result.
C#:
int healtPoints = (int)context.Load("healthPoints");
int healtPoints = context.Load<int>("healthPoints");
void Load(out T target)
Parameters:
This loads the next available slot with the generated name: “Slot.” + nextSlotIndex + “]” and puts the result into the given target.
C#:
int healtPoints; context.Load(out healthPoints);
void Load(string name, out T target)
Parameters:
This loads value stored at the given name and puts the result into the given target.
C#:
int healtPoints; context.Load("healthPoints", out healthPoints);
void WaitForLoad(string name, Action<object> onLoaded)
void WaitForLoad(string name, Action< T > onLoaded)
Parameters:
This waits unto the next available slot with the generated name: “Slot.” + nextSlotIndex + “]” has been loaded and calls the callBack onLoaded when finished.
This method is usefull when somwhere else is a Load call and you only want to know when this call has been finished. You want to use it for all deferred types which cant be constructed with a normal new call.
C#:
context.WaitForLoad("someComponent", loadedObject => { myComponent = (MyComponent)loadedObject; });
context.WaitForLoad<MyComponent>("someComponent", myBehaviour => { myComponent = myBehaviour; });
object LoadToInstance(object instance)
T LoadToInstance(T instance)
Parameters:
Returns:
This loads the next available slot with the generated name: “Slot.” + nextSlotIndex + “]” into the given instance. Use this when you already created an instance and want it to be filled with stored data. This is usefull when loading componants, as these are not creatable without using a GameObject.
C#:
context.LoadToInstance(gameObject.transform);
object LoadToInstance(string name, object instance)
T LoadToInstance(string name, T instance)
Parameters:
Returns:
This loads the value with the given name into the given instance. Use this when you already created an instance and want it to be filled with stored data. This is usefull when loading componants, as these are not creatable without using a GameObject.
C#:
context.LoadToInstance("myTransform", gameObject.transform);
bool Exists(string name)
Parameters:
Returns:
This method will check wether a value with the given name exists.
C#:
if (context.Exists("MagicNumber")) { int magicNumber = context.Load("MagicNumber"); }
bool MemberExists(string memberName)
Parameters:
Returns:
This method will check wether a value with the given memberName exists.
C#:
if (context.MemberExists("mainTexture")) { context.LoadMember("mainTexture"); }
Namespace: SaveIt
SaveContext(ITableSerializer tableSerializer = null, ResourceEntry[] resourceEntries = null)
Parameters:
Creates a new SaveContext instance. When no TableSerializer is provided a default one will be used, which saves to the file: “default.saveIt”.
C#:
1 | SaveContext context = new SaveContext(); |
1 | SaveContext context = new SaveContext(new TableSerializer.File("SaveItData")); |
1 | SaveContext context = new SaveContext(new TableSerializer.File("SaveItData"), myResourceEntries); |
void SaveMember(string memberName, string name = null)
Parameters:
Stores the value of the given member memberName of the currently serializing object at the given name, when no name is provided
a name will be generated: “[member:" + memberName + "]“.
You can only use this method when serializing an object (Custom serializer or Load/Save methods inside the objects class layout).
C#:
1 | context.SaveMember("localPosition"); |
1 | context.SaveMember("localPosition", "myPosition"); |
void Save(T what, string name = null)
Parameters:
Stores the given object what with with the given name. When no name is given a name is generated: “[Slot." + currentSlot + "]”
This enables the ability to write Save and Load calls which mirrors themself without worrying about the names.
C#:
1 2 3 | context.Save(name); // Name is: [Slot.0] context.Save(active, "isActive"); // Name is: isActive context.Save(healthPoints); // Name is: [Slot.1] |
void Flush()
Parameters:
Saves all stored objects to the TableSerializer (for example to a File).
You must call this after you stored all your objects!
C#:
1 2 3 | context.Save(gameObject.name, "name"); context.Save(42, "magicNumber"); context.Flush(); |
Instead of buying a gift for the birthday of my beautiful wife I decided to create one with my bare hands. As inspiration I used a post in reddit, you can look it up here.
Here are the results..
The whole thing without special effects.
Click
A closer lookup to the flakon, a little note here, the image does not reflect the original appearance, the reality is more shiny.
Now some images with the first special effect, a red glow (I searched for a blue or white, which appears as light blue due to the blue fluid, but coukld not find any, but I think it works really good as a nice contrast).
The images where taken in a really dark environment so, some details are blurry or too dark in the pictures.
Finally an image with a very long exposure time (30 seconds) as the last special effect is only visible in really dark areas. So imagine this image in total darkness with only the light bluish elements in the picture glowing (they glow blue/lightblue).
I realized the last glow effect with night active color, NightTec-something, a phosphorescent color which glows blue in the dark (when charged with enough light). My first try was to dissolve the color in water, but the problem with it is that the color will drop to the ground as little particles, looks like fairydust (not the drug) but the water remains muddy.
The first glow effect is a simple red LED.
Powered by WordPress