> 技术文档 > C++ Catrix游戏引擎开发之全屏,无边框,窗口模式切换逻辑(适用于OpenGL接口)

C++ Catrix游戏引擎开发之全屏,无边框,窗口模式切换逻辑(适用于OpenGL接口)


前言

最近在开发自己的Catrix游戏引擎,手搓我的世界,分享一下有关游戏窗口在全屏,无边框模式和窗口模式之间的切换逻辑,相关代码已经从游戏主引擎剥离出来,可以单独运行。

Catrix Game Engine 制作的我的世界https://github.com/Korloa/BlockVerse-with-Catrix-Game-Engine由于项目刚刚建立,readme.md,代码注释等还未完善,尽情谅解,正在创作,谢谢支持!

总览

代码涉及到的GLFW相关函数如下:

//获取窗口位置与大小void glfwGetWindowPos(GLFWwindow* window,int* xpos,int* ypos)void glfwGetWindowSize(GLFWwindow* window,int* width,int* height)//设置窗口位置与大小void glfwSetWindowPos(GLFWwindow* window,int xpos,int ypos)void glfwSetWindowSize(GLFWwindow* window,int width,int height)//监视器的读取与设置GLFWmonitor* glfwGetPrimaryMonitor(void)void glfwSetWindowMonitor(GLFWwindow* window,GLFWmonitor* monitor,int xpos,int ypos,int width,int height,int refreshRate)//显示模式const GLFWvidMode* glfwGetVideoMode(GLFWmonitor* monitor)//修改窗口属性void glfwSetWindowAttrib(GLFWwindow* window,int attrib,int value)//初始化与卸载与交互int glfwInit(void)GLFWwindow* glfwCreateWindow(int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share)void glfwMakeContextCurrent(GLFWwindow* window)void glfwTerminate(void)int glfwWindowShouldClose(GLFWwindow* window)int glfwGetKey(GLFWwindow* window, int key)void glfwSwapBuffers(GLFWwindow* window)void glfwPollEvents(void)//绘制相关void glClearColor(GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)void glClear(GLbitfield mask)

重点函数介绍

1...glfwGetVideoMode(GLFWmonitor* monitor)

这个函数用于获取指定显示器的显示模式,显示模式实际上就是一个结构体数据,包含了显示器的分辨率,刷新率和红绿蓝的位深,具体而言的结构体构造如下:

typedef struct GLFWvidMode{ int width; int height; int redBits; int greenBits; int blueBits; int refreshRate;}GLFWvidMode;

在本示例中,我们要使用要使用的是refreshRate和width,height以支持全屏和无边框显示

2...glfwGetPrimaryMonitor(void) && glfwSetWindowMonitor(....)

这两个函数是配合使用的,第一个函数获取当前的主显示器,主显示器通常是用户操作系统中设置为“主屏幕”的那个显示器(比如 Windows 中“主显示器”设置,或 macOS 中菜单栏所在的屏幕)。成功则返回指向GLFWmonitor结构的指针,否则NULL

第二个函数就比较复杂,它的第二个参数moniter可以填NULL或一个GLFWmontior类型的指针对象,当为NULL,就代表是窗口模式,这时候它的refreshRate就失效了,而不为NULL时,它的xpos,ypos,width,height都会失效,全屏显示,以refreshRate刷新率显示。(严格来说,width,height,refreshRate的设置在全屏状态下不会立即生效,而是作为一个搜索项,较为复杂,不作讨论)


3...glfwSetWindowAttrib(GLFWwindow* window,int attrib,int value)

//常见属性如下GLFW_VISIBLE //是否可见GLFW_FLOATING //是否置顶GLFW_RESIZABLE //是否可以改变窗口大小GLFW_DECORATED //是否显示标题栏,边框

value值就是GLFW_FALSE,GLFW_TRUE

代码实践

引擎是使用Visual Studio构建项目的,但是这里的演示程序是使用Dev C++

main.cpp

#include #include \"screen.h\"#include int main() { if (!glfwInit()) { fprintf(stderr, \"Failed to initialize GLFW\\n\"); return -1; } GLFWwindow* window = glfwCreateWindow(1280, 720, \"Window Mode Demo\", NULL, NULL); if (!window) { fprintf(stderr, \"Failed to create GLFW window\\n\"); glfwTerminate(); return -1; } glfwMakeContextCurrent(window); initWindowModeSystem(window); printf(\"Press:\\n\"); printf(\" F1 -> Windowed Mode\\n\"); printf(\" F2 -> Fullscreen Mode\\n\"); printf(\" F3 -> Borderless Windowed Mode\\n\"); while (!glfwWindowShouldClose(window)) { float r = (currentMode == WINDOWED) ? 0.2f :  (currentMode == FULLSCREEN) ? 0.4f :  (currentMode == BORDERLESS) ? 0.6f : 0.2f; glClearColor(r, 0.3f, 0.5f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); static int f1_down = 0, f2_down = 0, f3_down = 0; if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !f1_down) { setWindowMode(window, WINDOWED); f1_down = 1; } else if (glfwGetKey(window, GLFW_KEY_F2) == GLFW_PRESS && !f2_down) { setWindowMode(window, FULLSCREEN); f2_down = 1; } else if (glfwGetKey(window, GLFW_KEY_F3) == GLFW_PRESS && !f3_down) { setWindowMode(window, BORDERLESS); f3_down = 1; } if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) f1_down = 0; if (glfwGetKey(window, GLFW_KEY_F2) == GLFW_RELEASE) f2_down = 0; if (glfwGetKey(window, GLFW_KEY_F3) == GLFW_RELEASE) f3_down = 0; glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0;}

screen.h

#include #include #define WINDOWED 0#define FULLSCREEN 1#define BORDERLESS 2static int currentMode = -1;static int windowedX = 100, windowedY = 100;static int windowedWidth = 1280, windowedHeight = 720;static GLFWmonitor* currentMonitor = NULL;static const GLFWvidmode* desktopMode = NULL;void initWindowModeSystem(GLFWwindow* window) { currentMonitor = glfwGetPrimaryMonitor(); desktopMode = glfwGetVideoMode(currentMonitor); currentMode = -1; glfwGetWindowPos(window, &windowedX, &windowedY); glfwGetWindowSize(window, &windowedWidth, &windowedHeight);}void setWindowMode(GLFWwindow* window, int mode) { if (currentMonitor == NULL || desktopMode == NULL) { return; }if (mode == currentMode) { return; } switch (mode) { case WINDOWED: { glfwSetWindowMonitor(window, NULL,  windowedX, windowedY,  windowedWidth, windowedHeight,  0); glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_TRUE); break; } case FULLSCREEN: { if(currentMode==WINDOWED){ glfwGetWindowPos(window, &windowedX, &windowedY); glfwGetWindowSize(window, &windowedWidth, &windowedHeight); } glfwSetWindowMonitor(window, currentMonitor,  0, 0,  desktopMode->width,  desktopMode->height,  desktopMode->refreshRate); break; } case BORDERLESS: { if(currentMode==WINDOWED){ glfwGetWindowPos(window, &windowedX, &windowedY); glfwGetWindowSize(window, &windowedWidth, &windowedHeight);} glfwSetWindowMonitor(window, NULL, 0, 0, 1280, 720, 0); glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_FALSE); glfwSetWindowPos(window, 0, 0); glfwSetWindowSize(window, desktopMode->width, desktopMode->height); break; } default: return; } currentMode = mode;}

注意,因为DevC++的编译器是Mingw,所以链接的是.a后缀的静态glfw库,下载链接如下

Download | GLFWGLFW source code and binary distribution download links.https://www.glfw.org/download.html

解压后的这个目录要加入到Dev C++里,或者复制到Dev C++的默认bin目录下

并且加入编译命令即可

运行效果

出现的问题

其实有两个bug是很隐蔽的

NO.1

最初的代码是没有加入if验证这一块在这两处,如果没有if的话在全屏和无边框之间切换后会使得windowX,windowY丢失数据,再切换回窗口模式就会出现问题

NO.2

最初这部分代码长这样,方框中的语句位置是有问题的,无边框切回窗口模式没问题,但是无边框切回全屏再切回窗口模式就读不到这个代码了,导致窗口模式无边框,不如把这两个if-else-if分支全干掉,统一一下

 glfwSetWindowMonitor(window, NULL,windowedX, windowedY,windowedWidth, windowedHeight,0); glfwSetWindowAttrib(window, GLFW_DECORATED, GLFW_TRUE);

结语

制作不易,感谢对项目支持!