Unity对象池优化与单向易碎平台实现技术问询
Unity对象池优化与单向易碎平台实现方案
一、对象池删除与回池逻辑优化
问题核心
当前代码完全没实现对象池的复用逻辑:
- 所有生成平台的方法都在调用
Instantiate创建新对象,而非从池中取闲置对象,导致池列表持续膨胀,内存占用越来越高。 - 没有回收机制:玩家远离的旧平台、坍塌后的易碎平台仅被隐藏,未回池复用。
- 池操作逻辑混乱:
ContinueToSpawn中对池对象的移动、重新添加操作毫无意义,和后续生成逻辑冲突。
优化方案
- 拆分类型化对象池:将普通、二段跳(史莱姆)、易碎平台分别维护独立的队列池,避免混管混乱。
- 预生成闲置对象:初始化时批量生成对应数量的平台,设为禁用状态存入池。
- 复用优先逻辑:生成平台时先从池取禁用对象,重置属性后激活;池为空时再实例化新对象。
- 自动回收机制:
- 定期检查玩家下方超出视野阈值的平台,自动回池。
- 易碎平台完成坍塌动画后,通过动画事件触发回池。
修改后的ObjectPool代码
using System.Collections.Generic; using UnityEngine; public class ObjectPool : MonoBehaviour { public Transform player; [Header("General Values")] public float currentBlockY; public float disBeforeSpawnBlock = 10f; public float minDisBetweenBlocks; public float maxDisBetweenBlocks; public float minInsOffsetX; public float maxInsOffsetX; public float brokenMinOffsetX; public float brokenMaxOffsetX; public int initPoolSize = 10; // 每种平台的初始池容量 public float recycleThresholdY = -15f; // 玩家下方超过此Y值的平台将被回收 public AnimController animeC1; [Header("Grass")] public GameObject grassPlatformPrefab; public List<Sprite> grassSpriteList; private Queue<GameObject> grassPool = new Queue<GameObject>(); [Header("Slime")] public GameObject slimePlatformPrefab; private Queue<GameObject> slimePool = new Queue<GameObject>(); [Header("Broken")] public GameObject brokenPlatformPrefab; private Queue<GameObject> brokenPool = new Queue<GameObject>(); private void Awake() { InitPools(); InitSpawnBlock(); } private void Update() { if (currentBlockY - player.transform.position.y < disBeforeSpawnBlock) { ContinueToSpawn(); } RecycleOffscreenBlocks(); } // 初始化各类对象池 private void InitPools() { // 草平台池 for (int i = 0; i < initPoolSize; i++) { GameObject go = Instantiate(grassPlatformPrefab, transform); go.SetActive(false); grassPool.Enqueue(go); } // 史莱姆平台池 for (int i = 0; i < initPoolSize; i++) { GameObject go = Instantiate(slimePlatformPrefab, transform); go.SetActive(false); slimePool.Enqueue(go); } // 易碎平台池 for (int i = 0; i < initPoolSize; i++) { GameObject go = Instantiate(brokenPlatformPrefab, transform); go.SetActive(false); brokenPool.Enqueue(go); } } private void InitSpawnBlock() { for (int i = 0; i < initPoolSize; i++) { SpawnGrassPlatform(); } } private void ContinueToSpawn() { currentBlockY += Random.Range(minDisBetweenBlocks, maxDisBetweenBlocks); if (player.position.y > 20 && Random.Range(0f, 1f) < 0.5f) { SpawnBrokenPlatform(); // 额外生成一个草平台 Vector2 extraPos = new Vector2(Random.Range(minInsOffsetX, maxInsOffsetX), currentBlockY - Random.Range(1, 5)); SpawnGrassPlatform(extraPos); } else if (player.position.y > 150 && Random.Range(0f, 1f) < 0.1f) { SpawnSlimePlatform(); } else { SpawnGrassPlatform(); } } // 生成草平台(支持指定位置) private void SpawnGrassPlatform(Vector2? pos = null) { Vector2 spawnPos = pos ?? new Vector2(Random.Range(minInsOffsetX, maxInsOffsetX), currentBlockY); GameObject go = GetOrCreateGrassPlatform(); go.transform.position = spawnPos; go.GetComponent<SpriteRenderer>().sprite = grassSpriteList[Random.Range(0, grassSpriteList.Count)]; go.SetActive(true); } // 从草平台池取对象,无则创建 private GameObject GetOrCreateGrassPlatform() { if (grassPool.Count > 0) { return grassPool.Dequeue(); } else { return Instantiate(grassPlatformPrefab, transform); } } // 生成史莱姆平台 private void SpawnSlimePlatform() { Vector2 spawnPos = new Vector2(Random.Range(minInsOffsetX, maxInsOffsetX), currentBlockY); GameObject go = GetOrCreateSlimePlatform(); go.transform.position = spawnPos; animeC1.SlimeAnimationOne(); go.SetActive(true); } private GameObject GetOrCreateSlimePlatform() { if (slimePool.Count > 0) { return slimePool.Dequeue(); } else { return Instantiate(slimePlatformPrefab, transform); } } // 生成易碎平台 private void SpawnBrokenPlatform() { Vector2 spawnPos = new Vector2(Random.Range(minInsOffsetX, maxInsOffsetX), currentBlockY); GameObject go = GetOrCreateBrokenPlatform(); go.transform.position = spawnPos; // 重置平台状态 var collider = go.GetComponent<CapsuleCollider2D>(); if (collider != null) collider.enabled = true; var anim = go.GetComponent<Animator>(); if (anim != null) anim.Rebind(); go.SetActive(true); } private GameObject GetOrCreateBrokenPlatform() { if (brokenPool.Count > 0) { return brokenPool.Dequeue(); } else { return Instantiate(brokenPlatformPrefab, transform); } } // 回收屏幕外的平台 private void RecycleOffscreenBlocks() { foreach (Transform child in transform) { if (!child.gameObject.activeSelf) continue; if (child.position.y < player.position.y + recycleThresholdY) { if (child.CompareTag("GrassPlatform")) { child.gameObject.SetActive(false); grassPool.Enqueue(child.gameObject); } else if (child.CompareTag("SlimePlatform")) { child.gameObject.SetActive(false); slimePool.Enqueue(child.gameObject); } else if (child.CompareTag("BrokenPlatform")) { child.gameObject.SetActive(false); brokenPool.Enqueue(child.gameObject); } } } } // 对外提供易碎平台回池接口(动画结束后调用) public void RecycleBrokenPlatform(GameObject brokenPlatform) { brokenPlatform.SetActive(false); brokenPool.Enqueue(brokenPlatform); } }
二、单向易碎平台实现(仅上方触碰触发坍塌)
问题核心
当前碰撞逻辑未判断接触方向,只要玩家碰到平台就触发隐藏,导致从下方顶到平台时也会消失。需要通过碰撞法线方向+玩家垂直速度判断是否为上方踩踏。
解决方案
利用碰撞接触点的法线向量判断方向:平台顶部被踩踏时,法线会向下指向玩家(y值为负),同时玩家处于下落状态(垂直速度为负)。
修改后的碰撞逻辑代码(挂载在易碎平台上)
using UnityEngine; public class BrokenPlatformLogic : MonoBehaviour { private ObjectPool objectPool; private void Awake() { objectPool = FindObjectOfType<ObjectPool>(); } private void OnCollisionEnter2D(Collision2D other) { if (other.gameObject.CompareTag("Player")) { // 获取第一个接触点的法线 ContactPoint2D contact = other.GetContact(0); // 判定条件:法线向下(y < -0.5)且玩家处于下落状态 if (contact.normal.y < -0.5f && other.relativeVelocity.y < 0) { // 播放坍塌动画 Animator anim = GetComponent<Animator>(); if (anim != null) anim.SetTrigger("Collapse"); // 禁用碰撞体防止重复触发 GetComponent<CapsuleCollider2D>().enabled = false; } } } // 动画事件:坍塌动画结束后调用此方法回池 public void OnCollapseAnimationEnd() { objectPool.RecycleBrokenPlatform(gameObject); } }
注意事项
- 给玩家添加
Player标签,易碎平台添加BrokenPlatform标签。 - 在易碎平台的Animator中,给坍塌动画添加动画事件,调用
OnCollapseAnimationEnd方法,确保动画完成后再回池。 - 可根据平台碰撞体形状微调
contact.normal.y的阈值(如-0.5),提升判断准确性。
内容的提问来源于stack exchange,提问作者The Hapki
相关产品推荐
相关产品推荐

