Trap mouse no SDL

12

Como posso prender o mouse do usuário dentro do meu jogo? Não é um jogo em tela cheia, mas quero que o usuário não possa arrastar o mouse para fora da tela.

Preciso verificar as coordenadas do mouse e do SDL_WarpCursor para dizer dentro da janela (manualmente) ou existe uma maneira automática?

Obrigado.

David Gomes
fonte
Você já pensou em esconder o cursor e enviá-lo de volta para o centro? Em seguida, basta desenhar a mira no centro da tela, no lugar do cursor do SO.
Vaughan Hilts

Respostas:

24

Embora você possa implementar isso usando SDL_WarpCursor (), eu tive problemas com esse método em algumas plataformas. Eu tive problemas reais com algumas plataformas que não executam com confiabilidade a ação WarpCursor (), principalmente quando a chamo de todos os quadros.

Além disso, lembre-se de que em muitas plataformas, o cursor é tratado com uma frequência mais alta que o seu aplicativo. Isto é particularmente verdade se você estiver executando abaixo de 60fps! Portanto, mesmo que a plataforma entorte com sucesso o mouse em todos os quadros do seu jogo, o cursor ainda poderá sair da sua janela se estiver recebendo atualizações entre os quadros renderizados.

O método correto para interceptar o mouse no SDL é:

SDL_WM_GrabInput( SDL_GRAB_ON );

Isso informa à SDL que sua intenção é realmente obter o controle total do mouse + teclado (consulte a documentação ) e, portanto, manter o mouse dentro da janela, não importa o quê. A partir deste momento, o mouse não se moverá para fora da janela, independentemente da sua taxa de quadros, e você não deve ligar para SDL_WarpCursor, exceto se realmente pretende teleportar o cursor para algum lugar, em vez de enviá-lo continuamente.

Enquanto estiver no modo "agarrar", você continuará recebendo eventos de movimento do mouse como se o mouse não estivesse sendo restringido dentro da janela. (Portanto, se o cursor estiver na borda direita da tela e o usuário mover o mouse mais para a direita, você receberá um evento de movimento do mouse mostrando o movimento para a direita, embora a posição do cursor não seja alterada). Isso é realmente útil para (por exemplo) girar controles para jogos de FPS, onde você se importa apenas com o movimento relativo do cursor, não com sua posição absoluta.

Atualização para SDL2 - 9 de dezembro de 2013

Essa interface foi alterada para SDL2. Se você estiver usando o SDL1.2, a resposta acima ainda está correta. No entanto, no SDL2, a SDL_WM_GrabInput(SDL_GrabMode)função não está mais disponível. A nova maneira de capturar o mouse do SDL2 é:

SDL_SetRelativeMouseMode(SDL_TRUE);

Caso contrário, isso é idêntico à função anterior - nesse modo, você continuará recebendo SDL_MOUSEMOTIONeventos como se o mouse não estivesse sendo restringido dentro da janela.

Trevor Powell
fonte
1
Advertência: notei que em algumas plataformas, recebo imediatamente um evento espúrio do MouseMotion com um movimento muito grande, imediatamente após ligar o mouse. Para lidar com isso, meu código ignora o primeiro evento de movimento do mouse recebido após ativar este modo.
Trevor Powell
Isso funcionou perfeitamente e é muito mais fácil do que o modo manual. Obrigado!
David Gomes
3

Embora a resposta atualizada de Trevor reflita o estado atual do SDL2, você também pode prender o mouse criando o seu SDL_Windowcom o SDL_WINDOW_INPUT_GRABBEDconjunto. Esteja ciente de que encontrei alguns problemas estranhos no Linux (provavelmente devido a alguma peculiaridade da janela do X), onde você nunca pode sair da janela a menos que SDL_QUIT seja chamado (mesmo que outro evento do sistema operacional roube o foco).

Justin Skiles
fonte
Obrigado, SDL_WINDOW_INPUT_GRABBED funciona perfeitamente no meu código SDL2.
Adrian Maire 14/05
3

Para o SDL2, também parece haver a função SDL_SetWindowGrab, se você deseja habilitá-lo e desabilitá-lo.

SDL_SetWindowGrab(SDL_Window* window, SDL_bool grabbed)
Michael Shaffer
fonte
2

Você pode usar o SDL Motion Event para detectar quando o mouse se move. Em seguida, você pode chamar WarpCursor dentro disso, mesmo depois de disparar um evento para canalizar os valores relX e relY. Você também pode ocultar o cursor do SO e desenhar uma mira ou algo semelhante no centro da tela. Isso garante que o mouse pareça estar sempre no meio para o usuário e você ainda poderá receber valores de movimento e similares.

Vaughan Hilts
fonte