You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

关于将游戏引擎共享资源以静态变量形式管理的技术疑问

游戏引擎共享资源管理:从单例模式到就近静态变量的技术疑问

嘿,作为一个常年跟游戏引擎打交道的开发者,太懂你这种代码越写越臃肿、单例管理器可读性崩盘的痛苦了😂!先理清楚你的情况:你是正在学习游戏引擎结构的学生,之前靠单例管理器类来统筹共享资源,但随着代码量增长,这种集中式管理的可读性越来越差,所以想改成在资源使用位置就近声明变量的方式来优化。

首先先明确你要共享的两个核心类:

class Mesh {
public:
    Mesh() {}
    ~Mesh() {}
};
class Material {
public:
    Material() {}
    ~Material() {}
};

你之前用的单例管理器代码大概是这样的(补全了部分未写完的内容):

class SomeManager {
public:
    static SomeManager& GetInst() {
        static SomeManager inst{};
        return inst;
    }

    Mesh* get_shared_msh() { return s_msh; }
    Material* get_shared_mtrl() { return s_mtrl; }

private:
    SomeManager()
        : s_msh(nullptr)
        , s_mtrl(nullptr)
    {
        s_msh = new Mesh;
        s_mtrl = new Material;

        // 这里原本还有其他资源的初始化逻辑...
    }

    ~SomeManager() {
        delete s_msh;
        delete s_mtrl;
    }

    Mesh* s_msh;
    Material* s_mtrl;
};

接下来聊聊你想改成的「就近静态变量」方案,以及它的优缺点和注意点:

这种方案的核心思路(举个例子)

你可以把每个共享资源封装成一个返回静态实例的函数,比如:

Mesh& GetSharedMesh() {
    static Mesh sharedMesh;
    return sharedMesh;
}

Material& GetSharedMaterial() {
    static Material sharedMaterial;
    return sharedMaterial;
}

这样在需要用到共享Mesh的地方,直接调用GetSharedMesh()就能拿到同一个实例,不用再绕到单例管理器里。

优点很明显

  • 可读性大幅提升:资源的定义和访问逻辑绑定在一起,不用再在庞大的单例管理器里找对应的getter函数,新人接手也更容易理解
  • 延迟初始化:只有当第一次调用函数时才会创建资源,比单例管理器启动时就初始化所有资源更节省内存和启动时间
  • 职责拆分清晰:每个资源的管理逻辑独立,不会出现一个管理器类塞满几十种资源的情况

但也得注意潜在的坑

  • 生命周期不可控:静态局部变量的销毁顺序是C++标准未明确规定的,如果某个资源销毁时依赖另一个已经被销毁的资源,很容易触发崩溃;而单例管理器可以在引擎 shutdown 阶段统一销毁所有资源,顺序可控
  • 单元测试难度高:单例模式还能通过替换实例来做Mock测试,但静态局部变量的实例是固定的,想替换它做测试会非常麻烦
  • 资源重复初始化风险:如果不小心在多个地方写了重复的静态变量定义,会导致多个实例出现(比如不同编译单元里的同名静态变量),所以一定要把这种封装函数放在头文件里,并确保头文件被正确保护

折中方案推荐

如果你只是反感单例管理器的臃肿,其实可以把大的管理器拆分成多个小的职责单一的单例,比如MeshManagerMaterialManager,每个只负责一类资源的加载、缓存和销毁,既保留了单例模式的生命周期可控性,又避免了单个类过于庞大的问题。

另外还要提一句:你之前的代码里用了裸指针new创建资源,一定要记得在析构里delete,或者直接用智能指针替代,比如:

std::unique_ptr<Mesh> s_msh = std::make_unique<Mesh>();

这样能避免手动管理内存带来的泄漏风险。

总的来说,如果是小型学习项目,「就近静态变量」的方案足够灵活好用;如果是需要长期维护的大型引擎,拆分后的单例管理器或者依赖注入框架会更适合。

备注:内容来源于stack exchange,提问作者Tee Mo

火山引擎 最新活动