创建 OpenGL 上下文
cocos2d-x 是基本 OpenGL 实现的,所以游戏启动之后做的第一件事就是创建 OpenGL 上下文,之后才能进行渲染。在 win32 上,创建 OpenGL context 的过程就是创建一个 OpenGL 窗口,cocos2d-x 使用的是 glfw 库,首先我们回顾一下 glfw 创建窗口的过程
int main()
{
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
GLFWwindow* window = glfwCreateWindow(400, 400, "Color", nullptr, nullptr);
if (window == nullptr) { return 1; }
glfwMakeContextCurrent(window);
glewExperimental = GL_TRUE;
if (GLEW_OK != glewInit()) { return 2; }
glClearColor(255, 255, 255, 255);
while (!glfwWindowShouldClose(window))
{
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT);
//render
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
这是 glfw 创建 OpenGL 窗口、调用 OpenGL 渲染指令以及监听处理各种事件的过程,接下来看看 cocos2d-x 中是如何使用 glfw 的。
回顾窗口启动
关于 cocos2d-x 的窗口启动过程可以看我前面的文章
cocos2d-x 窗口启动
下面简单的回顾一下这个过程。在 main 函数创建一个 AppDelegate 实例之后,调用 Application 的 run 方法启动游戏
int Application::run()
{
//...
initGLContextAttrs();
if (!applicationDidFinishLaunching())
{
return 1;
}
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
glview->retain();
while(!glview->windowShouldClose())
{
//...
director->mainLoop();
glview->pollEvents();
}
if (glview->isOpenGLReady())
{
director->end();
director->mainLoop();
director = nullptr;
}
glview->release();
return 0;
}
这是 run 方法的关键代码,首先调用 initGLContextAttrs 设置 OpenGL context 的属性,这个方法在其子类 Appdelegate 中实现
void AppDelegate::initGLContextAttrs()
{
GLContextAttrs glContextAttrs = {
8, 8, 8, 8, 24, 8};
GLView::setGLContextAttrs(glContextAttrs);
}
目前 cocos2d-x 只能设置六个属性,分别是 rgba 四个颜色属性,深度和模板;定义一个 GLContextAttrs 变量,然后调用 GLView 的静态方法进行设置,GLView 是我们这篇文章的重点,下面会解析这个类。设置完 OpenGL Context 属性之后,调用 applicationDidFinishLaunching 方法进行一些初始化,这个方法也是在子类 AppDelegate 中实现
bool AppDelegate::applicationDidFinishLaunching() {
auto director = Director::getInstance();
auto glview = director->getOpenGLView();
if(!glview) {
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32) || (CC_TARGET_PLATFORM == CC_PLATFORM_MAC) || (CC_TARGET_PLATFORM == CC_PLATFORM_LINUX)
glview = GLViewImpl::createWithRect("CppSource", Rect(0, 0, designResolutionSize.width, designResolutionSize.height));
#else
glview = GLViewImpl::create("CppSource");
#endif
director->setOpenGLView(glview);
}
//...
return true;
}
applicationDidFinishLaunching 做了很多初始化工作,其中最重要的就是初始化导演实例 Director 和 OpenGL 窗口管理类实例 GLView。Director 对象中有一个 GLView 实例,该实例默认为空,调用 GLViewImpl 的 createWithRect 创建一个实例。GLViewImpl 继承自 GLView,是桌面程序的 OpenGL 窗口管理类,而 GLView 则是一个上层的 OpenGL 窗口管理类,提供部分接口,更多的接口则在 GLViewImpl 中实现。GLView 和 GLViewImpl 的目录结构
|-cocos
|-platform
|-CCGLView.h
|-CCGLView.cpp
|-desktop
|-CCGLViewImpl-desktop.h
|-CCGLViewImpl-desktop.cpp
applicationDidFinishLaunching 执行完成之后就得到了一个 Director 实例和一个 GLViewImpl 实例,OpenGL 窗口也创建出来了,OpenGL context 也存在了,可以说 OpenGL 环境已经具备了,接下来就可以开始渲染了。在 while 循环中调用 GLView 的 windowShouldClose 检测 OpenGL 窗口是否关闭,如果关闭了则跳出循环,做一些回收操作然后程序结束,如果没关闭则程序在这里阻塞,每一帧都会进入到这个 while 循环体中,这个循环体做的事情就是监听事件和 OpenGL 渲染。
GLView(GLViewImpl)
接下来就是主角 GLView(GLViewImpl) 登场了,首先看 GLViewImpl 的构造函数
GLViewImpl::GLViewImpl(bool initglfw)
: _captured(false)
, _supportTouch(false)
, _isInRetinaMonitor(false)
, _isRetinaEnabled(false)
, _retinaFactor(1)
, _frameZoomFactor(1.0f)
, _mainWindow(nullptr)
, _monitor(nullptr)
, _mouseX(0.0f)
, _mouseY(0.0f)
{
_viewName = "cocos2dx";
g_keyCodeMap.clear();
for (auto& item : g_keyCodeStructArray)
{
g_keyCodeMap[item.glfwKeyCode] = item.keyCode;
}
GLFWEventHandler::setGLViewImpl(this);
if (initglfw)
{
glfwSetErrorCallback(GLFWEventHandler::onGLFWError);
glfwInit();
}
}
这里做了两件重要的事,第一件就是初始化 GLFWEventHandler;第二件就是执行 glfwInit()
,也就是我们熟悉的 glfw 初始化了。接下来看GLFWEventHandler 这个类,这个类在 CCGLViewImpl-desktop.cpp 中定义,它的内容很简单
class GLFWEventHandler
{
public: