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

Unity 2D场景游戏卡顿排查及平台复用方案咨询

Unity 2D游戏冻结问题排查与平台复用改造方案

一、游戏冻结问题原因及修复

你的代码存在无限死循环风险,这是导致游戏冻结的核心原因:

1. 死循环场景分析

  • GetRandomXPosition方法:当lastSpawnPositionX的位置使得随机生成的X值(范围-7到7)与它的差值始终小于platformWidth时,while循环会无限执行。比如platformWidth=4且lastSpawnPositionX=7,所有随机X的最大值接近7,差值必然小于4,循环永远无法退出。
  • SpawnPlatform方法:同理,当randomX1的位置导致所有新生成的randomX2与它的差值都小于platformWidth时,这里的while循环也会无限运行。

2. 修复代码

给循环添加最大尝试次数,避免无限阻塞:

using UnityEngine;

public class PlatformManager : MonoBehaviour
{
    [SerializeField] private GameObject[] platformPrefabs;
    [SerializeField] private Transform player;
    [SerializeField] private float spawnDistance = 10f;
    [SerializeField] private float platformWidth = 4f;
    // 添加最大尝试次数,防止死循环
    private const int MaxRandomAttempts = 20;

    private float lastSpawnPositionY = -4.5f;
    private float lastSpawnPositionX = 0f;

    private void OnTriggerEnter2D(Collider2D collision)
    {
        Destroy(collision.gameObject);
    }

    private void Update()
    {
        if (player.position.y > lastSpawnPositionY - spawnDistance)
        {
            SpawnPlatform();
        }
    }

    private void SpawnPlatform()
    {
        float randomX1 = GetRandomXPosition();
        float randomX2 = GetRandomXPosition();
        float randomY1 = lastSpawnPositionY + Random.Range(1.5f, 2.7f);
        float randomY2 = randomY1 + Random.Range(0.2f, 0.5f);

        int attempts = 0;
        while (Mathf.Abs(randomX1 - randomX2) < platformWidth && attempts < MaxRandomAttempts)
        {
            randomX2 = GetRandomXPosition();
            attempts++;
        }
        // 超过尝试次数直接使用当前值,避免死循环
        if (attempts >= MaxRandomAttempts)
        {
            Debug.LogWarning("无法生成符合间距要求的X位置,使用当前值");
        }

        Vector2 spawnPosition1 = new Vector2(randomX1, randomY1);
        Vector2 spawnPosition2 = new Vector2(randomX2, randomY2);

        int platformIndex = GetRandomPlatformIndex();
        GameObject platformPrefab = platformPrefabs[platformIndex];

        Instantiate(platformPrefab, spawnPosition1, Quaternion.identity);
        Instantiate(platformPrefabs[0], spawnPosition2, Quaternion.identity);

        lastSpawnPositionY = Mathf.Max(randomY1, randomY2);
        lastSpawnPositionX = Mathf.Max(randomX1, randomX2);
    }

    private float GetRandomXPosition()
    {
        float randomX = Random.Range(-7f, 7f);
        int attempts = 0;

        while (Mathf.Abs(randomX - lastSpawnPositionX) < platformWidth && attempts < MaxRandomAttempts)
        {
            randomX = Random.Range(-7f, 7f);
            attempts++;
        }

        if (attempts >= MaxRandomAttempts)
        {
            Debug.LogWarning("无法生成符合间距要求的X位置,使用当前值");
        }

        return randomX;
    }

    private int GetRandomPlatformIndex()
    {
        float[] probabilities = { 0.1f, 0.05f, 0.03f, 0.1f, 0.05f };
        float totalProbability = 0f;

        foreach (float probability in probabilities)
        {
            totalProbability += probability;
        }

        float randomValue = Random.Range(0f, totalProbability);
        float cumulativeProbability = 0f;

        for (int i = 0; i < probabilities.Length; i++)
        {
            cumulativeProbability += probabilities[i];

            if (randomValue <= cumulativeProbability)
            {
                return i;
            }
        }

        return 0;
    }
}

二、平台复用(对象池)实现方案

替代销毁重建的方式,通过对象池回收并复用平台,减少性能开销:

1. 核心思路

  • 初始化时预生成一批平台存入对象池,或在运行中动态创建并回收。
  • 当平台触发销毁逻辑时,将其从场景中隐藏并加入对象池,而非直接销毁。
  • 需要生成新平台时,优先从对象池中取出可用平台,重置位置和状态后重新激活;若池为空则实例化新平台。

2. 改造后的完整代码

using UnityEngine;
using System.Collections.Generic;

public class PlatformManager : MonoBehaviour
{
    [SerializeField] private GameObject[] platformPrefabs;
    [SerializeField] private Transform player;
    [SerializeField] private float spawnDistance = 10f;
    [SerializeField] private float platformWidth = 4f;
    [SerializeField] private int initialPoolSize = 10; // 初始对象池大小
    private const int MaxRandomAttempts = 20;

    private float lastSpawnPositionY = -4.5f;
    private float lastSpawnPositionX = 0f;
    // 按预制体类型维护对象池
    private List<GameObject>[] platformPools;

    private void Awake()
    {
        // 初始化对象池数组
        platformPools = new List<GameObject>[platformPrefabs.Length];
        for (int i = 0; i < platformPrefabs.Length; i++)
        {
            platformPools[i] = new List<GameObject>();
            // 预生成初始数量的平台
            for (int j = 0; j < initialPoolSize; j++)
            {
                GameObject platform = Instantiate(platformPrefabs[i]);
                platform.SetActive(false);
                platformPools[i].Add(platform);
            }
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        // 判断碰撞对象是否是平台
        if (collision.CompareTag("Platform"))
        {
            collision.gameObject.SetActive(false);
            // 根据平台类型放回对应对象池
            for (int i = 0; i < platformPrefabs.Length; i++)
            {
                if (collision.gameObject.name.StartsWith(platformPrefabs[i].name))
                {
                    platformPools[i].Add(collision.gameObject);
                    break;
                }
            }
        }
    }

    private void Update()
    {
        if (player.position.y > lastSpawnPositionY - spawnDistance)
        {
            SpawnPlatform();
        }
    }

    private void SpawnPlatform()
    {
        float randomX1 = GetRandomXPosition();
        float randomX2 = GetRandomXPosition();
        float randomY1 = lastSpawnPositionY + Random.Range(1.5f, 2.7f);
        float randomY2 = randomY1 + Random.Range(0.2f, 0.5f);

        int attempts = 0;
        while (Mathf.Abs(randomX1 - randomX2) < platformWidth && attempts < MaxRandomAttempts)
        {
            randomX2 = GetRandomXPosition();
            attempts++;
        }

        if (attempts >= MaxRandomAttempts)
        {
            Debug.LogWarning("无法生成符合间距要求的X位置,使用当前值");
        }

        Vector2 spawnPosition1 = new Vector2(randomX1, randomY1);
        Vector2 spawnPosition2 = new Vector2(randomX2, randomY2);

        int platformIndex = GetRandomPlatformIndex();
        // 从对象池获取或实例化平台
        GameObject platform1 = GetPlatformFromPool(platformIndex);
        platform1.transform.position = spawnPosition1;
        platform1.SetActive(true);

        GameObject platform2 = GetPlatformFromPool(0);
        platform2.transform.position = spawnPosition2;
        platform2.SetActive(true);

        lastSpawnPositionY = Mathf.Max(randomY1, randomY2);
        lastSpawnPositionX = Mathf.Max(randomX1, randomX2);
    }

    private float GetRandomXPosition()
    {
        float randomX = Random.Range(-7f, 7f);
        int attempts = 0;

        while (Mathf.Abs(randomX - lastSpawnPositionX) < platformWidth && attempts < MaxRandomAttempts)
        {
            randomX = Random.Range(-7f, 7f);
            attempts++;
        }

        if (attempts >= MaxRandomAttempts)
        {
            Debug.LogWarning("无法生成符合间距要求的X位置,使用当前值");
        }

        return randomX;
    }

    private int GetRandomPlatformIndex()
    {
        float[] probabilities = { 0.1f, 0.05f, 0.03f, 0.1f, 0.05f };
        float totalProbability = 0f;

        foreach (float probability in probabilities)
        {
            totalProbability += probability;
        }

        float randomValue = Random.Range(0f, totalProbability);
        float cumulativeProbability = 0f;

        for (int i = 0; i < probabilities.Length; i++)
        {
            cumulativeProbability += probabilities[i];

            if (randomValue <= cumulativeProbability)
            {
                return i;
            }
        }

        return 0;
    }

    // 从对象池获取平台的方法
    private GameObject GetPlatformFromPool(int index)
    {
        // 先查找池中的可用平台
        foreach (GameObject platform in platformPools[index])
        {
            if (!platform.activeSelf)
            {
                platformPools[index].Remove(platform);
                return platform;
            }
        }
        // 池为空则实例化新平台
        GameObject newPlatform = Instantiate(platformPrefabs[index]);
        return newPlatform;
    }
}

3. 配套设置

  • 给所有平台预制体添加Platform标签,方便在触发时识别。
  • 确保平台的Collider组件已正确设置,触发区域对象的Box Collider覆盖玩家下方的销毁区域。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 01:05:06