一个真正的程序往往不是一个 main 方法就可以完成的,显然,对于初学 OpenGL 的我们(尽管我们可能有很长时间的 C/C++ 编程经验)来说,在初次实现一个小项目的时候,需要在开发之前理清项目结构。这样在正式编码的时候才能不会手忙脚乱。
首先定义顶级类游戏类(Game Class)控制游戏状态,资源管理器类控制游戏资源(ResourceManager Class),所以我们需要这两个类:
1 | // Game 类 |
除此之外,我们之前在代码中使用的 Shader 类和纹理操作也是需要封装的,在之前的 Shader 类中,我们用 Shader 类直接从读取着色器文件到返回 Program 全部在类内实现,这显然不是一个好的实现,更何况我们现在又有了全局的资源管理器类,所以将 Shader 类重写并添加相应的 Texture 类(Shader 和 Texture 统属于 ResourceManager 类进行管理,请注意我们的资源管理类是为了直接调用其他类而存在的,所以属性和方法等都用 static 修饰)。所以我们为 ResourceManager 类添加 Shader 和 Texture 的管理:
1 | // 定义着色器与纹理存储 |
我们定义了用来存储 shader 对象和 texture 对象的变量,然后给定了从文件中读取源文件编译处理成 shader 或者 texture 并写入 map 的方法,并给定从 map 中返回 shader 或 texture 的方法。这样,我们在调用着色器或者纹理的时候可以这样操作。
1 | Shader shader = ResourceManager::GetShader("shader_name"); //调用已有着色器 |
纹理操作类似。
在暂时不考虑细节实现的情况下,我们使用 ResourceManager 进行资源对象管理暂时就这样子了。接下来看另一个类 Game 。Game 类的主要作用是(简单)管理游戏代码,并与此同时将所有的窗口代码从游戏中解耦。这样子的话,我们就可以把相同的类迁移到完全不同的窗口库(比如SDL或SFML)而不需要做太多的工作。在我们的 Game 类中,我们需要一个游戏状态的属性(GameState),为此我们先创建一个枚举值代表游戏的不同状态:
1 | enum GameState { |
除此外我们再封装一个初始化函数(Init),一个输入处理的函数(ProcessInput),一个画面的更新函数(Update),一个渲染函数(Render)。
1 | // 游戏状态 |
暂不考虑具体的实现,我们一个小游戏的框架首先要搭建起来,在这个游戏框架里, Game 类处理游戏代码, ResourceManager 管理游戏的资源。