Allegro5全屏切换
我看到有些人切换全屏的代码只有一段,但没有什么效果:
al_toggle_display_flag(display, ALLEGRO_FULLSCREEN, true);
第一个参数是 指向ALLEGRO_DISPLAY的指针 ;
第二个参数是 ALLEGRO属性,要切换全屏可以用 ALLEGRO_FULLSCREEN 或者 ALLEGRO_FULLSCREEN_WINDOW ;
第三个参数是 bool值 ,表示第二个参数的属性是否要开启;
- ALLEGRO_FULLSCREEN和ALLEGRO_FULLSCREEN_WINDOW的区别
这里说明一下 ALLEGRO_FULLSCREEN 和 ALLEGRO_FULLSCREEN_WINDOW 的区别:
前者会更改屏幕分辨率并创建全屏的display,现在大部分游戏全屏方案都是这种。
后者不会更改屏幕分辨率,而是把窗口大小改为与当前屏幕分辨率相同的大小,并无边框化,然后背景为黑色,游戏渲染区域会在左上角,这种方案很少有游戏会采用,但切换速度比前者快。
- 解决方案
我有个思路,就是先摧毁旧的窗口/全屏的display,然后更改下个创建窗口的属性 并 创建新的窗口/全屏的display
- 以下为主要代码
思路:
1、首先检测左/右Alt是否按下或者已经松开;
2、然后再检测Enter是否在Alt按下时按下;
3、如果触发Alt+Enter快捷键,则开始切换全屏。
void toggleFullscreenThread(bool *b){ALLEGRO_DISPLAY* oldDisplay = alDisplay;al_destroy_display(oldDisplay);static int lastFlg = 0;if (fullScreen) {lastFlg = al_get_display_flags(alDisplay);al_set_new_display_flags(ALLEGRO_FULLSCREEN);}elseal_set_new_display_flags(lastFlg);alDisplay = al_create_display(currentResolutionWidth, currentResolutionHeight);al_register_event_source(alEventQueue, al_get_display_event_source(alDisplay));SetFocus(al_get_win_window_handle(alDisplay));if (fullScreen) {windowOffset1 = 0;windowOffset2 = 0;}else {windowOffset1 = GetSystemMetrics(SM_CXFRAME) * 2;windowOffset2 = GetSystemMetrics(SM_CYCAPTION);}*b = true;return;
}int msg(){static bool ALT_PUSHING = false;if (alEvent.type == ALLEGRO_EVENT_KEY_DOWN) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ALT|| alEvent.keyboard.keycode == ALLEGRO_KEY_ALTGR) {ALT_PUSHING = true;}}if (alEvent.type == ALLEGRO_EVENT_KEY_UP) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ALT|| alEvent.keyboard.keycode == ALLEGRO_KEY_ALTGR) {ALT_PUSHING = false;}}if (alEvent.type == ALLEGRO_EVENT_KEY_DOWN) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ENTER) {if (ALT_PUSHING) {fullScreen = !fullScreen;bool toggleDone = false;std::thread th(toggleFullscreenThread,&toggleDone);th.detach();while(!toggleDone){al_rest(5);}}}}
}
- msg()函数解析:
1、反转fullScreen变量;
2、声明toggleDone变量,用来判断切换是否完成;
3、使用std的thread创建多线程,传递toggleFullscreenThread函数地址给thread以及toggleDone变量指针地址给toggleFullscreenThread函数;
4、detach线程,让它在后台切换;
5、主线程使用Allegro5自带的rest函数每过5秒检测一次是否切换完成,如果未完成则继续休息5秒;
**Q.**为什么不直接切换,而是使用多线程切换?
**A.**因为直接切换会导致一些问题,如果在游戏跑的时候display被摧毁太久没有恢复就会直接退出。
**Q.**为什么不使用windows的Sleep函数或this_thread::sleep_for函数或者usleep等其他休眠函数?
**A.**使用Allegro5提供的rest函数可能暂停Allegro5一切有关的内容。
**Q.**为什么要rest5秒这么久而不是2秒及以下?
**A.*多给它点时间切换,如果切换的太快就会出现问题,比如GPU占用很高但只有十几fps,卡成。
-
toggleFullscreenThread(bool* )函数解析
1、声明 名为oldDisplay的ALLEGRO_DISPLAY指针 并指向当前的display即alDisplay。
2、调用 al_destroy_display(oldDisplay) 销毁旧的display。
3、声明 lastFlg,int变量,用来表示窗口模式下的display flag。
4、判断要切换为窗口模式还是全屏模式。
5、调用 al_create_display(800, 600) 来创建一个新的display并让当前的display指针指向它。
6、调用 al_register_event_source(alEventQueue, al_get_display_event_source(alDisplay)) 来重新注册display事件源队列,即窗口事件。
7、调用SetFocus(al_get_win_window_handle(alDisplay)),WINAPI函数用来给窗口获取焦点,防止全屏后因一些原因失去焦点。
8、重新获取窗口标题高度以及窗口边框大小,若全屏则直接设置为0。
9、最后设置toggleDone值为true。 -
以下是我的整体代码,可以参考一下
main.cpp
#include <allegro5/allegro.h>
#include <allegro5/allegro_font.h>
#include <allegro5/allegro_ttf.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_primitives.h>
#include <allegro5/allegro_direct3d.h>
#include <allegro5/allegro_windows.h>#include <cstdio>
#include <string>#include "defs.h"
#include "fps.hpp"
#include "math.hpp"
#include "thread.hpp"static ALLEGRO_DISPLAY* alDisplay = nullptr;
static ALLEGRO_EVENT_QUEUE* alEventQueue = nullptr;
static ALLEGRO_EVENT alEvent;
static ALLEGRO_BITMAP* alCursor_arrow = nullptr;
static ALLEGRO_BITMAP* alCursor_hand = nullptr;void init();
void destroy();
int msg();
void frame();
void render();static ALLEGRO_FONT* alFont_arial_12 = 0;
static ALLEGRO_FONT* alFont_arial_12_blod = 0;
static ALLEGRO_BITMAP* backMap = nullptr;static int currentResolutionWidth = -1;
static int currentResolutionHeight = -1;
static int windowOffset1 = 0;
static int windowOffset2 = 0;static ALLEGRO_BITMAP* currentCursor = nullptr;
static float mouseX = 0.f, mouseY = 0.f;
static std::string fpsStr = "";
static bool windowFoucing = -1;
static bool fullScreen = false;int main(int argc, const char* argv[]) {init();alCursor_arrow = al_load_bitmap("Data/Image/POINTER.png");alFont_arial_12 = al_load_font("Data/TTF/arial.ttf", 12, ALLEGRO_TTF_NO_KERNING | ALLEGRO_TTF_MONOCHROME);alFont_arial_12_blod = al_load_font("Data/TTF/arialbd.ttf", 12, ALLEGRO_TTF_NO_KERNING | ALLEGRO_TTF_MONOCHROME);//al_set_new_display_flags(ALLEGRO_FULLSCREEN);currentResolutionWidth = SAFE_RESOLUTION_WIDTH;currentResolutionHeight = SAFE_RESOLUTION_HEIGHT;if (fullScreen)al_set_new_display_flags(ALLEGRO_FULLSCREEN);elseal_set_new_display_flags(NULL);alDisplay = al_create_display(currentResolutionWidth, currentResolutionHeight);windowOffset1 = GetSystemMetrics(SM_CXFRAME) * 2;windowOffset2 = GetSystemMetrics(SM_CYCAPTION);alEventQueue = al_create_event_queue();al_register_event_source(alEventQueue, al_get_keyboard_event_source());al_register_event_source(alEventQueue, al_get_mouse_event_source());al_register_event_source(alEventQueue, al_get_display_event_source(alDisplay));backMap = al_load_bitmap("Data/Image/map.bmp");SetFocus(al_get_win_window_handle(alDisplay));al_hide_mouse_cursor(alDisplay);int alError = 0;frame_rater<SAFE_SCREEN_REFRESH_RATE> fpsRater;currentCursor = alCursor_arrow;for (;;) {if (!al_is_event_queue_empty(alEventQueue)) {alError = msg();if (alError != 0) break;}frame();render();al_wait_for_vsync();fpsRater.delay();}destroy();return alError;
}void init() {if (!al_install_system(ALLEGRO_VERSION_INT, atexit)){printf("Error: Allegro Install Error!");exit(1);}if (!al_install_mouse()) {printf("Warrn: Allegro Mouse Install Failed!");}if (!al_install_keyboard()) {printf("Warrn: Allegro Keyboard Install Failed!");}if (!al_init_font_addon()) {printf("Warrn: Allegro Font Addon Init Failed!");}if (!al_init_ttf_addon()) {printf("Warrn: Allegro TTF Addon Init Failed!");}if (!al_init_image_addon()) {printf("Warrn: Allegro Image Addon Init Failed!");}return;
}void destroy() {al_destroy_display(alDisplay);al_destroy_event_queue(alEventQueue);al_destroy_font(alFont_arial_12);al_destroy_font(alFont_arial_12_blod);al_destroy_bitmap(backMap);al_destroy_bitmap(alCursor_arrow);al_destroy_bitmap(alCursor_hand);al_shutdown_image_addon();return;
}void toggleFullscreenThread(bool *b){ALLEGRO_DISPLAY* oldDisplay = alDisplay;al_destroy_display(oldDisplay);int lastFlg = 0;if (fullScreen) {lastFlg = al_get_display_flags(alDisplay);al_set_new_display_flags(ALLEGRO_FULLSCREEN);}elseal_set_new_display_flags(lastFlg);alDisplay = al_create_display(currentResolutionWidth, currentResolutionHeight);al_register_event_source(alEventQueue, al_get_display_event_source(alDisplay));SetFocus(al_get_win_window_handle(alDisplay));al_hide_mouse_cursor(alDisplay);if (fullScreen) {windowOffset1 = 0;windowOffset2 = 0;}else {windowOffset1 = GetSystemMetrics(SM_CXFRAME) * 2;windowOffset2 = GetSystemMetrics(SM_CYCAPTION);}*b = true;return;
}int msg() {while (!al_is_event_queue_empty(alEventQueue)) {al_get_next_event(alEventQueue, &alEvent);if (alEvent.type == ALLEGRO_EVENT_DISPLAY_CLOSE)return 98;if (alEvent.type == ALLEGRO_EVENT_KEY_CHAR) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ESCAPE)return 99;}if (alEvent.type == ALLEGRO_EVENT_DISPLAY_SWITCH_IN) {al_hide_mouse_cursor(alDisplay);windowFoucing = true;}if (alEvent.type == ALLEGRO_EVENT_DISPLAY_SWITCH_OUT){al_show_mouse_cursor(alDisplay);windowFoucing = false;}static bool ALT_PUSHING = false;if (alEvent.type == ALLEGRO_EVENT_KEY_DOWN) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ALT|| alEvent.keyboard.keycode == ALLEGRO_KEY_ALTGR) {ALT_PUSHING = true;}}if (alEvent.type == ALLEGRO_EVENT_KEY_UP) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ALT|| alEvent.keyboard.keycode == ALLEGRO_KEY_ALTGR) {ALT_PUSHING = false;}}if (alEvent.type == ALLEGRO_EVENT_KEY_DOWN) {if (alEvent.keyboard.keycode == ALLEGRO_KEY_ENTER) {if (ALT_PUSHING) {fullScreen = !fullScreen;bool toggleDone = false;std::thread th(toggleFullscreenThread,&toggleDone);th.detach();while(!toggleDone){al_rest(5);}}}}}return 0;
}void frame() {fpsStr = std::to_string(fps(0.5));if (windowFoucing) {static int mx, my;static int wx, wy;al_get_mouse_cursor_position(&mx, &my);al_get_window_position(alDisplay, &wx, &wy);mx = mx - wx - windowOffset1;my = my - wy - windowOffset1 - windowOffset2;if (mx < 0)mx = 0;if (my < 0)my = 0;if (mx > currentResolutionWidth)mx = currentResolutionWidth;if (my > currentResolutionHeight)my = currentResolutionHeight;mouseX = mx;mouseY = my;}return;
}void render() {al_clear_to_color(al_map_rgb(0, 0, 0));al_draw_bitmap(backMap, 0.f, 0.f, NULL);al_draw_textf(alFont_arial_12, al_map_rgba(255, 255, 255, 255), 0, 14, NULL, "Mouse Position:%s,%s", std::to_string(mouseX).c_str(), std::to_string(mouseY).c_str());al_draw_bitmap(currentCursor, mouseX, mouseY, NULL); // Cursoral_draw_text(alFont_arial_12_blod, al_map_rgba(0, 0, 0, 125), 0, 0, NULL, fpsStr.c_str()); // FPS_Shdwal_draw_text(alFont_arial_12, al_map_rgba(255, 255, 255, 255), 0, 0, NULL, fpsStr.c_str()); // FPSal_flip_display();return;
}
types.hpp
#pragma oncetypedef int BOOL;
#define FALSE 0
#define TRUE 1
#define ERROR -1#ifndef NULL
#define NULL 0
#endif// typedef signed char INT8; /**< -127~+127 */
typedef unsigned char UINT8; /**< 0~255 */
typedef short INT16; /**< -32767~+32767 */
typedef unsigned short UINT16; /**< 0~+65535 */
typedef int INT32; /**< -2147483647 ~+2147483647*/
typedef unsigned int UINT32; /**< 0~4294967295 */typedef unsigned long DWORD;typedef float FLOAT;struct Vec2
{int x, y;Vec2(int _x, int _y){x = _x;y = _y;}bool operator==(const Vec2 &a) const{if (x == a.x && y == a.y)return true;return false;}bool operator!=(const Vec2 &a) const{if (x != a.x || y != a.y)return true;return false;}
};
typedef Vec2 *PVec2;struct FVec2
{float x, y;FVec2(float x, float y){this->x = x;this->y = y;}bool operator==(const FVec2 &a) const{if (x == a.x && y == a.y)return true;return false;}bool operator!=(const FVec2 &a) const{if (x != a.x || y != a.y)return true;return false;}
};
typedef FVec2 *PFVec2;
thread.hpp
#pragma once#include <thread>
#include <chrono>void sleep(unsigned long millisec)
{std::this_thread::sleep_for(std::chrono::milliseconds(millisec));
}
math.hpp
#pragma onceint addmax(int num, int numadd, int max)
{int result = num + numadd;if (result > max)return max;return result;
}int minusmin(int num, int numminus, int min)
{int result = num - numminus;if (result < min)return min;return result;
}
fps.hpp
#pragma once#include <ctime>#include "types.hpp"
#include "thread.hpp"template<std::intmax_t FPS>
class frame_rater {
public:frame_rater() : // initialize the object keeping the pacetime_between_frames{ 1 }, // std::ratio<1, FPS> secondstp{ std::chrono::steady_clock::now() }{}void delay() {// add to time pointtp += time_between_frames;// and sleep until that time pointstd::this_thread::sleep_until(tp);}private:// a duration with a length of 1/FPS secondsstd::chrono::duration<double, std::ratio<1, FPS>> time_between_frames;// the time point we'll add to in every loopstd::chrono::time_point<std::chrono::steady_clock, decltype(time_between_frames)> tp;
};class _FPS_Limit
{public:float maxFPS = 60;private:DWORD timeInPerFrame;DWORD timeBegin;DWORD timePhase;public:_FPS_Limit(){timeBegin = 0;timePhase = 0;timeInPerFrame = 0;}void GameStartofLoop(){timeInPerFrame = 1000.0f / maxFPS;timeBegin = clock() / 1000.0f;}void GameEndofLoop(){timePhase = clock() / 1000.0f - timeBegin;if (timePhase < timeInPerFrame){sleep(DWORD(timeInPerFrame - timePhase));}}
};double fps(double countDelay = 1)
{using namespace std;using namespace std::chrono;// static double countDelay = 1;static double fps = 0.0;static int frameCount = 0;static auto lastTime = system_clock::now();static auto curTime = system_clock::now();curTime = system_clock::now();auto duration = duration_cast<microseconds>(curTime - lastTime);double duration_s = double(duration.count()) * microseconds::period::num / microseconds::period::den;if (duration_s > countDelay){fps = frameCount / duration_s;frameCount = 0;lastTime = curTime;}++frameCount;return fps;
}template<typename T,typename ...Args>
T doLate(double countDelay,T (*func)(T),Args... args)
{using namespace std;using namespace std::chrono;static auto lastTime = system_clock::now();static auto curTime = system_clock::now();curTime = system_clock::now();auto duration = duration_cast<microseconds>(curTime - lastTime);double duration_s = double(duration.count()) * microseconds::period::num / microseconds::period::den;static T lstT;if (duration_s > countDelay){lstT=func(args...);lastTime = curTime;}return lstT;
}
defs.h
#pragma onceconst int SAFE_RESOLUTION_WIDTH = 800;
const int SAFE_RESOLUTION_HEIGHT= 600;
const int SAFE_SCREEN_REFRESH_RATE= 60;
此方法理论上linux和windows通用