WM_KEYUP反复调用保持键。

我正在用DirectX TK创建一个flappy bird克隆。输入时,我使用空格键。接收输入的代码是

 case WM_KEYUP:
        Keyboard::ProcessMessage(message, wParam, lParam);
        OutputDebugStringA("Up");
        break;

我以为WM_KEYUP应该只在按键释放时调用一次,当你只想接收一个按键的输入时,它就会很有用;但是,当我运行这段代码时,每按一次键,它都会打印 “Up “至少两次,如果我按住空格键,它就会重复调用,直到我释放它。如果我按住空格键,它就会重复调用它,直到我释放它。我应该如何解决这个问题?谢谢你!下面是回调的代码。

下面是回调函数的代码。

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static bool s_in_sizemove = false;
    static bool s_in_suspend = false;
    static bool s_minimized = false;
    static bool s_fullscreen = false;
    // TODO: Set s_fullscreen to true if defaulting to fullscreen.

    auto game = reinterpret_cast<Game*>(GetWindowLongPtr(hWnd, GWLP_USERDATA));

    switch (message)
    {
    case WM_PAINT:
        if (s_in_sizemove && game)
        {
            game->Tick();
        }
        else
        {
            PAINTSTRUCT ps;
            (void)BeginPaint(hWnd, &ps);
            EndPaint(hWnd, &ps);
        }
        break;

    case WM_SIZE:
        if (wParam == SIZE_MINIMIZED)
        {
            if (!s_minimized)
            {
                s_minimized = true;
                if (!s_in_suspend && game)
                    game->OnSuspending();
                s_in_suspend = true;
            }
        }
        else if (s_minimized)
        {
            s_minimized = false;
            if (s_in_suspend && game)
                game->OnResuming();
            s_in_suspend = false;
        }
        else if (!s_in_sizemove && game)
        {
            game->OnWindowSizeChanged(LOWORD(lParam), HIWORD(lParam));
        }
        break;

    case WM_ENTERSIZEMOVE:
        s_in_sizemove = true;
        break;

    case WM_EXITSIZEMOVE:
        s_in_sizemove = false;
        if (game)
        {
            RECT rc;
            GetClientRect(hWnd, &rc);

            game->OnWindowSizeChanged(rc.right - rc.left, rc.bottom - rc.top);
        }
        break;

    case WM_GETMINMAXINFO:
        if (lParam)
        {
            auto info = reinterpret_cast<MINMAXINFO*>(lParam);
            info->ptMinTrackSize.x = 320;
            info->ptMinTrackSize.y = 200;
        }
        break;

    case WM_ACTIVATEAPP:
        if (game)
        {
            if (wParam)
            {
                game->OnActivated();
            }
            else
            {
                game->OnDeactivated();
            }
        }
        Keyboard::ProcessMessage(message, wParam, lParam);
        Mouse::ProcessMessage(message, wParam, lParam);
        break;

    case WM_POWERBROADCAST:
        switch (wParam)
        {
        case PBT_APMQUERYSUSPEND:
            if (!s_in_suspend && game)
                game->OnSuspending();
            s_in_suspend = true;
            return TRUE;

        case PBT_APMRESUMESUSPEND:
            if (!s_minimized)
            {
                if (s_in_suspend && game)
                    game->OnResuming();
                s_in_suspend = false;
            }
            return TRUE;
        }
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_SYSKEYDOWN:
        if (wParam == VK_RETURN && (lParam & 0x60000000) == 0x20000000)
        {
            // Implements the classic ALT+ENTER fullscreen toggle
            if (s_fullscreen)
            {
                SetWindowLongPtr(hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
                SetWindowLongPtr(hWnd, GWL_EXSTYLE, 0);

                int width = 800;
                int height = 600;
                if (game)
                    game->GetDefaultSize(width, height);

                ShowWindow(hWnd, SW_SHOWNORMAL);

                SetWindowPos(hWnd, HWND_TOP, 0, 0, width, height, SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
            }
            else
            {
                SetWindowLongPtr(hWnd, GWL_STYLE, 0);
                SetWindowLongPtr(hWnd, GWL_EXSTYLE, WS_EX_TOPMOST);

                SetWindowPos(hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED);

                ShowWindow(hWnd, SW_SHOWMAXIMIZED);
            }

            s_fullscreen = !s_fullscreen;
        }
        break;

    case WM_MENUCHAR:
        // A menu is active and the user presses a key that does not correspond
        // to any mnemonic or accelerator key. Ignore so we don't produce an error beep.
        return MAKELRESULT(0, MNC_CLOSE);
    case WM_INPUT:
    case WM_MOUSEMOVE:
    case WM_LBUTTONDOWN:
    case WM_LBUTTONUP:
    case WM_RBUTTONDOWN:
    case WM_RBUTTONUP:
    case WM_MBUTTONDOWN:
    case WM_MBUTTONUP:
        Mouse::ProcessMessage(message, wParam, lParam);
        break;
    case WM_MOUSEWHEEL:
    case WM_XBUTTONDOWN:
    case WM_XBUTTONUP:
    case WM_MOUSEHOVER:
        Mouse::ProcessMessage(message, wParam, lParam);
        break;
    case WM_KEYDOWN: 
    case WM_KEYUP:
        Keyboard::ProcessMessage(message, wParam, lParam);
        break;
    case WM_SYSKEYUP:
        Keyboard::ProcessMessage(message, wParam, lParam);
        break;
    }

    return DefWindowProc(hWnd, message, wParam, lParam);
}

解决方案:

这个 DirectX工具箱 Keyboard 类是把键盘当作一个 “控制器”,所以你可以通过使用 GetState: 所有开启的位子都是键向下,所有关闭的位子都是键向上。

如果你想让一个事件只发生在 过渡 这就是各种 “按键跟踪器 “类的作用。

std::unique_ptr<DirectX::Keyboard>          m_keyboard;
DirectX::Keyboard::KeyboardStateTracker     m_keyboardButtons;
m_keyboard = std::make_unique<Keyboard>();
void Game::Update(DX::StepTimer const&)
{
    float elapsedTime = float(timer.GetElapsedSeconds());

    auto kb = m_keyboard->GetState();
    m_keyboardButtons.Update(kb);

    // These controls are scaled by framerate, so we just want to use them 'raw'
    if (kb.A || kb.D)
    {
        m_yaw += (kb.D ? 0.1f : -0.1f);
    }

    if (kb.W || kb.S)
    {
        m_pitch += (kb.W ? 0.1f : -0.1f);
    }

    if (kb.Home)
    {
        m_yaw = m_pitch = 0.f;
    }

    // Later yaw/pitch are used scaled by elapsedTime 

    // These keys we want 'debounced'
    if (m_keyboardButtons.IsKeyPressed(Keyboard::Q))
    {
        m_usedInstanceCount = std::max(c_minInstanceCount, m_usedInstanceCount - 1000);
    }
    else if (m_keyboardButtons.IsKeyPressed(Keyboard::E))
    {
        m_usedInstanceCount = std::min(c_maxInstances, m_usedInstanceCount + 1000);
    }

    if (m_keyboardButtons.IsKeyPressed(Keyboard::Space))
    {
        Fire();
    }
}

键盘跟踪器会告诉你 “刚刚按下的键” 和 “刚刚释放的键” 原始状态会告诉你 “按键向下 “和 “按键向上”。

同样的原理也适用于 GamePad::ButtonStateTrackerMouse::ButtonStateTracker. 因为按钮比较少,所以我明确报告了四种状态。UP, HELD, RELEASED, PRESSED. 与键盘的信息相同,只是使用起来更方便一些。

请看 GitHub wiki

本文来自投稿,不代表实战宝典立场,如若转载,请注明出处:https://www.shizhanbaodian.com/4755.html

(0)
上一篇 2022年6月1日 上午2:00
下一篇 2022年6月1日 上午2:30

相关推荐

发表评论

登录后才能评论