Unity对象池初体验

学习完了涂鸦跳跳后,对对象池有了一个初步的了解,由此进行一下总结

一.对象池

对象池运用:用我自己的话理解就是当我们对某些游戏物体重复出现时,为了节约内存的重复开销,我们可以通过对象池对已经生成的游戏物体(不需要时)进行重复利用。
其思想包含了:
1.创建对象池
2.当旧对象从池中移除时,同时要对此对象进行重新利用(赋值等操作)
3.添加移除的对象进入相应的对象池
由此进行2和3的循环就可以对重复利用的物体进行对象池调用以及回收。

二.对象池实例运用

1.对象池的创建时机

过早创建对象池,池中的对象会占用大量内存。若等到游戏使用对象时再创建对象池,可能因为大量实例化造成掉帧。所以,我认为在Loading界面创建下一个场景需使用的对象池是较为合理的。比如天天飞车中的NPC车,金币,赛道,在进入单局比赛后才用到。可以在进入比赛的Loading界面预先创建金币,NPC车,赛道的对象池,比赛中直接申请使用。
这里就直接展示代码了

private Queue<GameObject> tilePoor = new Queue<GameObject>();
/// <summary>
/// 生成对象池
/// </summary>
void InitPool()
{
    for (int i = 0; i < sum; i++)
    {
        GameObject go = Instantiate(tile, transform);
        go.SetActive(false);
        go.name = i.ToString();
        tilePoor.Enqueue(go);
    }
}

一上来创建的游戏物体是隐藏的,并且是没有任何特指是哪些物体,要通过另外代码来激活

 private void Start()
    {
        GetTotalWeight();//获取生成砖块的权重
        InitPool();
        for (int i = 0; i < sum; i++)
        {
            InitRandomTile();        //通过循环遍历 将游戏物体从对象池中移除 显示出来
        }
    }
  /// <summary>
    /// 随机砖块生成
    /// </summary>
    void InitRandomTile()
    {
        GameObject go = GetDiffObject(ObjectType.tile);//获取从池中移除(出队列)的那个旧对象
        float random = Random.Range(0, totalWeight);
        int tileType = SetTileByRandomNumber(random);
        Vector2 pos = new Vector2(Random.Range(-4.5f, 4.5f), currentY);
        switch (tileType)
        {
            case 0:
                go.transform.position = pos;
                currentY += Random.Range(gameSetting.normalTile.minHeight, gameSetting.normalTile.maxHeight);
                go.name = "0";//因为Tile脚本中砖块图片是根据名字序号生成的
                go.SetActive(true);
                break;
            case 1:
                go.transform.position = pos;
                currentY += Random.Range(gameSetting.brokenTile.minHeight, gameSetting.brokenTile.maxHeight);
                go.name = "1";//因为Tile脚本中砖块图片是根据名字序号生成的
                go.SetActive(true);


                break;
            case 2:
                go.transform.position = pos;
                currentY += Random.Range(gameSetting.oneTimeOnly.minHeight, gameSetting.oneTimeOnly.maxHeight);
                go.name = "3";//因为Tile脚本中砖块图片是根据名字序号生成的
                go.SetActive(true);

                break;
            case 3:
                go.transform.position = pos;
                currentY += Random.Range(gameSetting.springTile.minHeight, gameSetting.springTile.maxHeight);
                go.name = "3";//因为Tile脚本中砖块图片是根据名字序号生成的
                go.SetActive(true);


                break;
            case 4:
                go.transform.position = pos;
                currentY += Random.Range(gameSetting.movingHorizontally.minHeight, gameSetting.movingHorizontally.maxHeight);
                go.name = "4";//因为Tile脚本中砖块图片是根据名字序号生成的
                go.SetActive(true);

                break;
            case 5:
                go.transform.position = pos;
                currentY += Random.Range(gameSetting.movingVertically.minHeight, gameSetting.movingVertically.maxHeight);
                go.name = "5";//因为Tile脚本中砖块图片是根据名字序号生成的
                go.SetActive(true);

                break;
            default:
                break;
        }
    }
  public GameObject GetDiffObject(ObjectType type)
    {
        switch (type)
        {
            case ObjectType.tile:
                return tilePoor.Dequeue();//从池子中取出的游戏物体
            case ObjectType.item:
                return itemPoor.Dequeue();
            case ObjectType.enemy:
                return enemyPoor.Dequeue();
            case ObjectType.coin:
                return coinPoor.Dequeue();
            case ObjectType.bullet:
                return bulletPoor.Dequeue();
            default:
                return null;
        }
    }
2.对象池的重复利用
 /// <summary>
    /// 重复利用池子中相机看不见的物体
    /// </summary>
    /// <param name="go"></param>
    /// <param name="type"></param>
    public void AddUsedToPoor(GameObject go, ObjectType type)
    {
        go.SetActive(false);
        switch (type)
        {
            case ObjectType.tile:
                tilePoor.Enqueue(go); //将要移除物体添加至池中(尾部)
                CreateTile();//将移除物体(首部)从池子出队然后进行生成
                break;
            case ObjectType.coin:
                coinPoor.Enqueue(go);
                break;
            case ObjectType.enemy:
                enemyPoor.Enqueue(go);
                break;
            case ObjectType.item:
                itemPoor.Enqueue(go);
                break;
            case ObjectType.bullet:
                bulletPoor.Enqueue(go);
                break;
        }
    }
     /// <summary>
    /// 当一个Tile被移除对象池调用此方法
    /// </summary>
    void CreateTile()
    {
        if (gameState != GameState.GAMEOVER)
        {
            InitRandomTile();
        }
    }

AddUsedToPoor()函数添加到相应需要条件回收的语句判断中即可

存在的问题:

由于每个砖块都有其独自的特性(刚体组件),比如说人物踩到一个碎的砖块会改变砖块的刚体组件的重力为1.5f垂直下落,此时如果回收完这个砖块后,其身上组件并不会初始化,会导致最上方生成的砖块直接下落,这点还尚未改进。
我这里使用的是简单粗暴的方法,应该是可以在对象池中同步解决的吧…希望有大佬能指教一下

//在Tile脚本的Update中调用
//只要砖块在人物正上方都不会受重力影响 (解决触发检测gravityScale=1.5f后 对象池生成的砖块直接下落bug)
        if (transform.position.y > player.transform.position.y)
        {
            transform.GetComponent<Rigidbody2D>().gravityScale = 0;
        }

总结

这是我第一次接触到对象池的运用,可能并没有说的特别清楚,大致能理解到一些概念了吧,今后会在某些需要重复使用游戏物体时进行调用(FPS子弹或者刷怪笼这类)

猜你喜欢

转载自blog.csdn.net/wangjianxin97/article/details/87077261