Unity PlayerPrefs失效及FormatException报错问题求助
PlayerPrefs跨场景保存失效及FormatException错误排查解决
问题现象
- Unity编辑器内初始测试时PlayerPrefs功能正常,打包后跨场景保存完全失效
- 返回编辑器测试仍正常,但重启Unity后,编辑器与打包版本均抛出
FormatException,报错指向UI_Inventory.cs的RefreshInventoryItemsNewWorld方法第123行的int.Parse操作
错误根源分析
- 空字符串解析异常:当PlayerPrefs中无对应物品类型的键时,
PlayerPrefs.GetString()返回空字符串,直接使用int.Parse()会触发格式转换异常。 - 生命周期时序问题:
Start()中直接调用RefreshInventoryItemsNewWorld()时,inventory可能未被SetInventory()初始化,打包后生命周期调用顺序的变化放大了这个问题。 - 保存逻辑不完整:仅在物品数量>1时保存数据,数量变为1或0时未处理对应键值,导致空数据残留或键值未清理。
修复方案
1. 安全解析PlayerPrefs数据
使用int.TryParse()替代int.Parse(),同时先检查键是否存在,避免空字符串解析失败。
2. 调整初始化顺序
确保inventory赋值完成后再调用刷新方法,避免未初始化的空数据遍历。
3. 完善保存逻辑
统一处理所有数量情况,数量为0时删除对应键,避免无效数据残留。
修正后的完整代码
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using CodeMonkey.Utils; using TMPro; public class UI_Inventory : MonoBehaviour { private Inventory inventory; private Transform itemSlotContainer; private Transform itemSlotTemplate; private PlayerController player; private void Awake() { itemSlotContainer = transform.Find("itemSlotContainer"); itemSlotTemplate = itemSlotContainer.Find("itemSlotTemplate"); } private void Start() { // 移除原有直接调用,确保inventory初始化后再刷新 } public void SetPlayer(PlayerController player) { this.player = player; } public void SetInventory(Inventory inventory) { this.inventory = inventory; inventory.OnItemListChanged += Inventory_OnItemListChanged; // 初始化时先加载保存的数据 RefreshInventoryItemsNewWorld(); } private void Inventory_OnItemListChanged(object sender, System.EventArgs e) { RefreshInventoryItems(); } private void RefreshInventoryItems() { foreach (Transform child in itemSlotContainer) { if (child == itemSlotTemplate) continue; Destroy(child.gameObject); } int x = 0; int y = 0; float itemSlotCellSize = 90f; foreach (Item item in inventory.GetItemList()) { RectTransform itemSlotRectTransform = Instantiate(itemSlotTemplate, itemSlotContainer).GetComponent<RectTransform>(); itemSlotRectTransform.gameObject.SetActive(true); itemSlotRectTransform.GetComponent<Button_UI>().ClickFunc = () => { inventory.UseItem(item); }; itemSlotRectTransform.GetComponent<Button_UI>().MouseRightClickFunc = () => { Item duplicateItem = new Item { itemType = item.itemType, amount = item.amount }; inventory.RemoveItem(item); ItemWorld.DropItem(player.GetPosition(), duplicateItem); }; itemSlotRectTransform.anchoredPosition = new Vector2(x * itemSlotCellSize, y * itemSlotCellSize); Image image = itemSlotRectTransform.Find("image").GetComponent<Image>(); image.sprite = item.GetSprite(); TextMeshProUGUI uiText = itemSlotRectTransform.Find("text").GetComponent<TextMeshProUGUI>(); if (item.amount > 1) { uiText.SetText(item.amount.ToString()); } else { uiText.SetText(""); } // 统一处理保存逻辑,数量为0时删除键 if (item.amount <= 0) { PlayerPrefs.DeleteKey(item.itemType.ToString()); } else { PlayerPrefs.SetString(item.itemType.ToString(), item.amount.ToString()); } PlayerPrefs.Save(); x++; if (x > 4) { x = 0; y++; } } } private void RefreshInventoryItemsNewWorld() { if (inventory == null) return; // 避免inventory未初始化时执行 foreach (Transform child in itemSlotContainer) { if (child == itemSlotTemplate) continue; Destroy(child.gameObject); } int x = 0; int y = 0; float itemSlotCellSize = 90f; foreach (Item item in inventory.GetItemList()) { // 安全解析保存的数量 if (PlayerPrefs.HasKey(item.itemType.ToString())) { int savedAmount; if (int.TryParse(PlayerPrefs.GetString(item.itemType.ToString()), out savedAmount)) { item.amount = savedAmount; } } RectTransform itemSlotRectTransform = Instantiate(itemSlotTemplate, itemSlotContainer).GetComponent<RectTransform>(); itemSlotRectTransform.gameObject.SetActive(true); itemSlotRectTransform.GetComponent<Button_UI>().ClickFunc = () => { inventory.UseItem(item); }; itemSlotRectTransform.GetComponent<Button_UI>().MouseRightClickFunc = () => { Item duplicateItem = new Item { itemType = item.itemType, amount = item.amount }; inventory.RemoveItem(item); ItemWorld.DropItem(player.GetPosition(), duplicateItem); }; itemSlotRectTransform.anchoredPosition = new Vector2(x * itemSlotCellSize, y * itemSlotCellSize); Image image = itemSlotRectTransform.Find("image").GetComponent<Image>(); image.sprite = item.GetSprite(); TextMeshProUGUI uiText = itemSlotRectTransform.Find("text").GetComponent<TextMeshProUGUI>(); if (item.amount > 1) { uiText.SetText(item.amount.ToString()); } else { uiText.SetText(""); } x++; if (x > 4) { x = 0; y++; } } } }
内容的提问来源于stack exchange,提问作者codingGuy
相关产品推荐
相关产品推荐

