C # / XNA obtém a posição do mouse de hardware

10

Estou usando C # e tentando obter a posição do mouse de hardware. A primeira coisa que tentei foi a simples funcionalidade XNA, simples de usar

Vector2 position = new Vector2(Mouse.GetState().X, Mouse.GetState().Y);

Depois disso, eu também faço o desenho do mouse e, comparando com o mouse de hardware do Windows, esse novo mouse com coordenadas fornecidas pelo xna está "relaxando". Com isso, quero dizer, que está atrasado em alguns quadros. Por exemplo, se o jogo estiver rodando a 600 fps, a maldição será responsiva, mas a 60 fps o atraso do mouse do software não será mais aceitável. Por isso, tentei usar o que eu pensava ser um mouse de hardware,

[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool GetCursorPos(out POINT lpPoint);

mas o resultado foi exatamente o mesmo. Também tentei obter o cursor de formulário do Windows, e esse também era um beco sem saída, mas com o mesmo atraso. Brincando com a funcionalidade xna:

GraphicsDeviceManager.SynchronizeWithVerticalRetrace = true/false
Game.IsFixedTimeStep = true/fale

alterou o tempo de atraso por razões um tanto óbvias, mas a linha inferior é que, independentemente disso, ainda estava atrás do mouse padrão do Windows. Eu já vi em alguns jogos, eles fornecem opção para mouse acelerado por hardware, e em outros (acho) já é por padrão. Alguém pode dar alguma pista sobre como conseguir isso.

Sunder
fonte
2
Eu não acho que Mouse.GetState()está quebrado. Você tem certeza de que está posicionando o mouse no mesmo quadro que o desenha? Lembre-se também de que o cursor do Windows tem a aceleração / desaceleração ativada, para que ele se sinta mais responsivo. Se você quiser se aproximar do hardware, pressione DirectInput, que fornecerá deltas de movimento em vez de posições absolutas do mouse. Mas acredito que Mouse.GetState()ainda é a maneira correta de fazer isso no XNA.
Panda Pyjama
Não, Mouse.GetState()não está quebrado e desde que as IsFixedTimeStep == truepessoas não consigam ver atraso, a menos que comparadas diretamente com o mouse visível do Windows. No entanto, definir IsFixedTimeStep como false na taxa de quadros inferior torna-se visível para todos. Sobre a entrada direta, considerei uma possibilidade (último recurso).
Sunder
Não tenho certeza se é o que você deseja, mas que tal alterar o sprite do cursor enquanto ele está na sua janela e torná-lo sempre visível?
William Mariager
É exatamente o que estou fazendo, o fato é que eu não quero apenas mostrar isso, mas também saber a posição exata do mouse. Mas o que estou recebendo está um pouco atrás do que deveria ser.
Sunder

Respostas:

16

O que você está vendo não é atraso de entrada.

Para obter a posição do cursor no Windows, você pode usar WM_MOUSEMOVEou GetCursorPos. Ambos fornecem a posição do cursor depois que o Windows o processou, aplicando coisas como aceleração. Esta é a posição do cursor que o Windows usa, inclusive para desenhar. E quase sempre o que você deseja usar também.

É isso que o XNA usa . Especificamente GetCursorPos( MSDN ). Isso significa que, quando você liga Mouse.GetState, está obtendo a verdadeira posição do cursor como o Windows vê - com praticamente zero atraso.

Agora, você pode usar WM_INPUT(ou DirectInput, embora isso seja preterido). Isso fornece os dados brutos do ponteiro. Isso ocorre antes que o Windows possa aplicar coisas como aceleração e geralmente não é o que você deseja.

O que você está vendo é atraso na saída.

Isso ocorre por causa do " cursor de hardware ". Este é um pequeno sprite que é atraída sobre a tela pela GPU no mesmo fim de seu pipeline. E eu quero dizer o fim . Ele vem após o buffer de quadro - é inserido no fluxo de dados à medida que a imagem está sendo enviada ao monitor. A razão pela qual ele reage tão rapidamente é porque sua posição é definida configurando diretamente os registros da GPU. Não há nenhuma espera no pipeline.

Seu jogo, por outro lado, precisa passar pelo pipeline da GPU. Isso adiciona muitos atrasos por conta própria. O mais problemático para você é o buffer duplo - o qual (tenho certeza) não pode ser desativado no XNA 4. Você não desenha diretamente no buffer do quadro - você desenha no buffer traseiro. Que é apresentado a cada ~ 16ms (a 60FPS). Isso significa que você está olhando no mínimo cerca de 16ms entre receber o resultado de Mouse.GetStateobter algo com base nesse resultado na tela. Provavelmente mais. Certamente muito mais lento que o cursor do hardware.

Então, quais são as soluções?

Uma é usar SetCursor( MSDN ) para alterar a imagem exibida pelo cursor do hardware, para uma imagem que melhor se adapte ao seu jogo (ou simplesmente viva com o cursor do Windows).

E o outro (muito mais fácil) é apenas aceitar o atraso e definir Game.IsMouseVisible = falsepara ocultar o atraso relativo. Exiba seu próprio cursor no jogo. Será 16ms mais lento que o cursor oculto do Windows - mas quem se importa? Os jogadores estão acostumados a isso. E 16ms é quase visualmente imperceptível de qualquer maneira (é por isso que renderizamos as coisas a 60FPS).

Andrew Russell
fonte
Obrigado pela explicação detalhada do assunto. Provavelmente vou ficar com o mouse XNA. Vai esperar um pouco antes de aceitar, caso alguém tenha alguma solução adicional para isso.
Sunder
Eu acho que aceitar o atraso ou não é uma pergunta que deve ser respondida pelo playtesting.
Zonko