SDL2需要多个密钥粘性控制,难以防止早期密钥错误解释为新命令

时间:2017-01-20 01:38:11

标签: c++ sdl keyboard-events sdl-2

简而言之,我无法实现非错误的方向键盘控件(这些键的上,下,左,右和两键组合),因此按下其中一个组合(或单键)具有粘滞键效果。也就是说,向上+向左应该会导致游戏对象停留在向上+向左的位置。

不幸的是,这不是正在发生的事情。 如果我点击其中一个双键组合,那么程序通常会错误地解释将两个键中的一个作为按键之间的小延迟。所以向下+向右可能被解释为(向下+向右,从未对错向右)。

我在例如Processing中遇到了同样的问题,并且在某种程度上它发生了它是有意义的。如果您将箭头键视为虚拟d-pad,那么如何区分向上+向右和向上+向右,但立即切换到向上?所需的逻辑并不完全清楚。

我已经查看了stackoverflow和官方SDL论坛的解决方案,但似乎没有什么能帮助我解决这个特殊情况。这是我能找到的最接近的问题:A way to make keyboard event queue both responsive and not take whole CPU power

我尝试过的一些“解决方案”包括:检查下一次迭代是否保持相同的键(换句话说,浪费一个帧......这几乎可以工作但并非总是如此,而且它似乎是一种hacky方式go)或每4次迭代只检查键盘输入。这些都不会产生很好的结果。

请注意,在我的程序中,向上/向下,向左/向右,向上+向左/向下+向右,向上+向右/向下 - 向左被视为相同的命令,因此有四个选项。

主循环省略渲染逻辑:

while (is_running)
{
    const Uint8 up = key_states[SDL_SCANCODE_UP];
    const Uint8 down = key_states[SDL_SCANCODE_DOWN];
    const Uint8 left = key_states[SDL_SCANCODE_LEFT];
    const Uint8 right = key_states[SDL_SCANCODE_RIGHT];

    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                is_running = false;
                break;
        }
    }

    if ((up || down) && (!left && !right))
    {
        if (prev != 0) // to avoid printing the same position repeatedly
        {
            puts("UP/DOWN");
            prev = 0;
        }   
    }
    else if ((left || right) && (!up && !down))
    {
        if (prev != 1)
        {
            puts("LEFT/RIGHT");
            prev = 1;
        }
    }
    else if ((up && left) || (down && right))
    {
        if (prev != 2)
        {
            puts("UP-LEFT/DOWN-RIGHT");
            prev = 2;
        }
    }
    else if ((up && right) || (down && left))
    {
        if (prev != 3)
        {
            puts("UP-RIGHT/DOWN-LEFT");
            prev = 3;
        }
    }
}

(另请注意:我打算使用bit掩码而不是上面看到的许多布尔检查,但这与问题无关。)

我设置程序将方向输出到控制台。

Output:

UP/DOWN
LEFT/RIGHT
UP/DOWN
UP-RIGHT/DOWN-LEFT // worked here
UP-LEFT/DOWN-RIGHT // worked here
UP-RIGHT/DOWN-LEFT // did not work here, reverted to LEFT/RIGHT
LEFT/RIGHT // unexpected
UP-LEFT/DOWN-RIGHT // also did not work here, reverted to LEFT/RIGHT
LEFT/RIGHT // unexpected

结果似乎是随机的,奇怪的是,如果我将案件分为上,下,左,右,上+右,下+右,下+左,上+左,情况会更糟。

我最好的猜测?我需要某种计时机制和/或使用事件系统的解决方案。

我认为我有点死路一条。我可以帮忙解决这个问题吗?这是妨碍我完成游戏演示的主要问题。

非常感谢。

编辑:我认为最大的挑战是区分从对角线切换到垂直或水平而不放弃所有按键以及释放一个按键与另一个按键之间的自然轻微延迟只需点击一个组合键即可。

编辑2:这个主要是(一些例外)有效,除了我不能在不释放所有键的情况下从对角线转换为正交。如果不改变方向而不首先释放所有键,则可能无法获得完全平滑的版本。不过,我认为这可以通过游戏控制器实现......它绝对是在实践中完成的。为什么不用键盘?

while (is_running)
{       
    /*
    const Uint8 up = (key_states[SDL_SCANCODE_UP] == 0) ? 0 : UP;
    const Uint8 down = (key_states[SDL_SCANCODE_DOWN] == 0) ? 0 : DOWN;
    const Uint8 left = (key_states[SDL_SCANCODE_LEFT] == 0) ? 0 : LEFT;
    const Uint8 right = (key_states[SDL_SCANCODE_RIGHT] == 0) ? 0 : RIGHT;
    */



    bool _u = false;
    bool _d = false;
    bool _l = false;
    bool _r = false;

    Uint8 key_down = 0;
    bool single_key_down = true;
    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                is_running = false;
                break;
            case SDL_KEYDOWN:
                    switch (event.key.keysym.scancode)
                    {
                        case SDL_SCANCODE_UP:
                            _u = true;
                            break;
                        case SDL_SCANCODE_DOWN:
                            _d = true;
                            break;
                        case SDL_SCANCODE_LEFT:
                            _l = true;
                            break;
                        case SDL_SCANCODE_RIGHT:
                            _r = true;
                            break;
                    }
        }
    }

    const Uint8 up = key_states[SDL_SCANCODE_UP];
    const Uint8 down = key_states[SDL_SCANCODE_DOWN];
    const Uint8 left = key_states[SDL_SCANCODE_LEFT];
    const Uint8 right = key_states[SDL_SCANCODE_RIGHT];

    if ((_u && _l) || (_d && _r) 
        || (up && left) || (down && right))
    {
        if (prev != 2)
        {
            puts("UP-LEFT/DOWN-RIGHT");
            prev = 2;

        }
        which_index = 2;

    }
    else if ((_u && _r) || (_d && _l) 
             || (up && right) || (down && left))
    {
        if (prev != 3)
        {
            puts("UP-RIGHT/DOWN-LEFT");
            prev = 3;
        }
        which_index = 3;
    }
    else if (_u || _d)
    {
        puts("UP/DOWN");
        which_index = 0;
    }
    else if (_r || _l)
    {
        puts("LEFT/RIGHT");
        which_index = 1;
    }
}

编辑3:

较少的无缝旋转(没有保持像d-pad-ness),但手指的压力较小,因为你可以轻拍。这似乎没有任何缺陷:

while (is_running)
{       
    /*
    const Uint8 up = (key_states[SDL_SCANCODE_UP] == 0) ? 0 : UP;
    const Uint8 down = (key_states[SDL_SCANCODE_DOWN] == 0) ? 0 : DOWN;
    const Uint8 left = (key_states[SDL_SCANCODE_LEFT] == 0) ? 0 : LEFT;
    const Uint8 right = (key_states[SDL_SCANCODE_RIGHT] == 0) ? 0 : RIGHT;
    */



    bool _u = false;
    bool _d = false;
    bool _l = false;
    bool _r = false;

    Uint8 key_down = 0;
    bool single_key_down = true;
    SDL_Event event;
    while (SDL_PollEvent(&event))
    {
        switch (event.type)
        {
            case SDL_QUIT:
                is_running = false;
                break;
            case SDL_KEYDOWN:
                    switch (event.key.keysym.scancode)
                    {
                        case SDL_SCANCODE_UP:
                            _u = true;
                            break;
                        case SDL_SCANCODE_DOWN:
                            _d = true;
                            break;
                        case SDL_SCANCODE_LEFT:
                            _l = true;
                            break;
                        case SDL_SCANCODE_RIGHT:
                            _r = true;
                            break;
                    }
        }
    }

    const Uint8 up    = key_states[SDL_SCANCODE_UP];
    const Uint8 down  = key_states[SDL_SCANCODE_DOWN];
    const Uint8 left  = key_states[SDL_SCANCODE_LEFT];
    const Uint8 right = key_states[SDL_SCANCODE_RIGHT];
    bool none_held = false;
    if (up || down || left || right)
    {
        keys_reset = true;
    }
    else
    {
        none_held = true;
    }

    if (!none_held && keys_reset)
    {
        if ((_u && _l) || (_d && _r) 
            || (up && left) || (down && right))
        {
            if (prev != 2)
            {
                puts("UP-LEFT/DOWN-RIGHT");
                prev = 2;
            }
            which_index = 2;
            keys_reset = false;
        }
        else if ((_u && _r) || (_d && _l) 
                 || (up && right) || (down && left))
        {
            if (prev != 3)
            {
                puts("UP-RIGHT/DOWN-LEFT");
                prev = 3;
            }
            which_index = 3;
            keys_reset = false;
        }
        else if (keys_reset)
        { 
            if (_u || _d)
            {
                if (prev != 0)
                {
                    puts("UP/DOWN");
                    prev = 0;
                }
                which_index = 0;
                keys_reset = false;
            }
            else if (_r || _l)
            {
                if (prev != 1)
                {
                    puts("LEFT/RIGHT");
                    prev = 1;
                }
                which_index = 1;
                keys_reset = false;
            }
        }
    }
}

1 个答案:

答案 0 :(得分:1)

你永远不会同时发布两把钥匙;在处理第二个密钥发布之前,总会有一些延迟会导致主轮询循环出现间隙。

你必须调整你的逻辑来处理这个问题。可能不允许在不释放所有键的情况下从多键组合(UP-RIGHT)切换到单键组合(UP)。或者完全摆脱多键组合并使用另一个单键(Home,Page Up等)。

您还需要考虑如果用户按下3或4个按键会发生什么情况,无论是在转换期间还是暂时按下。