在地图上使用 WM_KEYDOWN 和 WM_KEYUP 时出现延迟

问题描述 投票:0回答:1

我将 WM_KEYDOWN 和 WM_KEYUP 与地图结合使用来处理按键事件,但当我同时按下太多按键时,它会产生滞后。

下面是我用来处理此事件的代码。

std::map<Key, KeyStatus> StatusKey;

void UpdateKeyEvent(const MSG& msg) noexcept
{
    switch (msg.message)
    {
    case WM_KEYDOWN:
        SetStatusKey(msg.wParam);
        return;

    case WM_KEYUP:
        ReUpStatusKey(msg.wParam);
        return;
    }
}
void SetStatusKey(const WPARAM& wParam) noexcept
{
    Key key = static_cast<Key>(wParam);
    if (StatusKey.find(key) == StatusKey.end()) {
        StatusKey.insert(std::make_pair(key, Down));
        return;
    }
    else {
        if (StatusKey[key] == NONE_GET || StatusKey[key] == Up) {
            StatusKey[key] = Down;
        }
    }
}
void UpdateStatusKey() noexcept
{
    for (auto& Key : StatusKey)
    {
        if (Key.second == Down)
        {
            Key.second = Hold;
        }
    }
}
void ReUpStatusKey(const WPARAM& wParam) noexcept
{
    Key key = static_cast<Key>(wParam);
    StatusKey[key] = Up;
}
float GetKeyRaw(const Key& key) noexcept
{
    if (StatusKey.find(key) != StatusKey.end()) {
        if (StatusKey[key] != Up && StatusKey[key] != NONE_GET) {
            return 1.0f;
        }
    }
    return 0.0f;
}

在主循环中,我像这样使用 UpdateKeyEvent

while (TRUE)
{
    if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE))
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
        EventMessage::UpdateKeyEvent(Msg);
        if (Msg.message == WM_QUIT)
            break;
    }
}

简而言之,我当前使用 Windows 消息循环实现的键盘输入处理可能有太多缺陷。我希望得到大家关于如何优化我的输入处理代码的建议。谢谢你!

PS: 我尝试使用 GetKeyState、GetAsyncKeyState 和 GetKeyboardState,但它们的功能与我上面编码的方式并不完全相同。

c++ winapi
1个回答
0
投票

想法#1

您的 SetStatusKey 似乎正在进行三次查找。一次使用

find
调用,然后使用
StatusKey[key] ==
比较进行两次额外查找。

更改此:

void SetStatusKey(const WPARAM& wParam) noexcept
{
    Key key = static_cast<Key>(wParam);
    if (StatusKey.find(key) == StatusKey.end()) {
        StatusKey.insert(std::make_pair(key, Down));
        return;
    }
    else {
        if (StatusKey[key] == NONE_GET || StatusKey[key] == Up) {
            StatusKey[key] = Down;
        }
    }
}

对此:

void SetStatusKey(const WPARAM& wParam) noexcept
{
    Key key = static_cast<Key>(wParam);
    auto itor = StatusKey.find(key);
    KeyStatus status = itor == StatusKey.end() ? NONE_GET : itor->second;
    if (status == NONE_GET || status == Up)
    {
        StatusKey[key] = Down;
    }
    
}

想法#2

停止使用

std::map
并使用
std::unordered_map
代替。 O(lg N) 与 O(1) 性能权衡。

想法#3

只需使用数组即可。

KeyStatus StatusKey[256] = {};

在代码中的某个位置,您可以在主循环开始之前将数组初始化为所有

Up
NONE_GET
值。那么您不需要使用 .find() 或任何其他方法。您可以像这样直接访问数组:

BYTE keyIndex = (BYTE)wParam;
StatusKey status = StatusKey[keyIndex];
if (status == NONE_GET || status == Up)
{
    StatusKey[key] = Down;
}

想法#4。与 #3 类似,但不要使用 WM_KEYDOWN 或 WM_KEYUP 消息来保持按键状态更新。只需在游戏循环的每次迭代中使用

GetKeyboardState
函数轮询整个键盘状态即可。很久以前,当我在游戏工作室工作时,这就是我们用来在每一帧上轮询键盘的方法。

© www.soinside.com 2019 - 2024. All rights reserved.