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

Unity对象池优化与单向易碎平台实现技术问询

Unity对象池优化与单向易碎平台实现方案

一、对象池删除与回池逻辑优化

问题核心

当前代码完全没实现对象池的复用逻辑:

  • 所有生成平台的方法都在调用Instantiate创建新对象,而非从池中取闲置对象,导致池列表持续膨胀,内存占用越来越高。
  • 没有回收机制:玩家远离的旧平台、坍塌后的易碎平台仅被隐藏,未回池复用。
  • 池操作逻辑混乱:ContinueToSpawn中对池对象的移动、重新添加操作毫无意义,和后续生成逻辑冲突。

优化方案

  1. 拆分类型化对象池:将普通、二段跳(史莱姆)、易碎平台分别维护独立的队列池,避免混管混乱。
  2. 预生成闲置对象:初始化时批量生成对应数量的平台,设为禁用状态存入池。
  3. 复用优先逻辑:生成平台时先从池取禁用对象,重置属性后激活;池为空时再实例化新对象。
  4. 自动回收机制:
    • 定期检查玩家下方超出视野阈值的平台,自动回池。
    • 易碎平台完成坍塌动画后,通过动画事件触发回池。

修改后的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);
    }
}

注意事项

  1. 给玩家添加Player标签,易碎平台添加BrokenPlatform标签。
  2. 在易碎平台的Animator中,给坍塌动画添加动画事件,调用OnCollapseAnimationEnd方法,确保动画完成后再回池。
  3. 可根据平台碰撞体形状微调contact.normal.y的阈值(如-0.5),提升判断准确性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 16:33:13