SDL 開發實戰(四): SDL 事件處理


在前面學習SDL的例子運行時,我們發現我們的窗口只停留了幾秒,但是如果設置更長時間顯然也有其他的弊端。

那么有沒有一種好的辦法可以解決這個問題呢?例如:能不能讓窗口一直顯示,直到檢測到用戶用鼠標點擊關閉按鈕后才關閉呢?

答:顯然可以! 下面就來介紹一下SDL的事件處理機制。

1. SDL 事件處理機制原理

SDL事件就是鍵盤事件,鼠標事件,窗口事件等。SDL將所有事件都存放在一個隊列中。所有對事件的操作,其實就是對隊列的操作。

而SDL對這些事件都做了封裝,提供了統一的API,下面我們就來詳細的看一下。

2. SDL 操作事件隊列的API

  • SDL_PollEvent: 將隊列頭中的事件拋出來。
  • SDL_WaitEvent: 當隊列中有事件時,拋出事件。否則處於阻塞狀態,釋放 CPU。
  • SDL_WaitEventTimeout: 與SDL_WaitEvent的區別時,當到達超時時間后,退出阻塞狀態。
  • SDL_PeekEvent: 從隊列中取出事件,但該事件不從隊列中刪除。
  • SDL_PushEvent: 向隊列中插入事件。

3. SDL 處理事件的API

  • SDL_WindowEvent : Window窗口相關的事件。
  • SDL_KeyboardEvent : 鍵盤相關的事件。
  • SDL_MouseMotionEvent : 鼠標移動相關的事件。
  • SDL_QuitEvent : 退出事件。
  • SDL_UserEvent : 用戶自定義事件。

實戰 

在上面我們也說過了,如果不做SDL窗口的關閉事件的處理,我們是不能夠通過點擊關閉按鈕,關閉SDL顯示的窗口的。這樣對用戶是非常不友好的。

下面我們對SDL的Hello World代碼做一下優化,其實就是在程序的末尾增加SDL_Event的事件處理,本例做的事情是檢測用戶是否按下了退出按鈕。如果檢測到了,則直接退出,否則保持顯示狀態。

代碼實例:

// SDL.cpp : 此文件包含 "main" 函數。程序執行將在此處開始並結束。
//

#include "pch.h"
#include <iostream>

extern "C" {
#include "SDL.h"
}

int main(int argc, char* argv[])
{
    if (SDL_Init(SDL_INIT_VIDEO)) {
        std::cout << "SDL_Init Error: " << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Window *win = SDL_CreateWindow("Hello World!", 100, 100, 640, 480, SDL_WINDOW_SHOWN);

    if (win == nullptr) {
        std::cout << "SDL_CreateWindow Error: " << SDL_GetError() << std::endl;
        return 1;
    }

    SDL_Renderer *ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC);
    if (ren == nullptr) {
        SDL_DestroyWindow(win);
        std::cout << "SDL_CreateRender Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    std::string imagePath = "1.bmp";
    SDL_Surface *bmp = SDL_LoadBMP(imagePath.c_str());
    if (bmp == nullptr) {
        SDL_DestroyRenderer(ren);
        SDL_DestroyWindow(win);
        std::cout << "SDL_LoadBMP Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    SDL_Texture *tex = SDL_CreateTextureFromSurface(ren, bmp);

    SDL_FreeSurface(bmp);
    if (tex == nullptr) {
        SDL_DestroyRenderer(ren);
        SDL_DestroyWindow(win);
        std::cout << "SDL_CreateTextureFromSurface Error: " << SDL_GetError() << std::endl;
        SDL_Quit();
        return 1;
    }

    for (int i = 0; i < 3; ++i) {
        SDL_RenderClear(ren);
        SDL_RenderCopy(ren, tex, NULL, NULL);
        SDL_RenderPresent(ren);
        SDL_Delay(1000);
    }

    int quit = 1;
    do {
        SDL_Event event;
        SDL_WaitEvent(&event);
        switch (event.type) {
        case SDL_QUIT:
            SDL_Log("Event type is %d", event.type);
            quit = 0;
        default:
            SDL_Log("Event type is %d", event.type);
            break;
        }
    } while (quit);

    SDL_DestroyTexture(tex);
    SDL_DestroyRenderer(ren);
    SDL_DestroyWindow(win);
    SDL_Quit();

    // Return
    return 0;
}

 

運行效果:

我們能看到同HelloWorld一樣的界面輸出,此時如果我們對SDL窗口不做任何處理的話,界面是不會消失的,當我們點擊窗口的關閉按鈕后,界面關閉了。

 

4. SDL_PollEvent 與 SDL_WaitEvent 

細心的人會發現,使用 SDL_PollEvent 和使用 SDL_WaitEvent 兩個方法都能處理SDL的事件隊列。如果我們簡單的將程序中的SDL_WaitEvent 替換為SDL_PollEvent ,運行時發現也沒什么問題。但是當我們打開任務管理器時,發現我們的程序居然跑滿了CPU。是什么原因造成的呢?我們來仔細看一下我們增加的代碼吧。它由兩層 while 循環組成,最里面的while循環的意思是,當隊列中一直能取出事件,那就讓他一直做下去,直到事件隊列為空。外面的while循環的意思是,當隊列為空的時候,重新執行內部的while循環。也就是說代碼一直在工作,從不休息。所以導致CPU很快就跑滿了。 而使用SDL_WaitEvent方法,CPU就不會出現這個問題,因為當它發現隊列為空時,它會阻塞在那里,並將CPU占用釋放掉。

SDL_WaitEvent和SDL_PollEvent這兩個方法使用的場景不同:

  • 對於游戲來說,它要求事件的實時處理,我們最好使用SDL_PollEvent方法
  • 對於一些其它實時性不高的case來說,則可以使用 SDL_WaitEvent了

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM