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仍然保持之前的状态。

相关推荐
图扑可视化16 小时前
统一 DataModel 底座,HT UI 组件万物同源|中篇・运笔
ui·数字孪生·组件
qq_3692243321 小时前
ddraw.dll丢失是什么原因引起的?老游戏运行报错,多种可行修复手段汇总分享
windows·游戏·dll·dll修复·dll丢失·dll错误
xiaohe06011 天前
🎮 豆包完胜 DeepSeek ?!零玩家竞技场,AI Agent 专属对弈!
游戏·llm·agent
微三云生态系统架构师-彭丹1 天前
任务卷轴积分系统架构设计:从任务状态机到合规风控的完整实现
unity·系统架构·游戏引擎
XR技术研习社1 天前
从 PICO 文档看未来短期内的 Unity 版本选择
unity·游戏引擎·xr·vr
星栈独行1 天前
自定义 UI-UX-Pro-Max 的 CSV 知识库,把 AI 生成的后台拉回行业该有的样子
前端·人工智能·ui·ux
白搞电子1 天前
ESP-Mosaico 掌上游戏开发:LVGL 抛硬币动画、音频混音与 BMI270 摇晃触发
单片机·嵌入式硬件·物联网·游戏·音视频
开开心心就好1 天前
手机悬屏翻译工具外语游戏漫画APP全覆盖
android·前端·javascript·python·游戏·pdf·html
河南花仙子科技1 天前
如何通过小游戏后台数据优化运营策略
科技·游戏
玖玥拾1 天前
Unity 热更新(一)
unity·游戏引擎