持久数据和游戏保存

对于大多数游戏,玩家应可载入和保存游戏状态,对于持续时间较长的游戏尤其如此,例如冒险类游戏、实时战略游戏以及RPG游戏。其中,游戏应使文件在外部文件间存储和加载游戏数据。

该任务可通过XML或二进制文件并采用序列化机制予以实现,如图10-27所示。

序列化是指内存数据(例如GameObject中的组件状态)与数据流之间的转换过程,并可写入文件中。随后,可从该文件中加载数据,并重新在内存中创建组件,其状态与保存时相同。因此,游戏保存操作首先需要确定需保存、加载的数据(与特定游戏相关),后续处理过程并创建新类加载此类数据。示例代码ObjSerializer.cs可与任意GameObject进行绑定,在外部文件间序列化Transform组件(平移、旋转和缩放状态),并可通过XML或二进制文件格式予以实现。对此,XmlSerializer类用于将内存中的对象
转换为XML文件;而BinaryFormatter类则将内存中的对象转换为二进制文件。其中,XML文件具有可读性,而二进制文件通常无法供人们阅读和理解。

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.Xml.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;

public class ObjSerializer : MonoBehaviour
{
//Data to save to file XML or Binary
[System.Serializable]
[XmlRoot("GameData")]
public class MySaveData
{
//Transform data to save/load to and from file
//This represents a conversion of a transform object
//into simpler values, like floats
[System.Serializable]
public struct DataTransform
{
public float X;
public float Y;
public float Z;
public float RotX;
public float RotY;
public float RotZ;
public float ScaleX;
public float ScaleY;
public float ScaleZ;
}

//Transform object to save
public DataTransform MyTransform = new DataTransform();
}

//My Save Data Object declared here
public MySaveData MyData = new MySaveData();

//Populate structure MyData with transform data
//This is the data to be saved to a file
private void GetTransform()
{
//Get transform component on this object
Transform ThisTransform = transform;

//We got the transform component, now fill data structure
MyData.MyTransform.X = ThisTransform.position.x;
MyData.MyTransform.Y = ThisTransform.position.y;
MyData.MyTransform.Z = ThisTransform.position.z;
MyData.MyTransform.RotX = ThisTransform.localRotation.eulerAngles.x;
MyData.MyTransform.RotY = ThisTransform.localRotation.eulerAngles.y;
MyData.MyTransform.RotZ = ThisTransform.localRotation.eulerAngles.z;
MyData.MyTransform.ScaleX = ThisTransform.localScale.x;
MyData.MyTransform.ScaleY = ThisTransform.localScale.y;
MyData.MyTransform.ScaleZ = ThisTransform.localScale.z;
}

//Restore the transform component with loaded data
//Call this function after loading data back from a file for restore
private void SetTransform()
{
//Get transform component on this object
Transform ThisTransform = transform;

//We got the transform component, now restore data
ThisTransform.position = new Vector3(MyData.MyTransform.X, MyData.MyTransform.Y, MyData.MyTransform.Z);
ThisTransform.rotation = Quaternion.Euler(MyData.MyTransform.RotX, MyData.MyTransform.RotY, MyData.MyTransform.RotZ);
ThisTransform.localScale = new Vector3(MyData.MyTransform.ScaleX, MyData.MyTransform.ScaleY, MyData.MyTransform.ScaleZ);
}

//Saves game data to XML file
//Call this function to save data to an XML file
//Call as Save(Application.persistentDataPath + "/Mydata.xml");
public void SaveXML(string FileName = "GameData.xml")
{
//Get transform data
GetTransform();

//Now save game data
XmlSerializer Serializer = new XmlSerializer(typeof(MySaveData));
FileStream Stream = new FileStream(FileName, FileMode.Create);
Serializer.Serialize(Stream, MyData);
Stream.Close();
}

//Load game data from XML file
//Call this function to load data from an XML file
//Call as Save(Application.persistentDataPath + "/Mydata.xml");
public void LoadXML(string FileName = "GameData.xml")
{
//If file doesn't exist, then exit
if(!File.Exists(FileName)) return;

XmlSerializer Serializer = new XmlSerializer(typeof(MySaveData));
FileStream Stream = new FileStream(FileName, FileMode.Open);
MyData = Serializer.Deserialize(Stream) as MySaveData;
Stream.Close();

//Set transform - load back from a file
SetTransform();
}

public void SaveBinary(string FileName = "GameData.sav")
{
//Get transform data
GetTransform();

BinaryFormatter bf = new BinaryFormatter();
FileStream Stream = File.Create(FileName);
bf.Serialize(Stream, MyData);
Stream.Close();
}

public void LoadBinary(string FileName = "GameData.sav")
{
//If file doesn't exist, then exit
if(!File.Exists(FileName)) return;

BinaryFormatter bf = new BinaryFormatter();
FileStream Stream = File.Open(FileName, FileMode.Open);
MyData = bf.Deserialize(Stream) as MySaveData;
Stream.Close();

//Set transform - load back from a file
SetTransform();
}

void Update()
{
//If 1 key is pressed,then data saved to XML file
if(Input.GetKeyDown(KeyCode.Alpha1))
{
SaveXML(Application.persistentDataPath + "/Mydata.xml");
Debug.Log ("Saved to: " + Application.persistentDataPath + "/Mydata.xml");
}

//If 2 key is pressed,then data loaded from XML file
if(Input.GetKeyDown(KeyCode.Alpha2))
{
LoadXML(Application.persistentDataPath + "/Mydata.xml");
Debug.Log ("Loaded from: " + Application.persistentDataPath + "/Mydata.xml");
}

//If 3 key is pressed,then data saved to binary file
if(Input.GetKeyDown(KeyCode.Alpha3))
{
SaveBinary(Application.persistentDataPath + "/Mydata.sav");
Debug.Log ("Saved to: " + Application.persistentDataPath + "/Mydata.sav");
}

//If 4 key is pressed,then data loaded from binary file
if(Input.GetKeyDown(KeyCode.Alpha4))
{
LoadBinary(Application.persistentDataPath + "/Mydata.sav");
Debug.Log ("Loaded from: " + Application.persistentDataPath + "/Mydata.sav");
}
}
}
0%