You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中能否保存Play Mode内设置/修改的变量?

解决星露谷风格日期管理器退出Play Mode变量重置的问题

嘿,这个问题我之前帮不少做模拟经营类游戏的开发者踩过坑——Unity默认情况下,Play Mode里对脚本变量的修改只会在运行时生效,退出后就会打回你在Inspector面板里设置的初始值,这就是你遇到的核心问题。要让日期修改在退出Play Mode后也保留,本质是要把运行时的变量变更同步到编辑器的持久化数据里,或者直接做本地数据存储。下面给你几个实用的方案,按复杂度从低到高排序:

方案1:用Editor脚本同步变量(适合开发阶段快速调试)

这个方法能让你在Play Mode里改完日期,退出后自动把值同步回Inspector的初始配置,不用额外写文件存储逻辑。

步骤:

  1. 给你的日期管理器脚本加上[ExecuteInEditMode]属性(注意:这个属性会让脚本在编辑模式下也运行,所以要给运行时专属的逻辑加Application.isPlaying判断,避免编辑时的意外行为)
  2. 在Assets目录下创建一个Editor文件夹(必须叫这个名字,Unity才会识别为编辑器脚本目录),然后写一个Editor脚本监听Play Mode退出事件,同步变量值。

代码示例:

首先是你的核心日期管理器脚本:

using UnityEngine;

[ExecuteInEditMode]
public class DateManager : MonoBehaviour
{
    public int currentDay = 1;
    public int currentSeason = 0; // 比如0=春,1=夏,以此类推
    public int currentYear = 1;

    // 与床交互的日期修改逻辑
    public void ChangeDate(int day, int season, int year)
    {
        // 只在运行时执行修改
        if (Application.isPlaying)
        {
            currentDay = day;
            currentSeason = season;
            currentYear = year;
        }
    }
}

然后是Editor目录下的同步脚本:

using UnityEditor;
using UnityEngine;

[CustomEditor(typeof(DateManager))]
public class DateManagerEditor : Editor
{
    private DateManager _dateManager;

    void OnEnable()
    {
        _dateManager = target as DateManager;
        // 监听Play Mode状态变化
        EditorApplication.playModeStateChanged += OnPlayModeStateChanged;
    }

    void OnDisable()
    {
        // 移除监听,避免内存泄漏
        EditorApplication.playModeStateChanged -= OnPlayModeStateChanged;
    }

    void OnPlayModeStateChanged(PlayModeStateChange state)
    {
        // 当Play Mode退出到编辑模式时
        if (state == PlayModeStateChange.ExitingPlayMode)
        {
            // 先缓存运行时的变量值
            int cachedDay = _dateManager.currentDay;
            int cachedSeason = _dateManager.currentSeason;
            int cachedYear = _dateManager.currentYear;

            // 延迟执行,确保已经回到编辑模式
            EditorApplication.delayCall += () =>
            {
                // 把缓存的值赋值回Inspector的初始变量
                _dateManager.currentDay = cachedDay;
                _dateManager.currentSeason = cachedSeason;
                _dateManager.currentYear = cachedYear;

                // 标记对象为已修改,让Unity保存这些变更
                EditorUtility.SetDirty(_dateManager);
                AssetDatabase.SaveAssets();
            };
        }
    }
}

这个方案的优势是零文件读写,开发阶段调试超方便;缺点是Editor脚本不会被打包进游戏,所以只适合编辑器内测试。

方案2:本地文件持久化(适合最终打包版本)

如果你的游戏需要在打包后让玩家退出再进也能保留日期进度,就必须把数据写到本地文件里。这里给你两种常用的实现方式:

方式A:用PlayerPrefs(简单快捷)

PlayerPrefs是Unity提供的轻量级键值对存储,适合存储简单的数值型数据:

using UnityEngine;

public class DateManager : MonoBehaviour
{
    public int currentDay = 1;
    public int currentSeason = 0;
    public int currentYear = 1;

    void Awake()
    {
        // 游戏启动时读取保存的日期
        LoadSavedDate();
    }

    public void ChangeDate(int day, int season, int year)
    {
        currentDay = day;
        currentSeason = season;
        currentYear = year;
        // 修改后立即保存到本地
        SaveDate();
    }

    void SaveDate()
    {
        PlayerPrefs.SetInt("Game_Day", currentDay);
        PlayerPrefs.SetInt("Game_Season", currentSeason);
        PlayerPrefs.SetInt("Game_Year", currentYear);
        PlayerPrefs.Save(); // 强制写入磁盘,避免数据丢失
    }

    void LoadSavedDate()
    {
        // 如果有保存的数据就读取,没有就用默认值
        currentDay = PlayerPrefs.GetInt("Game_Day", 1);
        currentSeason = PlayerPrefs.GetInt("Game_Season", 0);
        currentYear = PlayerPrefs.GetInt("Game_Year", 1);
    }

    // 编辑器内退出Play Mode时也保存
    #if UNITY_EDITOR
    void OnApplicationQuit()
    {
        SaveDate();
    }
    #endif
}

这个方案代码极简,打包后也能正常工作;缺点是存储的数据是明文的,如果需要隐私性可以给数值加个简单的加密(比如异或运算)。

方式B:用JSON文件(灵活扩展)

如果之后要扩展更多保存内容(比如玩家金钱、物品),JSON文件会更适合:

using UnityEngine;
using System.IO;

// 定义日期数据结构
[System.Serializable]
public class SaveDateData
{
    public int day;
    public int season;
    public int year;
}

public class DateManager : MonoBehaviour
{
    public int currentDay = 1;
    public int currentSeason = 0;
    public int currentYear = 1;

    private string _saveFilePath;

    void Awake()
    {
        // 保存路径用Unity的持久化目录,跨平台都能用
        _saveFilePath = Path.Combine(Application.persistentDataPath, "game_date_save.json");
        LoadSavedDate();
    }

    public void ChangeDate(int day, int season, int year)
    {
        currentDay = day;
        currentSeason = season;
        currentYear = year;
        SaveDate();
    }

    void SaveDate()
    {
        SaveDateData data = new SaveDateData();
        data.day = currentDay;
        data.season = currentSeason;
        data.year = currentYear;

        // 序列化为JSON字符串
        string jsonString = JsonUtility.ToJson(data);
        // 写入文件
        File.WriteAllText(_saveFilePath, jsonString);
    }

    void LoadSavedDate()
    {
        if (File.Exists(_saveFilePath))
        {
            string jsonString = File.ReadAllText(_saveFilePath);
            SaveDateData data = JsonUtility.FromJson<SaveDateData>(jsonString);

            currentDay = data.day;
            currentSeason = data.season;
            currentYear = data.year;
        }
        // 没有保存文件就用默认值
    }

    #if UNITY_EDITOR
    void OnApplicationQuit()
    {
        SaveDate();
    }
    #endif
}

这个方案扩展性强,后续加新的保存字段只需要修改SaveDateData类就行。

方案3:用ScriptableObject存储(编辑器+打包通用)

ScriptableObject是Unity的专属数据容器,既可以在编辑器里创建资产文件存储数据,运行时修改后能直接保存到资产里,退出Play Mode也不会丢,打包后也能正常读取,是我最推荐的Unity式数据管理方式。

步骤:

  1. 创建ScriptableObject类:
using UnityEngine;

// 右键菜单创建资产
[CreateAssetMenu(fileName = "GameDateData", menuName = "Custom/Game Date Data")]
public class GameDateData : ScriptableObject
{
    public int currentDay = 1;
    public int currentSeason = 0;
    public int currentYear = 1;
}
  1. 在Project窗口右键,选择Custom > Game Date Data,创建一个资产文件(比如叫DefaultDateData)。
  2. 修改你的日期管理器脚本:
using UnityEngine;

public class DateManager : MonoBehaviour
{
    public GameDateData dateData; // 在Inspector里拖入你创建的DateData资产

    public void ChangeDate(int day, int season, int year)
    {
        dateData.currentDay = day;
        dateData.currentSeason = season;
        dateData.currentYear = year;

        // 编辑器内标记资产为已修改,保存变更
        #if UNITY_EDITOR
        UnityEditor.EditorUtility.SetDirty(dateData);
        UnityEditor.AssetDatabase.SaveAssets();
        #endif
    }

    // 启动时可以读取数据(如果需要)
    void Awake()
    {
        currentDay = dateData.currentDay;
        currentSeason = dateData.currentSeason;
        currentYear = dateData.currentYear;
    }
}

这个方案的优势是数据和代码完全分离,编辑器内调试方便,打包后也能正常工作;如果需要多存档,只需要创建多个GameDateData资产就行。


最后给你个选择建议:

  • 开发阶段快速调试:用方案1或方案3;
  • 打包后玩家进度保存:用方案2或方案3;
  • 追求Unity最佳实践:优先选方案3。

内容的提问来源于stack exchange,提问作者PolyRose

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 10:25:45