Here is the error message:
XmlException: Root element is missing.
System.Xml.XmlTextReaderImpl.Throw (System.Exception e) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReaderImpl.ThrowWithoutLineInfo (System.String res) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReaderImpl.ParseDocumentContent () (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReaderImpl.Read () (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlTextReader.Read () (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.XmlReader.MoveToContent () (at <3df1727680a4410c8922c5d71cde3a04>:0)
Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderActorContainer.Read4_ActorCollection () (at <c06125c6813e448a84b3586be54913c5>:0)
System.Reflection.RuntimeMethodInfo.Invoke (System.Object obj, System.Reflection.BindingFlags invokeAttr, System.Reflection.Binder binder, System.Object[] parameters, System.Globalization.CultureInfo culture) (at <e40e5a8f982c4b618a930d29f9bd091c>:0)
Rethrow as InvalidOperationException: There is an error in XML document (0, 0).
System.Xml.Serialization.XmlSerializer.Deserialize (System.Xml.XmlReader xmlReader, System.String encodingStyle, System.Xml.Serialization.XmlDeserializationEvents events) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.Serialization.XmlSerializer.Deserialize (System.Xml.XmlReader xmlReader, System.String encodingStyle) (at <3df1727680a4410c8922c5d71cde3a04>:0)
System.Xml.Serialization.XmlSerializer.Deserialize (System.IO.Stream stream) (at <3df1727680a4410c8922c5d71cde3a04>:0)
GenshinImpactMovementSystem.SaveData.LoadActors (System.String path) (at Assets/GenshinImpactMovementSystem/Scripts/Characters/Player/Data/XML/SaveData.cs:54)
GenshinImpactMovementSystem.SaveData.Load (System.String path) (at Assets/GenshinImpactMovementSystem/Scripts/Characters/Player/Data/XML/SaveData.cs:19)
GenshinImpactMovementSystem.GameController+<>c.<OnEnable>b__9_1 () (at Assets/GenshinImpactMovementSystem/Scripts/Controllers/GameController.cs:76)
UnityEngine.Events.InvokableCall.Invoke () (at <4a31731933e0419ca5a995305014ad37>:0)
UnityEngine.Events.UnityEvent.Invoke () (at <4a31731933e0419ca5a995305014ad37>:0)
UnityEngine.UI.Button.Press () (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:70)
UnityEngine.UI.Button.OnPointerClick (UnityEngine.EventSystems.PointerEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/UI/Core/Button.cs:114)
UnityEngine.EventSystems.ExecuteEvents.Execute (UnityEngine.EventSystems.IPointerClickHandler handler, UnityEngine.EventSystems.BaseEventData eventData) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:57)
UnityEngine.EventSystems.ExecuteEvents.Execute[T] (UnityEngine.GameObject target, UnityEngine.EventSystems.BaseEventData eventData, UnityEngine.EventSystems.ExecuteEvents+EventFunction`1[T1] functor) (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/ExecuteEvents.cs:272)
UnityEngine.EventSystems.EventSystem:Update() (at Library/PackageCache/com.unity.ugui@1.0.0/Runtime/EventSystem/EventSystem.cs:501)
I am not sure on what the error is but I think its because the xml file is blank but in the tutorial the code should already be written. There are 4 scripts that I am using to create the saving and loading system.
Actor script:
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using UnityEngine;
namespace GenshinImpactMovementSystem
{
public class Actor : MonoBehaviour
{
public ActorData data = new ActorData();
public string name = "actor";
public void StoreData()
{
data.name = name;
Vector3 pos = transform.position;
data.posX = pos.x;
data.posY = pos.y;
data.posZ = pos.z;
}
public void LoadData()
{
name = data.name;
transform.position = new Vector3(data.posX, data.posY, data.posZ);
}
void OnEnable()
{
SaveData.OnLoaded += delegate { LoadData(); };
SaveData.OnBeforeSave += delegate { StoreData(); };
SaveData.OnBeforeSave += delegate { SaveData.AddActorData(data); };
}
void OnDisable()
{
SaveData.OnLoaded -= delegate { LoadData(); };
SaveData.OnBeforeSave -= delegate { StoreData(); };
SaveData.OnBeforeSave -= delegate { SaveData.AddActorData(data); };
}
}
public class ActorData
{
[XmlAttribute("Name")]
public string name;
[XmlElement("PosX")]
public float posX;
[XmlElement("PosY")]
public float posY;
[XmlElement("PosZ")]
public float posZ;
}
}
Actor container script:
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using UnityEngine;
namespace GenshinImpactMovementSystem
{
[XmlRoot("ActorCollection")]
public class ActorContainer
{
[XmlArray("Actors")]
[XmlArrayItem("Actor")]
public List<ActorData> actors = new List<ActorData>();
}
}
Save data script:
using System.Collections;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.IO;
using UnityEngine;
namespace GenshinImpactMovementSystem
{
public class SaveData
{
public static ActorContainer actorContainer = new ActorContainer();
public delegate void SerializeAction();
public static event SerializeAction OnLoaded;
public static event SerializeAction OnBeforeSave;
public static void Load(string path)
{
actorContainer = LoadActors(path);
foreach (ActorData data in actorContainer.actors)
{
GameController.CreateActor(data, GameController.playerPath, new Vector3(data.posX, data.posY, data.posZ), Quaternion.identity);
}
OnLoaded();
}
public static void Save(string path, ActorContainer actors)
{
OnBeforeSave();
SaveActors(path, actors);
ClearActors();
}
public static void AddActorData(ActorData data)
{
actorContainer.actors.Add(data);
}
public static void ClearActors()
{
actorContainer.actors.Clear();
}
private static ActorContainer LoadActors(string path)
{
XmlSerializer serializer = new XmlSerializer(typeof(ActorContainer));
FileStream stream = new FileStream(path, FileMode.Create);
ActorContainer actors = serializer.Deserialize(stream) as ActorContainer;
stream.Close();
return actors;
}
private static void SaveActors(string path, ActorContainer actors)
{
XmlSerializer serializer = new XmlSerializer(typeof(ActorContainer));
FileStream stream = new FileStream(path, FileMode.Open);
serializer.Serialize(stream, actors);
stream.Close();
}
}
}
Game controller:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Cinemachine;
using TMPro;
namespace GenshinImpactMovementSystem
{
public class GameController : MonoBehaviour
{
public Button saveButton;
public Button loadButton;
public CinemachineVirtualCamera camera;
public const string playerPath = "Prefabs/Player";
private static string dataPath = string.Empty;
void Awake()
{
if (Application.platform == RuntimePlatform.IPhonePlayer)
{
dataPath = System.IO.Path.Combine(Application.persistentDataPath, "GenshinImpactMovementSystem/Resources/actors.xml");
}
else
{
dataPath = System.IO.Path.Combine(Application.dataPath, "GenshinImpactMovementSystem/Resources/actors.xml");
}
}
void Start()
{
CreateActor(playerPath, new Vector3(0, 0, 0), Quaternion.identity);
}
public static Actor CreateActor(string path, Vector3 position, Quaternion rotation)
{
GameObject prefab = Resources.Load<GameObject>(path);
GameObject go = GameObject.Instantiate(prefab, position, rotation) as GameObject;
Player playerScript = go.GetComponent<Player>();
Transform cameraLookAt = playerScript.cameraPointLookAt;
CinemachineVirtualCamera cam = FindObjectOfType<CinemachineVirtualCamera>();
cam.Follow = cameraLookAt;
cam.LookAt = cameraLookAt;
playerScript.CameraRecenteringUtility.VirtualCamera = cam;
Actor actor = go.GetComponent<Actor>() ?? go.AddComponent<Actor>();
return actor;
}
public static Actor CreateActor(ActorData data, string path, Vector3 position, Quaternion rotation)
{
GameObject prefab = Resources.Load<GameObject>(path);
GameObject go = GameObject.Instantiate(prefab, position, rotation);
Actor actor = go.GetComponent<Actor>() ?? go.AddComponent<Actor>();
actor.data = data;
return actor;
}
void OnEnable()
{
saveButton.onClick.AddListener(delegate {SaveData.Save(dataPath, SaveData.actorContainer);});
loadButton.onClick.AddListener(delegate {SaveData.Load(dataPath);});
}
void OnDisable()
{
saveButton.onClick.RemoveListener(delegate {SaveData.Save(dataPath, SaveData.actorContainer);});
loadButton.onClick.RemoveListener(delegate {SaveData.Load(dataPath);});
}
}
}