Unity3D仿星露谷物语开发55之保存游戏到文件

1、目标

将游戏保存到文件,并从文件中加载游戏。

Player在游戏中种植的Crop,我们希望保存到文件中,当游戏重新加载时Crop的GridProperty数据仍然存在。这次主要实现保存地面属性(GridProperties)信息。

我们要做的是实现一个我们可以点击的方式,将游戏保存到一个文件中,然后点击加载按钮,它将获取该文件,然后将数据带回游戏中。

2、创建GameSave.cs脚本

在Assets/Scripts/SaveSystem下创建新的脚本命名为:GameSave.cs。

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行代码:

cs 复制代码
    GameObjectSave ISaveableSave();

    void ISaveableLoad(GameSave gameSave);

ISaveableSave: 保存变量和游戏对象,这一步是获取数据,后续会写入文件

ISaveableLoad:将游戏保存数据对象(从文件中读取的数据)作为它的参数

4、修改GridPropertiesManager.cs脚本

添加新的引用:

cs 复制代码
using UnityEngine.SceneManagement;

添加ISaveableLoad函数实现:

cs 复制代码
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方法的实现:

cs 复制代码
 public GameObjectSave ISaveableSave()
 {
     // Store current scene data
     ISaveableStoreScene(SceneManager.GetActiveScene().name);

     return GameObjectSave;
 }

5、修改SceneItemsManager.cs脚本

添加新的引用:

cs 复制代码
using UnityEngine.SceneManagement;

添加如下2个方法的实现:

cs 复制代码
 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个引用:

cs 复制代码
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

接着添加2个函数:

cs 复制代码
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();
}

可以通过如下代码查看存储的路径:

cs 复制代码
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仍然保持之前的状态。

相关推荐
Hare_bai2 小时前
WPF响应式UI的基础:INotifyPropertyChanged
ui·c#·wpf·xaml
Hare_bai2 小时前
WPF的布局核心:网格布局(Grid)
ui·c#·wpf·交互·xaml
lqj_本人6 小时前
鸿蒙OS&UniApp导航栏组件开发:打造清新简约的用户界面#三方框架 #Uniapp
ui·uni-app·harmonyos
FAREWELL000759 小时前
Unity基础学习(十二)Unity 物理系统之范围检测
学习·unity·c#·游戏引擎·范围检测
Magnum Lehar9 小时前
vulkan游戏引擎renderer_backend实现
游戏引擎
!chen12 小时前
基于element-UI 实现下拉框滚动翻页查询通用组件
javascript·vue.js·ui
Magnum Lehar13 小时前
vulkan游戏引擎renderer_frontend实现
游戏引擎
Hare_bai14 小时前
WPF的基础控件:布局控件(StackPanel & DockPanel)
ui·c#·wpf·交互·xaml·visual studio
benben04415 小时前
Photoshop使用钢笔绘制图形
ui·photoshop
NRatel16 小时前
Unity 中实现首尾无限循环的 ListView
unity·listview·无限循环·首尾循环