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

Unity PlayerPrefs失效及FormatException报错问题求助

PlayerPrefs跨场景保存失效及FormatException错误排查解决

问题现象

  • Unity编辑器内初始测试时PlayerPrefs功能正常,打包后跨场景保存完全失效
  • 返回编辑器测试仍正常,但重启Unity后,编辑器与打包版本均抛出FormatException,报错指向UI_Inventory.cs的RefreshInventoryItemsNewWorld方法第123行的int.Parse操作

错误根源分析

  1. 空字符串解析异常:当PlayerPrefs中无对应物品类型的键时,PlayerPrefs.GetString()返回空字符串,直接使用int.Parse()会触发格式转换异常。
  2. 生命周期时序问题:Start()中直接调用RefreshInventoryItemsNewWorld()时,inventory可能未被SetInventory()初始化,打包后生命周期调用顺序的变化放大了这个问题。
  3. 保存逻辑不完整:仅在物品数量>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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.15 00:10:26