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

Unity 2022场景内存分配器溢出问题求助(关联SaveLoadManager脚本)

问题:Unity 2022内存分配器溢出,关联SaveLoadManager脚本

场景启动后触发致命错误:

Fatal Error! Overflow in memory allocator. UnityEngine.Rendering.RenderPipelineManager:DoRenderLoop_Internal (UnityEngine.Rendering.RenderPipelineAsset,intptr,UnityEngine.Object,Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle)

移除以下脚本后场景可正常运行,但需要保留该脚本功能:

public class SaveLoadManager : MonoBehaviour
{
    private DatabaseReference databaseReference;
    public Transform player;
    private string userName;

    private float X;
    private float Y;
    private float Z;

    private float pos_x;
    private float pos_y;
    private float pos_z;

    private void Awake()
    {
        userName = PlayerPrefs.GetString("txt");
        databaseReference = FirebaseDatabase.DefaultInstance.RootReference;

        LoadData();
    }

    private void Start()
    {
        StartCoroutine(StartSaving());
    }

    private void Update()
    {
        pos_x = player.position.x;
        pos_y = player.position.y;
        pos_z = player.position.z;
    }

    public void SaveData()
    {
        StartCoroutine(SavePlayerPosition());
    }

    public void LoadData()
    {
        StartCoroutine(LoadPlayerPositionX());
        StartCoroutine(LoadPlayerPositionY());
        StartCoroutine(LoadPlayerPositionZ());
    }

    IEnumerator SavePlayerPosition()
    {
        while (true)
        {
            var DBTask_0 = databaseReference.Child("users").Child(userName).Child("GameData").Child("position_x").SetValueAsync(pos_x);
            var DBTask_1 = databaseReference.Child("users").Child(userName).Child("GameData").Child("position_y").SetValueAsync(pos_y);
            var DBTask_2 = databaseReference.Child("users").Child(userName).Child("GameData").Child("position_z").SetValueAsync(pos_z);

            yield return new WaitUntil(predicate: () => DBTask_0.IsCompleted);

            if (DBTask_0.Exception != null)
            {
                Debug.LogWarning(message: $"Failed to register task with {DBTask_0.Exception}");
            }

            yield return new WaitUntil(predicate: () => DBTask_1.IsCompleted);

            if (DBTask_1.Exception != null)
            {
                Debug.LogWarning(message: $"Failed to register task with {DBTask_1.Exception}");
            }

            yield return new WaitUntil(predicate: () => DBTask_2.IsCompleted);

            if (DBTask_2.Exception != null)
            {
                Debug.LogWarning(message: $"Failed to register task with {DBTask_2.Exception}");
            }
        }
    }

    IEnumerator LoadPlayerPositionX()
    {
        var DBTask = databaseReference.Child("users").Child(userName).Child("GameData").GetValueAsync();

        yield return new WaitUntil(predicate: () => DBTask.IsCompleted);

        if (DBTask.Exception != null)
        {
            Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
        }
        else
        {
            DataSnapshot dataSnapshot = DBTask.Result;
            X = float.Parse(dataSnapshot.Child("position_x").Value.ToString(), CultureInfo.InvariantCulture.NumberFormat);
        }
    }

    IEnumerator LoadPlayerPositionY()
    {
        var DBTask = databaseReference.Child("users").Child(userName).Child("GameData").GetValueAsync();

        yield return new WaitUntil(predicate: () => DBTask.IsCompleted);

        if (DBTask.Exception != null)
        {
            Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
        }
        else
        {
            DataSnapshot snapshot = DBTask.Result;
            Y = float.Parse(snapshot.Child("position_y").Value.ToString(), CultureInfo.InvariantCulture.NumberFormat);
        }
    }

    IEnumerator LoadPlayerPositionZ()
    {
        var DBTask = databaseReference.Child("users").Child(userName).Child("GameData").GetValueAsync();

        yield return new WaitUntil(predicate: () => DBTask.IsCompleted);

        if (DBTask.Exception != null)
        {
            Debug.LogWarning(message: $"Failed to register task with {DBTask.Exception}");
        }
        else
        {
            DataSnapshot snapshot = DBTask.Result;
            Z = float.Parse(snapshot.Child("position_z").Value.ToString(), CultureInfo.InvariantCulture.NumberFormat);
        }
    }

    IEnumerator StartSaving()
    {
        //yield return new WaitForSeconds(0.3f);
        //player.position = new Vector3(X, Y, Z);
        yield return new WaitForSeconds(1);
        SaveData();
    }
}

问题根源

  1. 无限循环无延迟:SavePlayerPosition协程中的while (true)没有任何延迟,会在每一帧疯狂发起Firebase写请求,瞬间耗尽内存资源,触发分配器溢出。
  2. 冗余加载请求:三个加载协程各自独立请求GameData节点,造成三次重复网络请求,额外消耗资源。
  3. 加载数据未生效:注释掉了设置玩家位置的代码,加载逻辑形同虚设。

修复方案

调整脚本逻辑,优化请求频率和资源占用:

using UnityEngine;
using Firebase.Database;
using System.Collections;
using System.Globalization;

public class SaveLoadManager : MonoBehaviour
{
    private DatabaseReference databaseReference;
    public Transform player;
    private string userName;

    private Vector3 loadedPosition;
    private Vector3 currentPosition;

    private void Awake()
    {
        userName = PlayerPrefs.GetString("txt");
        databaseReference = FirebaseDatabase.DefaultInstance.RootReference;

        StartCoroutine(LoadPlayerPosition());
    }

    private void Start()
    {
        StartCoroutine(StartSavingLoop());
    }

    private void Update()
    {
        currentPosition = player.position;
    }

    private IEnumerator StartSavingLoop()
    {
        // 等待1秒后开始保存
        yield return new WaitForSeconds(1f);
        
        while (true)
        {
            SavePlayerPosition();
            // 每5秒保存一次,可根据需求调整间隔
            yield return new WaitForSeconds(5f);
        }
    }

    private void SavePlayerPosition()
    {
        var positionData = new PlayerPosition
        {
            position_x = currentPosition.x,
            position_y = currentPosition.y,
            position_z = currentPosition.z
        };

        var dbTask = databaseReference.Child("users").Child(userName).Child("GameData").SetValueAsync(positionData);
        dbTask.ContinueWith(task =>
        {
            if (task.Exception != null)
            {
                Debug.LogWarning($"保存位置失败: {task.Exception}");
            }
        });
    }

    private IEnumerator LoadPlayerPosition()
    {
        var dbTask = databaseReference.Child("users").Child(userName).Child("GameData").GetValueAsync();

        yield return new WaitUntil(() => dbTask.IsCompleted);

        if (dbTask.Exception != null)
        {
            Debug.LogWarning($"加载位置失败: {dbTask.Exception}");
        }
        else
        {
            DataSnapshot snapshot = dbTask.Result;
            if (snapshot.Exists)
            {
                loadedPosition.x = float.Parse(snapshot.Child("position_x").Value?.ToString() ?? "0", CultureInfo.InvariantCulture);
                loadedPosition.y = float.Parse(snapshot.Child("position_y").Value?.ToString() ?? "0", CultureInfo.InvariantCulture);
                loadedPosition.z = float.Parse(snapshot.Child("position_z").Value?.ToString() ?? "0", CultureInfo.InvariantCulture);
                
                // 延迟一帧设置位置,确保玩家对象初始化完成
                yield return null;
                player.position = loadedPosition;
            }
            else
            {
                Debug.Log("未找到玩家位置数据,使用默认位置");
            }
        }
    }

    // 用于序列化位置数据的类
    private class PlayerPosition
    {
        public float position_x { get; set; }
        public float position_y { get; set; }
        public float position_z { get; set; }
    }
}

关键修改说明

  • 添加保存间隔:StartSavingLoop协程控制每5秒保存一次,避免高频请求。
  • 合并加载逻辑:单个协程完成位置加载,减少重复网络请求。
  • 一次性提交位置数据:通过PlayerPosition类序列化整个位置,单次请求完成保存,降低Firebase交互次数。
  • 修复位置应用逻辑:加载完成后正确设置玩家位置,并增加空值判断避免解析错误。
  • 移除冗余变量:简化位置存储变量,减少内存占用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 18:05:07