Combinações de teclas na entrada baseada em pesquisa

9

Portanto, suponha que você tenha um sistema de entrada baseado em pesquisas

void update()
{
    if( Keyboard[ 'A' ] )
        // A is down
}

Digamos que você queira reconhecer combinações de teclas de 3 a 8 comprimentos (como baixo, baixo para frente, frente, A para hado-ken)

Como você criaria melhor um sistema de entrada de combinação de teclas genérico (facilmente modificável / programável) na entrada pesquisada ?

bobobobo
fonte

Respostas:

7

Fundamentalmente, você não pode . As combinações de teclas requerem uma ordem, e uma ordem requer eventos.

O que você pode fazer é transformar a pesquisa em eventos comparando os estados-chave de cada quadro e gerando eventos de keyup / keydown post-hoc a partir das diferenças. Não é tão confiável porque você perde o registro de data e hora e outros pedidos intraquadro que os sistemas de eventos "nativos" fornecem, mas permitirá detectar e solicitar pelo menos uma chave por quadro.

Depois de ordenar esses eventos, você pode usar uma máquina de estados finitos ou outra ferramenta para compará-los às suas listas de movimentação e executar a correta.


fonte
É um pouco enganador dizer que você não pode fazer isso sem eventos. Tecnicamente, os eventos são apenas uma maneira de chamar um método ou lista de métodos quando alguma condição é atendida. Nesse sentido, você precisa de eventos. No entanto, posso aplicar a palavra eventos (incorretamente) a muitas tarefas de programação que se encaixam nessa descrição. Eu adicionei uma resposta que resolve o problema da maneira que costumo fazer, que não usa o que a maioria dos programadores costuma considerar "eventos".
Olhovsky 5/05
2
Seu código faz exatamente o que eu disse sem eventos, mas ignora as advertências - o que significa que você perde a ordem intra-frame. Quando você obtém eventos de, por exemplo, o loop de mensagens do Win32, obtém-os em uma ordem com mais granularidade do que um globo por quadro. Quando você calcula os deltas, perde isso. Para um jogo de luta em que os jogadores podem inserir várias partes de um combo em um único quadro, é necessário saber se eles estavam em ordem ou fora de ordem.
Justo. Desde que pesquisemos a 60hz, duvido que desejemos distinguir entre impressoras com menos de 16ms de distância que a pesquisa em 60Hz fornece (supondo que nossos jogadores sejam humanos).
Olhovsky
Nós fazemos. Jogadores de jogos de luta habilidosos podem inserir um comando hadouken / shoryuken stick em menos de três quadros, o que significa que devemos distinguir entre eles.
Então você precisa pesquisar com mais frequência do que 60hz.
Olhovsky
3

Uma maneira é armazenar os estados de entrada atuais e anteriores e compará-los sempre que você pesquisar a entrada.

Para cada tecla que pode ser pressionada, armazene um objeto com um registro de data e hora da última vez em que a tecla mudou de um estado inativo para um estado ativo.

Atualize esses objetos fazendo isso em todas as pesquisas:

void update(){
    some_key_has_been_pressed = false;
    foreach(key in keys){
        if(previous_input[key].Down && current_input[key].Up){
            keys[key].up_timestamp = current_time();
        }

        if(current_input[key].Down){
            keys[key].down_timestamp = current_time();
            some_key_has_been_pressed = true;
        }
    }
}

Agora você pode combinar seus combos com o conteúdo de keys.

Tenha um objeto de combinação para cada combinação e chame update () em cada objeto de combinação em cada pesquisa.

O método update () de um objeto de combinação corresponderá ao padrão, verificando se todas as condições necessárias para o combo são satisfeitas nesta enquete. Ou seja, todos os timestamps de teclas do combo até o momento estão em ordem e nenhuma outra tecla que interrompa o combo foi pressionada nesse quadro. Para cada condição atendida, aumente um contador no objeto combo para a próxima condição a ser verificada. Quando todas as condições forem atendidas em um combo, chame o método que o combo deve executar. Se some_key_has_been_pressed == truea tecla que é a próxima condição do combo não tiver sido pressionada, redefina o contador de condições satisfeitas do combo para 0.

O acima é o meu método preferido, pois é simples de implementar, fácil de manter, eficiente e altamente modular.

No entanto, para outro bom método, consulte a amostra da sequência de entrada XNA , escrita em C #, e a lógica provavelmente pode ser transferida para o idioma que você está usando.

Olhovsky
fonte
Na segunda instrução if, if(current_input[key].down){você também não gostaria de verificar se a chave estava no previous_inputestado? Como está escrito, acho que atualizaria continuamente uma chave retida down_timestamp.
Webdevkit 19/08/12
Atualizar continuamente o registro de data e hora inativo é o comportamento pretendido. Isso permite comparar o up_timestamp e o down_timestamp de qualquer chave, para verificar quanto tempo durou. Se você mantiver pressionada uma tecla continuamente, não estará alterando o up_timestamp, portanto (down_timestamp - up_timestamp) ainda fornecerá a duração pela qual ela foi mantida.
Olhovsky
obrigado pelo esclarecimento. Eu estava pensando o oposto, onde up_timestamp - down_timestamp é a duração de uma tecla em espera.
Webdevkit 19/08/12
1

Faça uma pilha dos últimos eventos da tecla X. Para cada evento da tecla, adicione um objeto com a tecla e a hora do pressionamento / liberação e verifique se os últimos membros da pilha correspondem a um padrão especial e se essas teclas foram pressionadas rápido o suficiente para conte como uma combinação. Por fim, remova o objeto mais antigo da pilha.

aaaaaaaaaaaa
fonte
2
Uma pilha que você pode remover o objeto do fundo de não é realmente uma pilha;)
Olhovsky
Um grupo ordenado de entidades de dados, uma lista pode ser o termo correto.
Aaaaaaaaaaaa
deque;) O (1) enq / deq ftw.
Michael.bartnett 5/05
3
Uma sequência ordenada em que os objetos são colocados em uma extremidade e removidos da outra é mais adequadamente chamada de fila .
11
Você pode criar um buffer de anel (BufferLength> = comando mais longo) e gravar chaves nele (Position = Number MOD BufferLength). Sem adição / remoção será obrigado a todos
Kromster