- Home /
Serializer Script
Hello everyone, So, as per @sleepandpancakes advice on question save datasets at runtime I used this tutorial on how to create a serializer script, courtesy of Ryan Nielson. saving games in unity
I transcribed the scripts word for word, and now am trying to put them into my application in unity by dragging+dropping the script onto a gameobject, like normal. However, when I do this, I get the error message:
can't add because
script needs to derive from monobehavior
script class can't be abstract
Any ideas/people run into this problem before? and by the way, the scripts don't want to be not abstract, or use monobehavior. They are not supposed to (at least not in the tutorial)
Anyway, sorry for asking this "error message" question, but I haven't been able to find anything similar to this online. My bad if this is an obvious fix, but thank you all for your help.
Gratefully, Wolfshadow
scripts:
using UnityEngine;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System;
public static class SaveGameSystem
{
public static bool SaveGame (SaveGame saveGame, string name)
{
BinaryFormatter formatter = new BinaryFormatter ();
using (FileStream stream = new FileStream (GetSavePath (name), FileMode.Create)) {
try {
formatter.Serialize (stream, saveGame);
} catch (Exception) {
return false;
}
}
return true;
}
public static SaveGame LoadGame (string name)
{
if (!DoesSaveGameExist (name)) {
return null;
}
BinaryFormatter formatter = new BinaryFormatter ();
using (FileStream stream = new FileStream (GetSavePath (name), FileMode.Open)) {
try {
return formatter.Deserialize (stream) as SaveGame;
} catch (Exception) {
return null;
}
}
}
public static bool DeleteSaveGame (string name)
{
try {
File.Delete (GetSavePath (name));
} catch (Exception) {
return false;
}
return true;
}
public static bool DoesSaveGameExist (string name)
{
return File.Exists (GetSavePath (name));
}
private static string GetSavePath (string name)
{
return Path.Combine (Application.persistentDataPath, name + ".sav");
}
}
and
using System;
[Serializable]
public class MySaveGame : SaveGame {
public string playerName = "John";
public int HighScore { get; set; }
[NonSerialized]
public string secret = "Nope";
void Start ()
{
MySaveGame mySaveGame1 = new MySaveGame ();
mySaveGame1.playerName = "John";
mySaveGame1.HighScore = 20;
mySaveGame1.secret =20.ToString ();
SaveGameSystem.SaveGame (mySaveGame1, "MySaveGame");
}
}
Answer by Vicarian · Apr 18, 2017 at 04:02 AM
@Wolfshadow Since all the methods in the base class SaveGame are static, instead of adding the script directly to a gameObject, you'd make a script to drive the SaveGame object and just call the static methods you need. Attempting to call instance methods from the derived class MySaveGame would result in a compiler error, because those methods aren't instanced. To call a static function, all you need to do is supply the Type and access the function you need:
MySaveGame.SaveGame();
If you need the fields and property you defined in the derived class, then you'd add this class as an instance member of another script, then wrap the static methods into functions in the driver class.
SaveGameCaller.cs:
using UnityEngine;
public class SaveGameCaller : MonoBehaviour
{
MySaveGame mySaveGame = new MySaveGame()
void Update ()
{
if (Input.GetKeyDown ("s")) {
SaveGame ();
}
if (Input.GetKeyDown("l")) {
LoadSaveGame ();
}
if (Input.GetKeyDown("d")) {
DeleteSaveGame ();
}
}
void Start() {
mySaveGame.playerName = "John";
mySaveGame.HighScore = 20;
mySaveGame.secret =20.ToString ();
SaveGame();
}
public void SaveGame()
{
// The first argument is an example of C#'s polymorphism functionality,
// which allows you to treat derived classes as their base class.
SaveGameSystem.SaveGame(mySaveGame, "SomeName");
}
public void LoadSaveGame()
{
SaveGameSystem.LoadSaveGame("SomeName");
}
public void DeleteSaveGame()
{
SaveGameSystem.DeleteSaveGame("SomeName");
}
}
You'd add the above to a GameObject and hook up some UI elements to call those functions, or create states necessary for saving.
SaveGameSystem.cs
using System;
public static class SaveGameSystem
{
private static MySaveGame mySaveGame;
public static bool SaveGame (SaveGame saveGame, string name)
{
BinaryFormatter formatter = new BinaryFormatter ();
using (FileStream stream = new FileStream (GetSavePath (name), FileMode.Create)) {
try {
formatter.Serialize (stream, saveGame);
} catch (Exception) {
return false;
}
}
return true;
}
public static SaveGame LoadGame (string name)
{
if (!DoesSaveGameExist (name)) {
return null;
}
BinaryFormatter formatter = new BinaryFormatter ();
using (FileStream stream = new FileStream (GetSavePath (name), FileMode.Open)) {
try {
return formatter.Deserialize (stream) as SaveGame;
} catch (Exception) {
return null;
}
}
}
public static bool DeleteSaveGame (string name)
{
try {
File.Delete (GetSavePath (name));
} catch (Exception) {
return false;
}
return true;
}
public static bool DoesSaveGameExist (string name)
{
return File.Exists (GetSavePath (name));
}
private static string GetSavePath (string name)
{
return Path.Combine (Application.persistentDataPath, name + ".sav");
}
}
MySaveGame.cs
using System;
using System.Collections;
using UnityEngine;
[Serializable]
public class MySaveGame: SaveGame
{
public string playerName = "John";
public int HighScore { get; set; }
[NonSerialized]
public string secret = "Nope";
}
Answer by SweatyChair · Apr 18, 2017 at 08:02 AM
SaveGameSystem is static so you cannot drag it and making it abstract (and you DON'T need to in 99% of the cases), just use it in your code anywhere. I would also keep the MySaveGame object reference in SaveGameSystem to make things easier as:
public static class SaveGameSystem
{
private static MySaveGame mySaveGame;
public static bool SaveGame (string fileName)
{
if (mySaveGame == null)
mySaveGame = new MySaveGame();
...
formatter.Serialize (stream, mySaveGame);
...
}
public static void LoadGame (string fileName)
{
...
mySaveGame = formatter.Deserialize (stream) as SaveGame;
...
}
...
}
Then, you can save the current game anywhere by just calling:
SaveGameSystem.Save(fileName);
You can make MySaveGame abstract and have other classes override it if you want. In my games, I have a abstract MySaveGame, and it's children class MyServerSaveGame (sync to PlayGames, etc) and MyLocalSaveGame (just works like PlayerPrefs in local):
[System.Serializable]
public abstract class MySaveGame
{
...
}
[System.Serializable]
public class MyServerSaveGame : MySaveGame
{
...
}
This is my game save system, syncing to PlayGames and iCloud using Prime31 plugin, if you wanna go further with it: GameSaveManager
Answer by Wolfshadow · Apr 19, 2017 at 02:16 AM
@Vicarian , alright, so I did what you suggested, but, as I am new to c#, I am having some trouble implementing it. So, I have savegamesystem using System;
public static class SaveGameSystem
{
private static MySaveGame mySaveGame;
public static bool SaveGame (SaveGame saveGame, string name)
{
BinaryFormatter formatter = new BinaryFormatter ();
using (FileStream stream = new FileStream (GetSavePath (name), FileMode.Create)) {
try {
formatter.Serialize (stream, saveGame);
} catch (Exception) {
return false;
}
}
return true;
}
public static SaveGame LoadGame (string name)
{
if (!DoesSaveGameExist (name)) {
return null;
}
BinaryFormatter formatter = new BinaryFormatter ();
using (FileStream stream = new FileStream (GetSavePath (name), FileMode.Open)) {
try {
return formatter.Deserialize (stream) as SaveGame;
} catch (Exception) {
return null;
}
}
}
public static bool DeleteSaveGame (string name)
{
try {
File.Delete (GetSavePath (name));
} catch (Exception) {
return false;
}
return true;
}
public static bool DoesSaveGameExist (string name)
{
return File.Exists (GetSavePath (name));
}
private static string GetSavePath (string name)
{
return Path.Combine (Application.persistentDataPath, name + ".sav");
}
}
that is working fine.
I have savegamecaller
using UnityEngine;
using System.Collections;
public class SaveGameCaller : MonoBehaviour {
MySaveGame mySaveGame = new MySaveGame ();
public void SaveGame()
{
MySaveGame.SaveGame (mySaveGame, "SomeName");
}
public void LoadSaveGame()
{
MySaveGame.LoadSaveGame ("SomeName");
}
public void DeleteSaveGame()
{
MySaveGame.DeleteSaveGame ("SomeName");
}
}
as you suggested. Now, what problematic is mysavegame:
using UnityEngine;
using System.Collections;
using System;
[Serializable]
public class MySaveGame: SaveGame
{
public string playerName = "John";
public int HighScore { get; set; }
[NonSerialized]
public string secret = "Nope";
void Start ()
{
MySaveGame mySaveGame1 = new MySaveGame ();
mySaveGame1.playerName = "John";
mySaveGame1.HighScore = 20;
mySaveGame1.secret =20.ToString ();
SaveGameSystem.SaveGame (mySaveGame1, "MySaveGame");
}
void Update ()
{
if (Input.GetKeyDown ("s")) {
SaveGame ();
}
if (Input.GetKeyDown("l")) {
LoadSaveGame ();
}
if (Input.GetKeyDown("d")) {
DeleteSaveGame ();
}
}
static public void SaveGame ()
{
}
static public void LoadSaveGame ()
{
}
static public void DeleteSaveGame()
{
}
}
I get error cs1501, saying that no overload for method SaveGame (LoadSaveGame, etc.) takes 1 arguements. I know this must be an easy fix, but I can't find anything online.
Thank you for your help. Its bee really helpful for me, and I am grateful.
p.s. sorry for answer format, comments wasn't working right
SaveGame and $$anonymous$$ySaveGame are not $$anonymous$$onobehaviours, they are just simple serializable classes that hold the save data. Your $$anonymous$$ySaveGame script should not have any $$anonymous$$onoBehaviour functions (Start, Update, etc.) and doesn't need SaveGame(), LoadSaveGame(), or DeleteSaveGame(), because that's what the SaveGameSystem is for. I recommend taking a closer look at the source website.
@Wolfshadow I updated my original post to distinguish between the three files you'll be using. Drop SaveGameCaller onto a GameObject somewhere, and the Update() method on it will take care of you.
Your answer
Follow this Question
Related Questions
Saving Game Confussion 1 Answer
object list not serializing 2 Answers
IOException: Sharing violation on path 1 Answer
how to serialize unity variables? (Sprite, Image ...) 1 Answer