Having terrible trouble with saving/loading player position
So I've looked at the beginner tutorial for how to save and load small bits of information using player prefs, but my problem is a little unique, in that I want my player's exact position saved when they perform a certain action in game, and it only loads when the player dies. I know that there are other questions like this that have been answered, but I couldn't make much sense out of the answers, either because it was in javascript, or it was done in a way that requires more in depth knowledge on the subject.
So, I was wondering if you guys could help me by taking a look at these few excerpts of my scripts and tell me what I need to do to save my player's position, and what do I need to do to load it back up.
This is the script that contains data:
using aUnityEngine;
using System.Collections;
using System;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
public class GameControl : MonoBehaviour {
public static GameControl control;
public GameObject player;
void Awake(){
if (control == null) {
DontDestroyOnLoad (gameObject);
control = this;
} else if (control != this) {
Destroy (gameObject);
}
}
public void Save()
{
BinaryFormatter bf = new BinaryFormatter ();
FileStream file = File.Create (Application.persistentDataPath + "/playerInfo.dat");
PlayerData data = new PlayerData ();
data.player = player;
bf.Serialize (file, data);
file.Close();
}
public void Load()
{
if (File.Exists (Application.persistentDataPath + "/playerInfo.dat")) {
BinaryFormatter bf = new BinaryFormatter ();
FileStream file = File.Open (Application.persistentDataPath + "/playerInfo.dat", FileMode.Open);
PlayerData data = (PlayerData)bf.Deserialize (file);
file.Close ();
player = data.player;
}
}
}
[Serializable]
class PlayerData
{
public GameObject player;
}
This is the script that causes a save to occur:
void Update () {
//chestclick
ray = Camera.main.ScreenPointToRay (Input.mousePosition);
if (Physics.Raycast (ray, out hit)) {
if (Input.GetButtonDown ("Fire1")) {
if (hit.transform.name == "Chest") {
GameControl.control.Save();
}
}
}
And this is the script that loads the data:
void OnTriggerEnter(Collider other)
{
if (other.gameObject.CompareTag ("Player")) {
GameControl.control.Load();
}
}
Keep in mind that everything seems to be set up correctly, for I am getting no errors, save for when the save/load parts of the scripts activate.
Have you tried figuring out what parts of code are actually executed? You can paste some Debug.Log("Chest was hit");
like lines to check it.
Answer by Immanuel-Scholz · Apr 20, 2016 at 07:46 AM
You are serializing a simple data class "PlayerData" using C#'s builtin BinaryFormatter. That is fine - in theory.
However, you can not serialze a GameObject reference there. GameObject is an unity class, mostly implemented in C++. You can not directly try to serialize such a complex class. In the very best, you will serialize all C# parts of the player reference (such as internal pointer and handles used by C++) and it might restore the reference to the original object (but only the reference, not the actual values of that object).
In the worst case, it will simply crash because the said handles and internal pointers are not valid anymore.
So what can you serialize using BinaryFormatter? Basically all standard C# datas and some very simple structs that also need to be declared [Serializable]. Not even Unity-structs like a Matrix3x3 or a Vector3 is serializable with BinaryFormatter.
Edit: After some comments, the easiest way is to abandon BinaryFormatter and use JsonUtility that ships since Unity 5.3. This tool can convert any unity-serializable object properties into JSON text format and back. The code example has been updated accordingly. Note, that JsonUtility still can not serialize complex Unity-intern things like a whole GameObject. But it can serialize basic structs like Vector3, Quaternion, Color etc.. (It also can serialize self-made MonoBehaviours, although you need to use AddComponent + FromJsonOverwrite to deserialize these)
First, think about what exactly it is you want to restore? What can change of the player that should be reset? You wrote already that you like to save/restore the players position. Also its rotation? Can the scale change?
Then just set/read these exact primitive values from PlayerData.
public void Save()
{
PlayerData data = new PlayerData ();
data.playerPosition = player.transform.position;
data.playerRotation = player.transform.rotation;
string json = JsonUtility.ToJson(data);
File.WriteAllText(Application.persistentDataPath + "/playerInfo.dat", json);
}
public void Load()
{
string json = File.ReadAllText(Application.persistentDataPath + "/playerInfo.dat");
PlayerData data = JsonUtility.FromJson<PlayerData>(json);
player = data.player;
player.transform.position = data.playerPosition;
player.transform.rotation = data.playerRotation;
}
[Serializable]
class PlayerData
{
public Vector playerPosition;
public Quaternion playerRotation;
}
Of course, the variable "player" needs to be ready and set up correctly in Load before you read out the serialized values. Whether you can just reuse the currently deceased player or you have to completely recreate him is up to you to decide (in the latter, you might think about spawning the player out of some prefab when the game starts too - just to keep things consistent).
Sorry for the delay in responding!
Alright, so I understand what you're getting at. You're basically saying that a BinaryFormatter can't quite save what I was wanting it to save, which makes sense because it seems like its meant for simple data rather than entire GameObjects. So, I decided to copy down what you had which is basically the player GameObject, but broken down into data that can be saved, and from the looks of it, this script will work when executed correctly...
unfortunately, my beginning understanding for this topic makes it difficult to figure out what to do with this new error I'm getting.
So, I decided to turn that Vector in the private serializable PlayerData class into a Vector3, for this game is in 3D, but whenever the Save function executes in-game, I get an error saying that it isn't marked as serializable. So, I decided to follow what you put down (Vector) and I get a compiler error saying that the type couldn't be found.
Also, I had a problem understanding what you meant by having the variable player set up.
Perhaps you can shine some light on this?
Indeed, seems like even the builtin data structs of Unity can not be used with BinaryFormatter.
You could now press on and put the individual members of the structs there (float playerPositionX, float playerPositionY ...) but this gets ugly very quickly.
Another way which might be a lot easier would be JsonUtility.ToJson and .FromJson which converts any Unity-serializable data structure to/from JSON text. You get a plain string out this way, so you might want to use File.WriteAllText / File.ReadAllText ins$$anonymous$$d.
Or you could just dump the string into PlayerPrefs, then your code does not even need a file system (and will run on more platforms, e.g. on WebGL)
Can you elaborate on this JSON text thing that you mentioned? I have never heard of this thing ever since I've started scripting and it sounds like it would make my life immensely easy, especially since I don't only plan on saving my player's position, but other objects' positions as well (along with whether or not they exist in the scene).
Answer by CubeCrafter360 · Apr 30, 2017 at 04:34 PM
Dude where you + the data path i learned that you should not use + but use , instead
soo instead off string json = File.ReadAllText(Application.persistentDataPath + "/playerInfo.dat"); do string json = File.ReadAllText(Application.persistentDataPath ,"/playerInfo.dat");
Your answer
![](https://koobas.hobune.stream/wayback/20220612075156im_/https://answers.unity.com/themes/thub/images/avi.jpg)
Follow this Question
Related Questions
How to teleport game object to another scene at the exact position from previous scene? 0 Answers
randomly Instantiate items 0 Answers
Help with double jump script! 2 Answers
My movement script wont work 1 Answer
Issues using two scripts. 1 Answer