C # / XNA obtiene la posición del mouse de hardware

10

Estoy usando C # e intento obtener la posición del mouse de hardware. Lo primero que probé fue la funcionalidad XNA simple que es fácil de usar

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

Después de eso, también hago el dibujo del mouse, y en comparación con el mouse de hardware de Windows, este nuevo mouse con coordenadas proporcionadas por xna se está "aflojando". Con eso quiero decir, que está detrás de unos pocos cuadros. Por ejemplo, si el juego se ejecuta a 600 fps, por supuesto, responderá, pero a 60 fps el retraso del mouse por software ya no es aceptable. Por lo tanto, intenté usar lo que pensé que era un mouse de hardware,

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

pero el resultado fue exactamente el mismo. También intenté obtener el cursor de formulario de Windows, y eso también fue un callejón sin salida, pero funcionó, pero con el mismo retraso. Jugando con la funcionalidad xna:

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

cambió el tiempo de retraso por razones algo obvias, pero la conclusión es que, independientemente de que todavía estaba detrás del mouse predeterminado de Windows. He visto en algunos juegos, que ofrecen opciones para el mouse acelerado por hardware, y en otros (creo) ya es por defecto. ¿Alguien puede dar alguna pista sobre cómo lograr eso?

Romper
fuente
2
No creo que Mouse.GetState()esté roto. ¿Está seguro de que está obteniendo la posición del mouse en el mismo marco que lo está dibujando? Recuerde también que el cursor de Windows tiene habilitada la aceleración / desaceleración, por lo que se siente más sensible. Si desea acercarse al hardware, debe presionar DirectInput, que le dará deltas de movimiento en lugar de posiciones absolutas del mouse. Pero creo que Mouse.GetState()sigue siendo la forma correcta de hacerlo en XNA.
Panda Pyjama
No, Mouse.GetState()no está roto, y mientras las IsFixedTimeStep == truepersonas no puedan ver el retraso a menos que se comparen directamente con el mouse de Windows visible. Sin embargo, si se establece IsFixedTimeStep en falso a una velocidad de fotogramas más baja, será visible para todos. Acerca de la entrada directa, lo consideré una posibilidad (último recurso).
Sunder
No estoy seguro de si es lo que quieres, pero ¿qué hay de cambiar el sprite del cursor mientras está en tu ventana y luego hacerlo siempre visible?
William Mariager
Eso es exactamente lo que estoy haciendo, lo que pasa es que no solo quiero mostrarlo, sino también saber la posición exacta del mouse. Pero lo que estoy obteniendo está un poco atrasado de lo que debería ser.
Sunder

Respuestas:

17

Lo que está viendo no es un retraso de entrada.

Para obtener la posición del cursor en Windows puede usar WM_MOUSEMOVEo GetCursorPos. Ambos proporcionan la posición del cursor después de que Windows lo haya procesado, aplicando cosas como la aceleración. Esta es la posición del cursor que usa Windows, incluso para dibujar. Y casi siempre lo que quieres usar también.

Esto es lo que usa XNA . Específicamente GetCursorPos( MSDN ). Esto significa que, cuando llama Mouse.GetState, obtiene la posición real del cursor tal como lo ve Windows, con prácticamente cero retraso.

Ahora, podría usar WM_INPUT(o DirectInput, aunque eso está en desuso). Esto le proporciona los datos de puntero sin formato. Esto es antes de que Windows pueda aplicar cosas como la aceleración, y generalmente no es lo que desea.

Lo que está viendo es el retraso de salida.

Esto se debe al " cursor de hardware ". Se trata de un pequeño duende que se ve arrastrado por la pantalla por la GPU en el mismo final de su tubería. Y me refiero al final . Viene después del búfer de trama: se inserta en el flujo de datos a medida que la imagen se envía al monitor. La razón por la que reacciona tan rápido es porque su posición se establece configurando directamente los registros de la GPU. No hay que esperar en la tubería en absoluto.

Tu juego, por otro lado, tiene que pasar por la tubería de GPU. Esto agrega muchos retrasos por sí solo. El más problemático de estos para usted es el doble búfer, que (estoy bastante seguro) no puede desactivar en XNA 4. No dibuja directamente en el búfer de cuadros, sino en el backbuffer. Que se presenta cada ~ 16 ms (a 60 FPS). Eso significa que está viendo un mínimo de aproximadamente 16 ms entre recibir el resultado de Mouse.GetStateobtener algo basado en ese resultado en la pantalla. Probablemente por más tiempo. Ciertamente mucho más lento que el cursor de hardware.

Entonces, ¿cuáles son las soluciones?

Una es usar SetCursor( MSDN ) para cambiar la imagen que se muestra con el cursor de hardware, para obtener una imagen que se adapte mejor a su juego (o simplemente vivir con el cursor de Windows).

Y el otro (mucho más fácil) es aceptar el retraso y configurarlo Game.IsMouseVisible = falsepara ocultar el retraso relativo. Muestra tu propio cursor en el juego. Será 16 ms más lento que el cursor oculto de Windows, pero ¿a quién le importa? Los jugadores están acostumbrados. Y 16ms es casi visualmente imperceptible de todos modos (es por eso que renderizamos las cosas a 60FPS).

Andrew Russell
fuente
Gracias por una explicación detallada del asunto. Lo más probable es que me quede con el mouse XNA. Esperar un poco antes de aceptar en caso de que alguien tenga alguna solución adicional para ello.
Sunder
Supongo que aceptar el retraso o no es una pregunta que debería responderse mediante pruebas de juego.
Zonko