3、PlayerPrefs实战:保存游戏设置(音量、分辨率)、保存玩家金币数量、跨场景数据传递
好,咱们直接进入实战环节。PlayerPrefs 这东西,说白了就是 Unity 给咱们准备的一个“小本本”,专门用来记一些简单的数据。你想想看,游戏设置、玩家金币这种轻量级数据,用 PlayerPrefs 来存,简直不要太方便。
我个人习惯是,只要数据量不大、不需要加密、不需要复杂的查询,优先考虑 PlayerPrefs。它简单、快,而且跨平台自动处理,省心。
3.1 保存游戏设置:音量与分辨率
游戏设置是 PlayerPrefs 最经典的用法。玩家调了音量、改了分辨率,总不能每次进游戏都重新调吧?
咱们先看音量设置。我一般会把音量分成“主音量”、“音乐音量”、“音效音量”三个维度来存。
// 保存音量设置
public void SaveVolumeSettings(float masterVolume, float musicVolume, float sfxVolume)
{
PlayerPrefs.SetFloat("MasterVolume", masterVolume);
PlayerPrefs.SetFloat("MusicVolume", musicVolume);
PlayerPrefs.SetFloat("SFXVolume", sfxVolume);
PlayerPrefs.Save(); // 我习惯手动调用 Save,确保数据立即写入磁盘
Debug.Log("音量设置已保存");
}
// 读取音量设置
public void LoadVolumeSettings()
{
float masterVolume = PlayerPrefs.GetFloat("MasterVolume", 0.8f); // 默认 0.8
float musicVolume = PlayerPrefs.GetFloat("MusicVolume", 0.7f);
float sfxVolume = PlayerPrefs.GetFloat("SFXVolume", 0.7f);
// 应用到音频管理器
AudioManager.Instance.SetMasterVolume(masterVolume);
AudioManager.Instance.SetMusicVolume(musicVolume);
AudioManager.Instance.SetSFXVolume(sfxVolume);
}
分辨率设置稍微复杂一点。因为分辨率涉及到屏幕宽高、全屏模式这些。我建议把分辨率索引和全屏标志分开存。
// 保存分辨率设置
public void SaveResolutionSettings(int resolutionIndex, bool isFullscreen)
{
PlayerPrefs.SetInt("ResolutionIndex", resolutionIndex);
PlayerPrefs.SetInt("IsFullscreen", isFullscreen ? 1 : 0); // 用 int 代替 bool
PlayerPrefs.Save();
}
// 读取分辨率设置
public void LoadResolutionSettings()
{
int resolutionIndex = PlayerPrefs.GetInt("ResolutionIndex", 2); // 默认第三个选项
bool isFullscreen = PlayerPrefs.GetInt("IsFullscreen", 1) == 1; // 默认全屏
// 应用分辨率
Resolution[] resolutions = Screen.resolutions;
if (resolutionIndex < resolutions.Length)
{
Screen.SetResolution(resolutions[resolutionIndex].width,
resolutions[resolutionIndex].height,
isFullscreen);
}
}
3.2 保存玩家金币数量
金币数量是游戏里最常见的“货币”数据。用 PlayerPrefs 存金币,简单直接。但要注意一点:金币数据是玩家资产,不能丢。
我曾经在一个项目里犯过傻,每次金币变化都直接 SetInt,但忘了调用 Save。结果玩家切后台再回来,金币回档了。嗯,从那以后我养成了“每次修改关键数据后立即 Save”的习惯。
// 保存金币
public void SaveGold(int goldAmount)
{
PlayerPrefs.SetInt("PlayerGold", goldAmount);
PlayerPrefs.Save(); // 关键数据,立即保存
Debug.Log("金币已保存: " + goldAmount);
}
// 读取金币
public int LoadGold()
{
return PlayerPrefs.GetInt("PlayerGold", 0); // 默认 0 金币
}
// 增加金币(带校验)
public void AddGold(int amount)
{
int currentGold = LoadGold();
currentGold += amount;
SaveGold(currentGold);
}
// 减少金币(带校验)
public bool SpendGold(int amount)
{
int currentGold = LoadGold();
if (currentGold >= amount)
{
currentGold -= amount;
SaveGold(currentGold);
return true; // 消费成功
}
else
{
Debug.LogWarning("金币不足!");
return false; // 消费失败
}
}
3.3 跨场景数据传递
跨场景传数据,很多新手会用静态变量或者 GameObject.DontDestroyOnLoad。但说实话,用 PlayerPrefs 来做跨场景数据传递,其实更稳妥。
为什么?因为静态变量在场景切换时可能会被重置(比如场景重新加载时),而 PlayerPrefs 的数据是持久化在磁盘上的,只要你不删,它就在那。
我举个例子:玩家在“关卡选择场景”选了一个关卡,然后跳转到“游戏场景”。这个关卡 ID 怎么传过去?
// 场景A:选择关卡时保存
public void SelectLevel(int levelId)
{
PlayerPrefs.SetInt("CurrentLevelID", levelId);
PlayerPrefs.Save();
SceneManager.LoadScene("GameScene");
}
// 场景B:游戏开始时读取
public void StartGame()
{
int levelId = PlayerPrefs.GetInt("CurrentLevelID", 1); // 默认第1关
LevelManager.Instance.LoadLevel(levelId);
// 用完后,我建议清理掉这个临时数据
PlayerPrefs.DeleteKey("CurrentLevelID");
}
再比如,玩家在“商店场景”买了道具,然后回到“主场景”。道具购买状态怎么传?
// 商店场景:购买道具
public void BuyItem(string itemId)
{
// 保存购买状态
PlayerPrefs.SetInt("Item_" + itemId + "_Purchased", 1);
PlayerPrefs.Save();
// 保存当前选中的道具
PlayerPrefs.SetString("SelectedItem", itemId);
PlayerPrefs.Save();
SceneManager.LoadScene("MainScene");
}
// 主场景:读取道具状态
public void LoadPlayerItems()
{
string selectedItem = PlayerPrefs.GetString("SelectedItem", "");
if (!string.IsNullOrEmpty(selectedItem))
{
bool isPurchased = PlayerPrefs.GetInt("Item_" + selectedItem + "_Purchased", 0) == 1;
if (isPurchased)
{
// 应用道具效果
ApplyItemEffect(selectedItem);
}
}
}
3.4 实战中的避坑指南
讲几个我踩过的坑,你们注意一下。
| 坑点 | 说明 | 解决方案 |
|---|---|---|
| 忘记调用 Save() | 数据只存在内存里,没写入磁盘 | 关键数据修改后立即 Save() |
| 键名拼写错误 | Set 和 Get 的键名不一致 | 用常量或枚举管理键名 |
| 默认值不合理 | Get 时没给默认值,返回 0 或空 | 始终提供合理的默认值 |
| 数据未清理 | 临时数据残留,影响后续逻辑 | 用完后 DeleteKey 清理 |
我曾经在一个项目里,因为键名拼写错误,导致玩家音量设置死活不生效。排查了半天才发现是 "MasterVolume" 写成了 "MasterVolum"(少了个 e)。从那以后,我统一用常量来管理键名。
// 用常量管理键名,避免拼写错误
public static class PlayerPrefsKeys
{
public const string MASTER_VOLUME = "MasterVolume";
public const string MUSIC_VOLUME = "MusicVolume";
public const string SFX_VOLUME = "SFXVolume";
public const string RESOLUTION_INDEX = "ResolutionIndex";
public const string IS_FULLSCREEN = "IsFullscreen";
public const string PLAYER_GOLD = "PlayerGold";
public const string CURRENT_LEVEL_ID = "CurrentLevelID";
public const string SELECTED_ITEM = "SelectedItem";
}
// 使用示例
PlayerPrefs.SetFloat(PlayerPrefsKeys.MASTER_VOLUME, 0.8f);
1. 关键数据修改后立即 Save()
2. 用常量管理键名,避免拼写错误
3. 临时数据用完后及时清理
好了,这一章的内容就这些。下一章咱们会讲 PlayerPrefs 的进阶用法,包括数据加密、批量操作、以及如何用 PlayerPrefs 实现简单的存档系统。到时候见。