Hey @Furcifer-bellator !!
Thank you for the direct question, I'm flattered :)
I created a Save System Example for you based on this other post:
http://answers.unity3d.com/questions/610893/how-do-i-save-a-custom-class-of-variables-to-playe.html
@iwaldrop showed a beautiful example for a Serializer that can be used to save data.
I created an example that applies this Serializer with some slight adjustments.
**Download the package here!**
https://www.dropbox.com/s/tmzf6vtc73wloom/SaveLoadExample.unitypackage?dl=0
**Here's the visual preview:**
![Preview of the SaveSystemExample][1]
**Here's the Code (Explained at bottom of the post):**
**Serializer:**
using System;
using System.IO;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.Serialization.Formatters.Binary;
public class Serializer
{
private const string m_fileExtension = ".dat";
private const string m_prefix = "SaveSystemExample_";
private static string _getFullStorePath(string filename)
{
return Application.persistentDataPath + "/" + m_prefix + filename + m_fileExtension;
}
public static T Load(string filename) where T : class
{
if (File.Exists(_getFullStorePath(filename)))
{
try
{
using (Stream stream = File.OpenRead(_getFullStorePath(filename)))
{
BinaryFormatter formatter = new BinaryFormatter();
return formatter.Deserialize(stream) as T;
}
}
catch (Exception e)
{
Debug.Log(e.Message);
}
}
return default(T);
}
public static void Save(string filename, T data) where T : class
{
using (Stream stream = File.OpenWrite(_getFullStorePath(filename)))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, data);
}
}
}
**SaveSlot:**
using UnityEngine;
using System.Collections;
public class SaveSlot : MonoBehaviour
{
public string m_boundFileName = "SaveSlotX";
}
**Character (Example class to save, that fits your context):**
using UnityEngine;
using System;
[Serializable]
public class Character {
[Header("Meta")]
public string m_name = "Lex";
[Header("Attributes")]
[Range(0f, 10f)]
public int m_strength = 7;
[Range(0f, 10f)]
public int m_intelligence = 9,
m_agility = 8,
m_endurance = 7,
m_luck = 10;
[Header("Weapon")]
public Weapon m_currentWeapon = new Weapon("Dragonslayer Blade", 9001f);
[Header("Armor")]
public Armor m_currentArmor = new Armor("Dragonscale Armor", 0.4f);
}
[Serializable]
public class Weapon
{
public string m_name;
public float m_dps; // Damage per second.
public Weapon(string name, float dps)
{
m_name = name;
m_dps = dps;
}
}
[Serializable]
public class Armor
{
public string m_name;
[Range(0f,1f)]
public float m_durability01,
m_damageReduction01;
public Armor(string name, float damageReduction01)
{
m_name = name;
m_damageReduction01 = Mathf.Clamp01(damageReduction01);
m_durability01 = 1.0f; // Starts at full durability.
}
}
**SaveSystem (Saving, Loading and a bit of UI, cut away what you don't need):**
using UnityEngine;
using System.Collections;
using UnityEngine.UI;
using System.Linq;
public class SaveSystem : MonoBehaviour {
public ToggleGroup m_saveSlotsGroup;
public Character m_currentCharacter;
void Start()
{
// Load existing saveSlots' data.
SaveSlot[] saveSlots = m_saveSlotsGroup.GetComponentsInChildren();
foreach (SaveSlot saveSlot in saveSlots)
{
LoadSaveSlot(saveSlot);
}
}
public void Save()
{
SaveSlot selectedSaveSlot = _getSelectedSaveSlot();
Serializer.Save(selectedSaveSlot.m_boundFileName, m_currentCharacter);
_updateCharacterText(selectedSaveSlot);
}
public void Load()
{
SaveSlot selectedSaveSlot = _getSelectedSaveSlot();
LoadSaveSlot(selectedSaveSlot);
}
private void LoadSaveSlot(SaveSlot saveSlot)
{
m_currentCharacter = Serializer.Load(saveSlot.m_boundFileName);
if (m_currentCharacter != null)
{
// Succesfully loaded character:
_updateCharacterText(saveSlot);
}
}
private void _updateCharacterText( SaveSlot saveSlot )
{
// Visual update for the button Label.
saveSlot.GetComponentInChildren().text = m_currentCharacter.m_name;
}
private SaveSlot _getSelectedSaveSlot()
{
if (m_saveSlotsGroup.AnyTogglesOn())
{
Toggle activeToggle = m_saveSlotsGroup.ActiveToggles().First();
SaveSlot selectedSaveSlot = activeToggle.GetComponent();
if (selectedSaveSlot == null)
{
throw new System.NullReferenceException("SaveSlot was null, check whether the active toggle had a SaveSlot script on it");
}
else
{
return selectedSaveSlot;
}
}
else
{
throw new System.NullReferenceException("To do anything with SaveSlots, you'll have to select one first.");
}
}
}
Basically:
1. The **Serializer class** is the main code you need for loading and saving, everything else is just data / filename binding.
2. The **Character class** is an example of a class you could save.
3. The **SaveSlot class** just hold a filename string that's used to save and load at a specific location.
4. The **SaveSystem** has a **ToggleGroup** reference and gets the **selected SaveSlot** from it (the Toggles each have a SaveSlot component), then it gets the filename defined in the SaveSlot and uses that to **Load and/or Save the current Character** data, lastly updates the text of the toggles for better visual feedback!
5. The two buttons shown in the preview call the Load and Save methods in the SaveSystem to trigger a Save or Load using the selected SaveSlot.
I hope all of that made sense!
Thank you for another cool question, I enjoyed making this one and I might need something similar myself in the future, so thank you! ;)
If this helped, please accept the answer, it'd be much appreciated!
Best of luck!
Cheers,
ThePersister
[1]: /storage/temp/82421-savesystemexamplepreview.png
↧