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);
结语
制作不易,感谢对项目支持!