yorickyao
yorickyao
发布于 2025-10-14 / 14 阅读
0
0

🧩基于Unity的简单框架工具类(2)

对象池

1、 概述

对象池系统提供了一个高效、易用的对象生命周期管理解决方案,支持普通C#对象和Unity GameObject两种类型的对象池化。系统采用静态门面模式,提供简洁的API接口,同时内部实现完善的池管理机制。

2、核心组件

IPoolable 接口

/// <summary>
/// 池对象接口,定义自动回收行为
/// </summary>
public interface IPoolable
{
    void OnGet();      // 从池获取时调用
    void OnRelease();  // 回收到池时调用
    void OnDestroy();  // 销毁时调用
}

池化对象基类

PoolableObject<T> - C#对象池化基类

  • 泛型设计,支持任意C#类

  • 自动回收机制

  • 继承自IPoolable接口

using UnityEngine.Pool;

/// <summary>
/// 泛型 C# 对象池抽象基类,支持自动回收机制。
/// </summary>
public abstract class PoolableObject<T> : IPoolable where T : PoolableObject<T>, new()
{
    internal ObjectPool<T> pool;         // 对象池引用

    public void Release()                // 回收当前实例到对象池
    {
        pool?.Release(this as T);
    }
    public virtual void OnGet() { }      // 获取对象时的回调方法。
    public virtual void OnRelease() { }  // 回收对象时的回调方法。
    public virtual void OnDestroy() { }  // 销毁对象时的回调方法。
}

PoolableGameObject - GameObject池化基类

  • MonoBehaviour基类

  • 支持Unity GameObject的完整生命周期

  • 自动处理激活状态和父子关系

using UnityEngine;
using UnityEngine.Pool;

/// <summary>
/// MonoBehaviour 池对象抽象基类,支持自动回收机制。
/// </summary>
public abstract class PoolableGameObject : MonoBehaviour, IPoolable
{
    internal ObjectPool<GameObject> pool;  // 对象池引用。

    public void Release()                  // 回收当前实例到对象池。
    {
        pool?.Release(gameObject);
    }
    public virtual void OnGet() { }        // 获取对象时的回调方法。
    public virtual void OnRelease() { }    // 回收对象时的回调方法。
    public virtual void OnDestroy() { }    // 销毁对象时的回调方法
}

静态门面类 - Pool

提供统一的简化API:

  • Get<T>()- 获取C#对象

  • Get(prefab, parent)- 获取GameObject

  • Release(obj)- 回收对象

  • ClearAllPools()- 清理所有池

using UnityEngine;

/// <summary>
/// 对象池静态门面类,提供统一的对象获取与回收接口
/// </summary>
public static class Pool
{
    // 全局 C# 对象池最大容量
    public static int ClassPoolMaxSize
    {
        get => PoolImpl.ClassPoolMaxSize;
        set => PoolImpl.ClassPoolMaxSize = value;
    }

    // 全局 GameObject 对象池最大容量
    public static int GameObjectPoolMaxSize
    {
        get => PoolImpl.GameObjectPoolMaxSize;
        set => PoolImpl.GameObjectPoolMaxSize = value;
    }

    // 获取 C# 池对象实例
    public static T Get<T>() where T : PoolableObject<T>, new()
    {
        return PoolImpl.Get<T>();
    }

    // 获取池化 GameObject 实例,可指定父节点
    public static GameObject Get(GameObject prefab, Transform parent = null)
    {
        return PoolImpl.Get(prefab, parent);
    }

    // 回收 C# 池对象实例
    public static void Release<T>(T obj) where T : PoolableObject<T>, new()
    {
        PoolImpl.Release(obj);
    }

    // 回收池化 GameObject 实例
    public static void Release(GameObject go)
    {
        PoolImpl.Release(go);
    }

    // 清理所有对象池,释放所有资源
    public static void ClearAllPools()
    {
        PoolImpl.ClearAllPools();
    }
}

内部实现类 - PoolImpl

内部实现类,负责具体池管理逻辑

关键特性

  • ​懒初始化​:对象池在首次使用时创建

  • ​分组管理​:相同预制体的对象自动分组存放

  • ​自动绑定​:自动为GameObject添加池化组件

  • ​容量控制​:支持全局最大容量设置

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;

/// <summary>
/// 对象池实现类,负责具体池管理逻辑。
/// </summary>
internal static class PoolImpl
{
    private static readonly Dictionary<Type, object> classPools = new();                     // C# 类型对象池集合。
    private static readonly Dictionary<string, ObjectPool<GameObject>> prefabPools = new();  // GameObject 类型对象池集合。
    private static Transform poolRoot;                                                       // 池化对象根节点。
    private static readonly Dictionary<string, Transform> prefabRoots = new();               // 每种预制体的分组节点集合。
    public static int ClassPoolMaxSize { get; set; } = 100;                                  // C# 对象池最大容量。
    public static int GameObjectPoolMaxSize { get; set; } = 100;                             // GameObject 对象池最大容量

    // 获取或创建池化对象根节点
    private static Transform PoolRoot
    {
        get
        {
            if (poolRoot == null)
            {
                var go = new GameObject("[PoolRoot]");
                go.hideFlags = HideFlags.DontSave;
                poolRoot = go.transform;
            }
            return poolRoot;
        }
    }

    // 获取或创建指定预制体的分组节点    
    private static Transform GetPrefabRoot(string prefabName)
    {
        if (!prefabRoots.TryGetValue(prefabName, out var root))
        {
            var go = new GameObject(prefabName + "_Pool");
            go.hideFlags = HideFlags.DontSave;
            root = go.transform;
            root.SetParent(PoolRoot, false);
            prefabRoots[prefabName] = root;
        }
        return root;
    }

    // 获取 C# 池对象实例
    public static T Get<T>() where T : PoolableObject<T>, new()
    {
        var type = typeof(T);
        if (!classPools.TryGetValue(type, out var poolObj))
        {
            ObjectPool<T> pool = null;
            pool = new ObjectPool<T>(
                () => {
                    var obj = new T();
                    obj.pool = pool;
                    obj.OnGet();
                    return obj;
                },
                obj => obj.OnGet(),
                obj => obj.OnRelease(),
                obj => obj.OnDestroy(),
                true,
                ClassPoolMaxSize
            );
            classPools[type] = pool;
            return pool.Get();
        }
        return ((ObjectPool<T>)poolObj).Get();
    }

    // 获取池化 GameObject 实例,可指定父节点
    public static GameObject Get(GameObject prefab, Transform parent = null)
    {
        var key = prefab.name;
        var groupRoot = GetPrefabRoot(key);
        if (!prefabPools.TryGetValue(key, out var pool))
        {
            pool = new ObjectPool<GameObject>(
                () => {
                    var go = GameObject.Instantiate(prefab, groupRoot);
                    var poolable = go.GetComponent<PoolableGameObject>();
                    if (poolable == null)
                        poolable = go.AddComponent<PoolableGameObject>();
                    poolable.pool = pool;
                    poolable.OnGet();
                    return go;
                },
                go => {
                    var poolable = go.GetComponent<PoolableGameObject>();
                    poolable?.OnGet();
                    go.SetActive(true);
                    go.transform.SetParent(parent ?? groupRoot, false);
                },
                go => {
                    var poolable = go.GetComponent<PoolableGameObject>();
                    poolable?.OnRelease();
                    go.SetActive(false);
                    go.transform.SetParent(groupRoot, false);
                },
                go => {
                    var poolable = go.GetComponent<PoolableGameObject>();
                    poolable?.OnDestroy();
                    GameObject.Destroy(go);
                },
                true,
                GameObjectPoolMaxSize
            );
            prefabPools[key] = pool;
            var obj = pool.Get();
            obj.transform.SetParent(parent ?? groupRoot, false);
            return obj;
        }
        var go2 = pool.Get();
        go2.transform.SetParent(parent ?? groupRoot, false);
        return go2;
    }

    // 回收 C# 池对象实例。
    public static void Release<T>(T obj) where T : PoolableObject<T>, new()
    {
        obj.Release();
    }

    // 回收池化 GameObject 实例。
    public static void Release(GameObject go)
    {
        var poolable = go.GetComponent<PoolableGameObject>();
        poolable?.Release();
    }
    
    // 清理所有对象池并释放所有资源。
    public static void ClearAllPools()
    {
        foreach (var pool in classPools.Values)
            (pool as IDisposable)?.Dispose();
        classPools.Clear();

        foreach (var pool in prefabPools.Values)
            pool.Clear();
        prefabPools.Clear();

        foreach (var root in prefabRoots.Values)
            if (root != null) GameObject.Destroy(root.gameObject);
        prefabRoots.Clear();

        if (poolRoot != null)
        {
            GameObject.Destroy(poolRoot.gameObject);
            poolRoot = null;
        }
    }
}

3、使用方法

池化C#对象

// 定义池化类
public class Bullet : PoolableObject<Bullet>
{
    public float speed;
    
    public override void OnGet()
    {
        // 对象被取出时的初始化
        speed = 10f;
    }
    
    public override void OnRelease()
    {
        // 对象被回收时的清理
        speed = 0f;
    }
}

// 使用示例
var bullet = Pool.Get<Bullet>();
// 使用bullet...
bullet.Release(); // 手动回收

池化GameObject

// 定义池化组件
public class Enemy : PoolableGameObject
{
    public int health = 100;
    
    public override void OnGet()
    {
        health = 100;
        gameObject.SetActive(true);
    }
    
    public override void OnRelease()
    {
        gameObject.SetActive(false);
    }
}

// 使用示例
var enemyPrefab = Resources.Load<GameObject>("Enemy");
var enemy = Pool.Get(enemyPrefab, parentTransform);
// 使用enemy...
enemy.GetComponent<Enemy>().Release(); // 通过组件回收

4、特性说明

自动管理

  • 自动分组​:相同预制体的对象自动分组管理

  • 层级管理​:在场景中创建清晰的池化对象层级结构

  • 容量控制​:可设置全局最大容量限制

生命周期回调

提供完整的生命周期管理:

  • OnGet()- 对象被取出时调用,用于初始化

  • OnRelease()- 对象被回收时调用,用于重置状态

  • OnDestroy()- 对象被销毁时调用,用于资源释放

内存管理

  • 自动回收​:支持对象自动回收到池中

  • 资源清理​:提供完整的池清理机制

  • 防止泄漏​:妥善处理场景切换时的资源释放

5、配置选项

全局容量设置

// 设置C#对象池最大容量
Pool.ClassPoolMaxSize = 200;

// 设置GameObject对象池最大容量
Pool.GameObjectPoolMaxSize = 50;

清理机制

// 清理所有对象池(通常在场景切换时调用)
Pool.ClearAllPools();

6、最佳实践

  • 对象设计​:池化对象应实现完整的状态重置逻辑

  • 及时回收​:使用完毕后及时调用Release()方法

  • 避免外部引用​:回收后不应继续持有对象引用

  • 合理设置容量​:根据实际使用情况调整池容量

7、注意事项

  • 池化对象不应在构造函数中进行复杂初始化,应在OnGet()中完成

  • GameObject池化要求预制体必须包含PoolableGameObject组件

  • 回收对象时,系统会自动处理激活状态和父子关系

  • 在游戏退出或场景切换时建议调用ClearAllPools()进行清理

对象池系统通过简洁的API和完善的内部管理机制,为项目提供了高效的对象复用解决方案,能够显著提升游戏性能并减少内存分配。


评论