Unity平台生成重叠卡顿问题求助(附移动与生成脚本)
问题描述
我编写了一套可定期生成平台的Unity脚本,但运行一段时间后平台会聚集在一起,导致严重卡顿。以下是平台移动脚本SetMove与生成脚本set_make:
平台移动脚本
using System.Collections; using UnityEngine; public class SetMove : MonoBehaviour { int j; int y; private IEnumerator Start() { y = Random.Range(15, 20); Rigidbody2D playerRG = GameObject.Find("player").GetComponent<Rigidbody2D>(); while (transform.position.x > -15) { yield return new WaitForSecondsRealtime(0.1f); transform.Translate(Vector3.left / 5); j++; if (j == y) { GameObject.Find("controller").GetComponent<set_make>().StartCoroutine("Start"); y = Random.Range(15, 20); } if (playerRG.gravityScale == 0 && transform.position.x <= 0) { playerRG.gravityScale = 1; GameObject player = playerRG.gameObject; player.transform.position = transform.position + new Vector3(0, transform.localScale.y / 2); } } Destroy(gameObject); } private void OnTriggerEnter2D(Collider2D collision) { GameObject.Find("player").GetComponent<player>().canJump = true; } private void OnTriggerExit2D(Collider2D collision) { GameObject.Find("player").GetComponent<player>().canJump = false; } }
平台生成脚本
using System.Collections.Generic; using UnityEngine; public class set_make : MonoBehaviour { public GameObject short_block; public GameObject long_block; public GameObject med_block; int num; int pastnum; public void Start() { float level = -3.5f; num = Random.Range(1, 4); switch (num) { case 1: Instantiate(short_block, new Vector3(13, level + 0.5f), Quaternion.identity); pastnum = num; break; case 2: Instantiate(med_block, new Vector3(13, level + 1f), Quaternion.identity); pastnum = num; break; case 3: if (pastnum != 1) { Instantiate(long_block, new Vector3(13, level + 1.5f), Quaternion.identity); pastnum = num; } else { Start(); } break; } } }
我尝试调整生成间隔但无效果,请求协助解决该卡顿问题。
问题分析与修复方案
核心卡顿原因
- 重复生成逻辑触发:
SetMove协程中每次j == y时,都会调用set_make的Start方法作为协程执行,导致多个生成逻辑同时运行,短时间内生成大量平台堆积。 - 递归生成的栈溢出风险:
set_make的Start方法在不符合长平台生成条件时直接递归调用自己,可能瞬间触发多次生成,加剧平台聚集。 - 高频
GameObject.Find调用:在协程和触发器方法中反复使用GameObject.Find,这是性能开销极高的操作,会放大卡顿问题。
具体修复步骤
1. 重构生成脚本,控制生成节奏
将生成逻辑改为协程管理,添加生成锁避免重复触发,同时移除递归调用:
using UnityEngine; using System.Collections; public class set_make : MonoBehaviour { public GameObject short_block; public GameObject long_block; public GameObject med_block; int num; int pastnum; private bool isGenerating = false; // 防止重复生成的标记 private void Start() { // 启动自动生成协程 StartCoroutine(GenerateLoop()); } private IEnumerator GenerateLoop() { isGenerating = true; float level = -3.5f; num = Random.Range(1, 4); switch (num) { case 1: Instantiate(short_block, new Vector3(13, level + 0.5f), Quaternion.identity); pastnum = num; break; case 2: Instantiate(med_block, new Vector3(13, level + 1f), Quaternion.identity); pastnum = num; break; case 3: if (pastnum != 1) { Instantiate(long_block, new Vector3(13, level + 1.5f), Quaternion.identity); pastnum = num; } else { // 不符合条件时重新生成,用yield避免递归栈溢出 yield return GenerateLoop(); yield break; } break; } // 设置和平台移动速度匹配的生成间隔(平台每秒移动2单位,从x=13到x=-15需14秒,间隔设为8-12秒避免堆积) float spawnInterval = Random.Range(8f, 12f); yield return new WaitForSeconds(spawnInterval); isGenerating = false; // 循环启动下一次生成 StartCoroutine(GenerateLoop()); } }
2. 优化移动脚本,移除重复生成调用
提前缓存所有需要的组件引用,移除触发生成的逻辑,让生成器自主控制节奏:
using System.Collections; using UnityEngine; public class SetMove : MonoBehaviour { int j; int y; private Rigidbody2D playerRG; private player playerScript; private void Awake() { // 仅在初始化时获取一次引用,避免高频查找 GameObject playerObj = GameObject.Find("player"); playerRG = playerObj.GetComponent<Rigidbody2D>(); playerScript = playerObj.GetComponent<player>(); } private IEnumerator Start() { y = Random.Range(15, 20); while (transform.position.x > -15) { yield return new WaitForSecondsRealtime(0.1f); transform.Translate(Vector3.left / 5); j++; // 移除此处的生成触发逻辑,由生成器自主控制 // if (j == y) // { // GameObject.Find("controller").GetComponent<set_make>().StartCoroutine("Start"); // y = Random.Range(15, 20); // } if (playerRG.gravityScale == 0 && transform.position.x <= 0) { playerRG.gravityScale = 1; playerRG.transform.position = transform.position + new Vector3(0, transform.localScale.y / 2); } } Destroy(gameObject); } private void OnTriggerEnter2D(Collider2D collision) { // 给Player添加标签,用标签判断更高效 if (collision.CompareTag("Player")) { playerScript.canJump = true; } } private void OnTriggerExit2D(Collider2D collision) { if (collision.CompareTag("Player")) { playerScript.canJump = false; } } }
3. 额外性能优化建议
- 给平台添加对象池,复用已销毁的平台对象,避免频繁
Instantiate和Destroy的性能开销。 - 给
player和controller对象添加标签,使用GameObject.FindWithTag替代GameObject.Find,进一步提升查找效率。 - 检查平台的Collider设置,禁用不必要的碰撞检测,减少物理计算开销。
内容的提问来源于stack exchange,提问作者Logan Kocher
相关产品推荐
相关产品推荐

