迭代事件向量 SDL2 的最佳方法

Best way to iterate over a vector of events SDL2

我有一个事件处理程序 class 可以将所有事件添加到一个向量中。该向量在每一帧之后被清除。但是,如果它在每一帧之后被清除,则无法在事件处理程序的句柄函数之外检测到事件 class。如果我不清除每帧末尾的向量。该程序变得无响应。虽然我可以检查我的 for 循环是否正在读取事件向量一段时间,直到向量变得阻塞以供程序理解它。

我的问题是:

使用 for 循环遍历事件是否是检查这些事件的好方法?或者像使用 while(SDL_PollEvents(&e))?

一样使用 while 循环来检查此向量中的事件是否更有用

为什么我在检查向量中的事件时没有清除向量时我的程序会变得无响应?

如何在 class 之外有效地检查我的向量 eventList。在哪里可以使用矢量检查按键事件或类似事件,并在检查事件后清除矢量,而不会出现程序无响应?

这是我的主要功能,我试图在没有上述问题的情况下读取这些事件。

int main(int argc, char* argv[]){
    EventHandler handler;

      SDL_SetRenderDrawColor(handler.render, 49, 49, 49, 255);
      SDL_RenderClear(handler.render);

      SDL_RenderPresent(handler.render);
      if(handler.eventList.size() >= 1){
        std::cout << "Detected an event: " << handler.eventList.size() << "\n";
      }
      for(std::vector<SDL_Event>::const_iterator i = handler.get().begin(); i != handler.get().end(); i++){
        if(i->type == SDL_KEYDOWN){
          if (i->key.keysym.sym == SDLK_w){
            std::cout << "You pressed the W key\n";
          }
        }
      }
      handler.eventList.clear();
      handler.handle();
    }
    return 0;
}

这是我希望能够检查 eventList

中的事件的地方
  for(std::vector<SDL_Event>::const_iterator i = handler.get().begin(); i != handler.get().end(); i++){
    if(i->type == SDL_KEYDOWN){
      if (i->key.keysym.sym == SDLK_w){
        std::cout << "You pressed the W key\n";
      }
    }
  }

这是我的eventHandlerclass

的简化版
class EventHandler {
public:
    
    bool quit;
    SDL_Event event;
    SDL_Window* window;
    SDL_Renderer* render;

    int width;
    int height;

    std::vector<SDL_Event> eventList;


    EventHandler()
    {
        //Iniliaze SDL
        if (SDL_Init(SDL_INIT_EVERYTHING) != 0){
            std::cout << "SDL Error: " << SDL_GetError();
        }
        window = SDL_CreateWindow("Fallen Planets", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1024, 1080, SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE);
        if (window == NULL){
            std::cout << "SDL Error: " << SDL_GetError();
            SDL_Quit();
            quit = true;
        }
        render = SDL_CreateRenderer(window, -1, 0);
        if (render == NULL){
            SDL_DestroyWindow(window);
            std::cout << "SDL Error: " << SDL_GetError();
            SDL_Quit();
        }

        //Initialize quit bool
        quit = false;


    }

    void handle(){
        while (SDL_PollEvent(&event)){
            eventList.push_back(event); // This adds the events to the eventList
            if (event.type == SDL_QUIT){
                SDL_DestroyWindow(window);
                SDL_DestroyRenderer(render);
                TTF_Quit();
                SDL_Quit();
                quit = true;
            }
            if (event.window.event == SDL_WINDOWEVENT_RESIZED){
                width = SDL_GetWindowSurface(window)->w;
                height = SDL_GetWindowSurface(window)->h;

            }

            //Update Mouse State
            SDL_GetMouseState(&mouseX, &mouseY);
            for (std::vector<SDL_Event>::const_iterator i = eventList.begin(); i != eventList.end(); ++i){
                if (i->type == SDL_KEYDOWN){
                    if (i->key.keysym.sym == SDLK_w){
                        std::cout << "w key pressed\n";
                    }
                }
            }
    }

    std::vector<SDL_Event> get(){
        return eventList;
    }

    void flush_events(){
        eventList.clear();
    }
};

最有效的方法是为每个事件调用一些函数,无论是鼠标移动、鼠标单击、window 调整大小等,这些应该通过调用在主事件循环中处理while (SDL_PollEvent(&event)) 然后对每个事件都有一些 if 语句,并在到达这些 if 语句时调用相应的函数。 SDL 这样做是因为您可能同时有超过 1 个事件,即像任何 fps 游戏一样,在按下 'w' 的同时移动鼠标。您可以(我根据您的示例猜测)只是将它们附加到该事件轮询循环中的向量,但这违背了让它循环遍历事件的目的,因为无论如何您最终都必须对您的向量执行此操作。您应该只创建一些 API 变量,当特定事件发生时将其设置为某个值,例如,不要使用函数,而只使用静态变量,例如 static unsigned int mousePosition[2] 并将 [0] 设置为x 鼠标位置 [1] 到 y 鼠标位置,然后在需要鼠标坐标时在程序中的某个位置使用它,或者在该示例中甚至 static SDL_Point mousePosition = {x, y}

例如:(单例class)

#pragma once

#ifndef EVENTS_H
#define EVENTS_H


#include <iostream>
#include <string>
#include <SDL2/SDL.h>




class Events    ///Singleton
{
public:
    Events(const Events&) = delete;
    Events(Events&&) = delete;
    Events& operator=(const Events&) = delete;
    Events& operator=(Events&&) = delete;




    static const bool& Display_Changed_Size();


    ///Mouse
    static const SDL_Point& Mouse_Pos();

    static const bool& Scrolled_Down();
    static const bool& Scrolled_Up();



    ///Keyboard
    static const std::string& Get_Text_Input();
    static const bool& Pasted_Text();
    static const bool& Copied_Text();

    static const bool& Backspace();



private:
    Events();


    static Events& Get_Instance();


    ///Allow Main to access private members. Works well, one instance, only called once for those functions too. in Main
    friend class Main;

    ///For event handling
    static void Event_Loop();


    ///For event handling
    static void Reset_Events();


    ///For quitting, used main only
    static const bool& Quit_Application();



    ///For Event_Loop()
    int eventLoopCounter = 0;   ///To ensure Event_Loop() doesn't get used twice in the same loop
    SDL_Event event;




    bool m_quit = false;

    bool m_Display_Changed_Size = false;



    ///Mouse
    SDL_Point m_Mouse_Pos = {0,0};
    bool m_Scrolled_Up = false;
    bool m_Scrolled_Down = false;



    ///Keyboard
    std::string m_Text_Input = "";
    bool m_Copied_Text = false;
    bool m_Pasted_Text = false;
    bool m_Backspace = false;



};



#endif // EVENTS_H

.cpp

#include "events.h"


Events::Events()
{
    std::cout << "Events constructor called\n";
}


Events& Events::Get_Instance()
{
    static Events instance;
    return instance;
}




void Events::Event_Loop()
{

    if (Get_Instance().eventLoopCounter == 0)
    {

        Get_Instance().eventLoopCounter += 1;

        while (SDL_PollEvent(&Get_Instance().event) != 0)
        {
            if (Get_Instance().event.type == SDL_QUIT)
            {
                Get_Instance().m_quit = true;
                break;
            }


            if (Get_Instance().event.type == SDL_WINDOWEVENT){
                if(Get_Instance().event.window.event == SDL_WINDOWEVENT_RESIZED) {
                    Get_Instance().m_Display_Changed_Size = true;
                }
            }





            ///Mouse
            if (Get_Instance().event.type == SDL_MOUSEMOTION)
            {
                Get_Instance().m_Mouse_Pos = {Get_Instance().event.motion.x, Get_Instance().event.motion.y};
            }


            if (Get_Instance().event.type == SDL_MOUSEWHEEL){

                if (Get_Instance().event.wheel.y > 0){  ///Scrolling up here
                    Get_Instance().m_Scrolled_Up = true;
                }
                if (Get_Instance().event.wheel.y < 0){  ///Scrolling down here
                    Get_Instance().m_Scrolled_Down = true;
                }
            }


            ///Keyboard
            if (Get_Instance().event.type == SDL_TEXTINPUT)
            {
                Get_Instance().m_Text_Input = Get_Instance().event.text.text;
                break;  ///Break here for multiple key presses registered at once
            }

            ///Keydown
            if (Get_Instance().event.type == SDL_KEYDOWN)
            {


                ///Handle copy
                if( Get_Instance().event.key.keysym.sym == SDLK_c && SDL_GetModState() & KMOD_CTRL )
                {
                    Get_Instance().m_Copied_Text = true;
                }
                ///Handle paste
                if( Get_Instance().event.key.keysym.sym == SDLK_v && SDL_GetModState() & KMOD_CTRL )
                {
                    Get_Instance().m_Pasted_Text = true;
                }


                if (Get_Instance().event.key.keysym.sym == SDLK_BACKSPACE)
                {
                    Get_Instance().m_Backspace = true;
                }

            }



        }

    }
    else
    {
        std::cout << "Called Events::Event_Loop(); more than once\n";
    }

}



void Events::Reset_Events()
{
    Get_Instance().eventLoopCounter = 0;

    Get_Instance().m_quit = false;

    Get_Instance().m_Display_Changed_Size = false;


    ///Mouse
    Get_Instance().m_Scrolled_Down = false;
    Get_Instance().m_Scrolled_Up = false;




    ///Keyboard
    Get_Instance().m_Text_Input = "";
    Get_Instance().m_Pasted_Text = false;
    Get_Instance().m_Copied_Text = false;

    Get_Instance().m_Backspace = false;


}


const bool& Events::Quit_Application()
{
    return Get_Instance().m_quit;
}

const bool& Events::Display_Changed_Size()
{
    return Get_Instance().m_Display_Changed_Size;
}





///Mouse
const SDL_Point& Events::Mouse_Pos()
{
    return Get_Instance().m_Mouse_Pos;
}


const bool& Events::Scrolled_Down()
{
    return Get_Instance().m_Scrolled_Down;
}

const bool& Events::Scrolled_Up()
{
    return Get_Instance().m_Scrolled_Up;
}







///Keyboard
const std::string& Events::Get_Text_Input()
{
    return Get_Instance().m_Text_Input;
}


const bool& Events::Pasted_Text()
{
    return Get_Instance().m_Pasted_Text;
}
const bool& Events::Copied_Text()
{
    return Get_Instance().m_Copied_Text;
}

const bool& Events::Backspace()
{
    return Get_Instance().m_Backspace;
}

我理解它的很多代码,但是这种类型的实现是我在使用 SDL2 时使用的,它不容易出错,因为它只是一个单例,没有人可以实例化并因此修改成员。我会修改 friend class Main 行,因为它在那里所以我的主循环 class 可以调用私有函数。甚至像 friend int main(int argc, char* argv[]); 这样的东西,所以 int main() 可以这样称呼它或类似的东西。为了使用它,只需在需要调用事件的任何地方包含 header“events.h”

mainloop中的用法,假设Mainloop是一个函数或者class并且是Events的友元,只需更改header[中已有的友元代码即可=39=]

主循环:

Events::Event_Loop();
.. Code
Events::Reset_Events();

在其他需要事件的文件中:

if ( SDL_PointInRect( &Events::Mouse_Pos(), &rct) ) {} //example