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
相关产品推荐
相关产品推荐

