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

相关推荐
平行云9 小时前
实时云渲染预启动技术解析:UE数字孪生应用的延迟优化机制(二)
linux·unity·ue5·webgl·实时云渲染·云桌面·像素流
charley.layabox12 小时前
大连理工,将 LayaAir AI 游戏设计带进校园
人工智能·游戏
WiChP12 小时前
【V0.1B10】从零开始的2D游戏引擎开发之路
java·数据库·游戏引擎
ZC跨境爬虫13 小时前
跟着 MDN 学CSS day_21:(图像溢出控制与表单元素样式定制)
前端·javascript·css·ui·交互
FrameNotWork13 小时前
HarmonyOS 智感握姿开发指南:让 UI 跟着握姿自动换边
ui·华为·harmonyos
ting945200013 小时前
深度解析 Google Stitch 3.0:文本驱动跨端 UI 生成技术原理、架构与工程实现
人工智能·ui·架构
心前阳光15 小时前
Unity之PhotonServer使用注意
unity·游戏引擎
UXbot16 小时前
无需设计经验也能做原型:AI辅助工具功能评测
前端·人工智能·低代码·ui·ios·交互
玖玥拾17 小时前
Cocos学习笔记:关节机制与物理交互
游戏引擎·cocos2d
Mark White18 小时前
行为树(Behavior Tree):从 ROS 机器人到 Unity 游戏 AI 的统一决策范式
游戏·unity·机器人