我正在用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::ButtonStateTracker
和 Mouse::ButtonStateTracker
. 因为按钮比较少,所以我明确报告了四种状态。UP
, HELD
, RELEASED
, PRESSED
. 与键盘的信息相同,只是使用起来更方便一些。
请看 GitHub wiki