【问题标题】:How to handle multiple keypresses at once with SDL?如何使用 SDL 一次处理多个按键?
【发布时间】:2010-11-18 04:29:45
【问题描述】:

让自己熟悉使用 C++ 在 Ubuntu 上使用 SDL 进行 OpenGL 编程。经过一番环顾和试验,我开始明白了。我需要有关使用 SDL 处理键盘事件的建议。

我有一个第一人称相机,可以前进,后退,左右扫射,并使用鼠标环顾四周,这很棒。这是我的 processEvents 函数:

void processEvents()
{
    int mid_x = screen_width  >> 1;
int mid_y = screen_height >> 1;
int mpx = event.motion.x;
int mpy = event.motion.y;
float angle_y  = 0.0f;
float angle_z  = 0.0f;

while(SDL_PollEvent(&event))
{
    switch(event.type)
    {
        case SDL_KEYDOWN:
            switch(event.key.keysym.sym)
            {
                case SDLK_ESCAPE:
                    quit = true;
                    break;
                case SDLK_w:
                    objCamera.Move_Camera( CAMERASPEED);
                    break;
                case SDLK_s:
                    objCamera.Move_Camera(-CAMERASPEED);
                    break;
                case SDLK_d:
                    objCamera.Strafe_Camera( CAMERASPEED);
                    break;
                case SDLK_a:
                    objCamera.Strafe_Camera(-CAMERASPEED);
                    break;
                default:
                    break;
            }
            break;

        case SDL_MOUSEMOTION:
            if( (mpx == mid_x) && (mpy == mid_y) ) return;

            SDL_WarpMouse(mid_x, mid_y);

            // Get the direction from the mouse cursor, set a resonable maneuvering speed
            angle_y = (float)( (mid_x - mpx) ) / 1000;      
            angle_z = (float)( (mid_y - mpy) ) / 1000;

            // The higher the value is the faster the camera looks around.
            objCamera.mView.y += angle_z * 2;

            // limit the rotation around the x-axis
            if((objCamera.mView.y - objCamera.mPos.y) > 8)  objCamera.mView.y = objCamera.mPos.y + 8;
            if((objCamera.mView.y - objCamera.mPos.y) <-8)  objCamera.mView.y = objCamera.mPos.y - 8;

            objCamera.Rotate_View(-angle_y);

            break;

        case SDL_QUIT:
            quit = true;
            break;

        case SDL_VIDEORESIZE:
            screen = SDL_SetVideoMode( event.resize.w, event.resize.h, screen_bpp, SDL_OPENGL | SDL_HWSURFACE | SDL_RESIZABLE | SDL_GL_DOUBLEBUFFER | SDL_HWPALETTE );
            screen_width = event.resize.w;
            screen_height = event.resize.h;
            init_opengl();
            std::cout << "Resized to width: " << event.resize.w << " height: " << event.resize.h << std::endl;
            break;

        default:
            break;
    }
}
}

现在,虽然它正在工作,但它有一些限制。最大的问题和我的问题的目的是它似乎只处理按下的最新键。因此,如果我按住 's' 向后走,然后按 'd' 向右扫射,我最终会向右扫射但不会向后走。

有人可以为我指出正确的方向,以便使用 SDL 更好地处理键盘、一次支持多个按键等吗?

谢谢

【问题讨论】:

    标签: c++ linux opengl ubuntu sdl


    【解决方案1】:

    SDL 跟踪所有键的当前状态。您可以通过以下方式访问此状态:

    SDL_GetKeyState()

    因此,每次迭代您都可以根据键状态更新动作。为了使运动平稳,您应该根据更新之间经过的时间来更新运动幅度。

    【讨论】:

      【解决方案2】:

      一个好的方法是编写一个键盘(“输入”)处理程序,该处理程序将处理输入事件并将事件的状态保持在某种结构中(关联数组听起来不错 - key[keyCode])。

      每次键盘处理程序接收到“按下按键”事件时,它都会将按键设置为启用 (true),当它收到按键按下事件时,将其设置为禁用 (false)。

      然后您可以一次检查多个键而无需直接拉动事件,并且您将能够在整个帧中重复使用键盘,而无需将其传递给子程序。

      一些快速的伪代码:

      class KeyboardHandler {
          handleKeyboardEvent(SDL Event) {
              keyState[event.code] = event.state;
          }
      
          bool isPressed(keyCode) {
              return (keyState[keyCode] == PRESSED);
          }
      
          bool isReleased(keyCode) {
              return (keyState[keyCode] == RELEASED);
          }
      
          keyState[];
      }
      
      ...
      
      while(SDL Pull events)
      {
          switch(event.type) {
              case SDL_KEYDOWN:
              case SDL_KEYUP:
                      keyHandler.handleKeyboardEvent(event);
                  break;
              case SDL_ANOTHER_EVENT:
                      ...
                  break;
          }   
      }
      
      // When you need to use it:
      if(keyHandler.isPressed(SOME_KEY) && keyHandler.isPressed(SOME_OTHER_KEY))
          doStuff(TM);
      

      【讨论】:

      • 真的是一个不错的解决方案,几乎可以用在你所有的游戏中
      【解决方案3】:

      如果您使用的是 SDL2,请使用 SDL_GetKeyboardState

      例子:

      const Uint8 *keyboard_state_array = SDL_GetKeyboardState(NULL);
      
      SDL_PollEvent(&event);
      
      if(event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
      {
          // Move centerpoint of rotation for one of the trees:
          if (keyboard_state_array[SDL_SCANCODE_UP] && !(keyboard_state_array[SDL_SCANCODE_DOWN]))
          {
              --location.y;
          }
          else if (!keyboard_state_array[SDL_SCANCODE_UP] && keyboard_state_array[SDL_SCANCODE_DOWN])
          {
              ++location.y;
          }
      
          if (keyboard_state_array[SDL_SCANCODE_RIGHT] && !keyboard_state_array[SDL_SCANCODE_LEFT])
          {
              ++location.x;
          }
          else if (!keyboard_state_array[SDL_SCANCODE_RIGHT] && keyboard_state_array[SDL_SCANCODE_LEFT])
          {
              --location.x;
          }
      }
      

      【讨论】:

      • 而不是else ifs&amp;&amp;s,我们可以放just ifs,一切都一样,不需要额外的条件。
      【解决方案4】:

      不仅仅是查看 keydown 事件,任何同时关注多个键的解决方案都必须同时查看 keydown 和 keyup 事件,并跟踪相关键的状态。

      所以代替(伪代码):

      on keydown:
          case left_key:
              object.setMovement(left)
          case forward_key:
              object.setMovement(forward)
      

      相反,您会得到更像(又是伪代码)的东西:

      on keydown:
          case left_key:
              keystates[left] = true
              object.updateMovement(keystates)
          case forward_key:
              keystates[forward] = true
              object.updateMovement(keystates)
      
      on keyup:
          case left_key:
              keystates[left] = false
              object.updateMovement(keystates)
          case forward_key:
              keystates[forward] = false
              object.updateMovement(keystates)
      

      然后updateMovement 例程将查看keystates 并根据所有移动键的状态一起计算出复合移动。

      【讨论】:

        【解决方案5】:

        使用SDL_GetKeyState获取键盘状态

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-03-19
          • 1970-01-01
          • 1970-01-01
          • 2020-04-22
          • 1970-01-01
          • 2021-07-08
          • 2012-08-04
          • 1970-01-01
          相关资源
          最近更新 更多