liangxiegame / QFramework

Godot/Unity3D System Design Architecture
https://qframework.cn
MIT License
4.41k stars 774 forks source link

关于 ResKit 中 ResLoader 用法上的可能的简化。 #126

Open Hocchi01 opened 8 months ago

Hocchi01 commented 8 months ago

目前我看文档的 ResLoader 推荐用法是在每个需要加载资源的脚本中申请 ResLoader 并在销毁时回收:

public class TestResKit : MonoBehaviour 
{
    ResLoader mResLoader = ResLoader.Allocate();
    void Destroy()
    {
    mResLoader.Recycle2Cache();
    mResLoader = null;
    }
}

秉持着减少样板代码的原则(尤其是在某个对象没有销毁事件,但却要为 ResLoader 额外编写 Destroy 方法) 我目前在项目中主要通过静态扩展(未修改 QF 项目代码)实现了用法上的简化:

public class TestResKit : MonoBehaviour, ICanLoadResource
{
    void Func()
    {
        // 某个需要加载资源的地方
        this.GetResLoader().LoadSync<GameObject>(...);
    }
}

实现思路:维护一个静态的关于<对象的HashCode,ResLoader>的字典,对象首次调用GetResLoader()方法会为其分配 ResLoader 并记录在字典中,后续调用则从字典中取 ResLoader;销毁时自动回收则是和 QF 中自动注销实现思路一致,为 gameObject 追加专门响应销毁时事件的 Component 并添加回收回调。代码如下:

    public interface ICanLoadResource
    {

    }

    public static class CanLoadResourceExtension
    {
        private static readonly Dictionary<int, ResLoader> _resLoaderDict = new Dictionary<int, ResLoader>();

        public static ResLoader GetResLoader(this ICanLoadResource obj)
        {
            int hashCode = obj.GetHashCode();

            // 对象首次调用 GetResLoader
            if (!_resLoaderDict.ContainsKey(hashCode))
            {
                _resLoaderDict.Add(hashCode, ResLoader.Allocate());

                // 首次调用,若是组件则默认在销毁时注册行为:回收资源引用
                if (obj is Component)
                {
                    var lm = (obj as Component).gameObject.GetComponent<LifecycleModule>();

                    if (!lm)
                    {
                        lm = (obj as Component).gameObject.AddComponent<LifecycleModule>();
                    }

                    lm.OnDestroyed += () =>
                    {
                        if (_resLoaderDict.ContainsKey(hashCode))
                        {
                            _resLoaderDict[hashCode].Recycle2Cache();
                            _resLoaderDict.Remove(hashCode);
                        }
                    };
                }
            }

            return _resLoaderDict[hashCode];
        }

        // 用于手动自行注销         
        public static void RecycleResLoader(this ICanLoadResource obj)
        {
            int hashCode = obj.GetHashCode();

            if (_resLoaderDict.ContainsKey(hashCode))
            {
                _resLoaderDict[hashCode].Recycle2Cache();
                _resLoaderDict.Remove(hashCode);
            }
        }
    }

附:其中 LifecycleModule

public class LifecycleModule : MonoBehaviour
{
    public Action OnDestroyed = () => { };
    private void OnDestroy()
    {
        OnDestroyed();
    }
}

可能存在的问题:不同对象的 HashCode 相同的情况,查阅了一些资料,不同对象应该是对应着不同的 HashCode。也可以考虑使用其他唯一性的键值。

Bian-Sh commented 8 months ago

收到