1、目标
将游戏保存到文件,并从文件中加载游戏。
Player在游戏中种植的Crop,我们希望保存到文件中,当游戏重新加载时Crop的GridProperty数据仍然存在。这次主要实现保存地面属性(GridProperties)信息。
我们要做的是实现一个我们可以点击的方式,将游戏保存到一个文件中,然后点击加载按钮,它将获取该文件,然后将数据带回游戏中。
2、创建GameSave.cs脚本
在Assets/Scripts/SaveSystem下创建新的脚本命名为:GameSave.cs。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[System.Serializable]
public class GameSave
{
// string key - GUID gameobject ID
public Dictionary<string, GameObjectSave> gameObjectData;
public GameSave()
{
gameObjectData = new Dictionary<string, GameObjectSave>();
}
}
3、修改ISaveable.cs脚本
添加2行代码:
GameObjectSave ISaveableSave();
void ISaveableLoad(GameSave gameSave);
ISaveableSave: 保存变量和游戏对象,这一步是获取数据,后续会写入文件
ISaveableLoad:将游戏保存数据对象(从文件中读取的数据)作为它的参数
4、修改GridPropertiesManager.cs脚本
添加新的引用:
using UnityEngine.SceneManagement;
添加ISaveableLoad函数实现:
public void ISaveableLoad(GameSave gameSave)
{
if(gameSave.gameObjectData.TryGetValue(ISaveableUniqueID, out GameObjectSave gameObjectSave))
{
GameObjectSave = gameObjectSave;
// Restore data for current scene
ISaveableRestoreScene(SceneManager.GetActiveScene().name);
}
}
其中,ISaveableUniqueID是通过GetComponent<GenerateGUID>().GUID生成的。
在GenerateGUID的实现中,它的属性是[ExecuteAlways],即运行/编译时都会执行,实际上是需要编译时生成_gUID信息。_gUID是[SerializeField]属性,而[SerializeField]
标记的字段会被 Unity 自动序列化到场景文件或预制体中。
所以一个游戏中,GenerateGUID的值是不会变化的。
另外,GameObjectSave是属性变量,其定义为:
public GameObjectSave GameObjectSave { get { return _gameObjectSave; } set { _gameObjectSave = value; } }
添加ISaveableSave方法的实现:
public GameObjectSave ISaveableSave()
{
// Store current scene data
ISaveableStoreScene(SceneManager.GetActiveScene().name);
return GameObjectSave;
}
5、修改SceneItemsManager.cs脚本
添加新的引用:
using UnityEngine.SceneManagement;
添加如下2个方法的实现:
public void ISaveableLoad(GameSave gameSave)
{
if(gameSave.gameObjectData.TryGetValue(ISaveableUniqueID, out GameObjectSave gameObjectSave))
{
GameObjectSave = gameObjectSave;
// Restore data for current scene
ISaveableRestoreScene(SceneManager.GetActiveScene().name);
}
}
public GameObjectSave ISaveableSave()
{
// Store current scene data
ISaveableStoreScene(SceneManager.GetActiveScene().name);
return GameObjectSave;
}
6、修改SaveLoadManager.cs脚本
SaveLoadManager有2个方法:StoreCurrentSceneData / RestoreCurrentSceneData。
在SceneControllerManager中,当场景切换和Start中会使用RestoreCurrentSceneData方法。
接下来就是改造SaveLoadManager,从文件中读写信息。
添加2个引用:
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
接着添加2个函数:
public void LoadDataFromFile()
{
BinaryFormatter bf = new BinaryFormatter();
if(File.Exists(Application.persistentDataPath + "/WildHopeCreek.data"))
{
gameSave = new GameSave();
FileStream file = File.Open(Application.persistentDataPath + "/WildHopeCreek.data", FileMode.Open);
gameSave = (GameSave)bf.Deserialize(file);
// loop through all ISaveable obvjects and apply save data
for(int i = iSaveableObjectList.Count - 1; i > -1; i--)
{
if (gameSave.gameObjectData.ContainsKey(iSaveableObjectList[i].ISaveableUniqueID))
{
iSaveableObjectList[i].ISaveableLoad(gameSave);
}
else
{
// else if iSaveableObject unique ID is not in the game object data then destroy object
Component component = (Component)iSaveableObjectList[i];
Destroy(component.gameObject);
}
}
file.Close();
}
UIManager.Instance.DisablePauseMenu();
}
public void SaveDataToFile()
{
gameSave = new GameSave();
// loop through all ISaveable objects and generate save data
foreach(ISaveable iSaveableObject in iSaveableObjectList)
{
gameSave.gameObjectData.Add(iSaveableObject.ISaveableUniqueID, iSaveableObject.ISaveableSave());
}
BinaryFormatter bf = new BinaryFormatter();
FileStream file = File.Open(Application.persistentDataPath + "/WildHopeCreek.data" , FileMode.Create);
bf.Serialize(file, gameSave);
file.Close();
UIManager.Instance.DisablePauseMenu();
}
可以通过如下代码查看存储的路径:
Debug.Log("dataPath:" + Application.persistentDataPath);
我的地址是:C:\Users\benbe\AppData\LocalLow\DefaultCompany\XingluValley
7、编辑UI界面
(1)设置界面元素
将PauseMenuPanel下的Tab2修改为Tab2SaveGame,
将SelectionTabButtonsPanel -> SelectionButton(2) -> Text的输入改为"Save/Load"。
给Tab2SaveGame对象添加Vertical Layout Group组件,相关配置如下:
在Tab2SaveGame下右击 -> UI -> Button(TextMeshPro),命名为SavevGameButton。再创建一个同样的Button命名为LoadGameButton。
分别点击SaveGameButton及LoadGameButton两个对象,修改Button属性的Normal Color为87775D ,Highlighed Color为D8DB84 。
最后界面呈现的样式为:
(2)添加点击事件
SaveGameButton对应的点击事件:
LoadGameButton对应的点击事件:
8、运行游戏
运行游戏,收集地面上的item,并且种植了Crop,点击Esc -> Save Game。
退出游戏后重新进入,点击Esc -> Load Game。之前地面上已经被收集掉的item没有再次出现,Crop仍然保持之前的状态。