C # / XNA получить аппаратную позицию мыши

10

Я использую C # и пытаюсь получить аппаратную позицию мыши. Первым делом я попробовал простую функциональность XNA, которая проста в использовании

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

После этого я также делаю рисование мыши и, по сравнению с аппаратной мышью Windows, эта новая мышь с координатами xna "отключается". Под этим я подразумеваю, что он отстает на несколько кадров. Например, если игра работает на скорости 600 к / с, то, конечно, она будет отзывчивой, но при 60 к / с программная задержка мыши больше не будет приемлемой. Поэтому я попытался использовать то, что считал аппаратной мышью,

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

но результат был точно таким же. Я также пытался получить курсор формы Windows, и это тоже тупик - работал, но с той же задержкой. Возиться с функциональностью xna:

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

действительно изменил время задержки по несколько очевидным причинам, но суть в том, что, несмотря на это, он по-прежнему отставал от мыши Windows по умолчанию. Я видел в некоторых играх, что они предоставляют опцию для аппаратной мыши, а в других (я думаю) это уже по умолчанию. Может ли кто-нибудь дать некоторое представление о том, как этого добиться.

разлучать
источник
2
Я не думаю, что Mouse.GetState()сломан. Вы уверены, что положение мыши находится в том же кадре, что и при рисовании? Также помните, что для курсора Windows включено ускорение / замедление, поэтому он выглядит более отзывчивым. Если вы хотите приблизиться к оборудованию, вы должны нажать DirectInput, который даст вам дельты движения вместо абсолютных положений мыши. Но я верю, что Mouse.GetState()это все еще правильный способ сделать это в XNA.
Панда Пижама
Нет, Mouse.GetState()не сломан, и до тех пор, пока IsFixedTimeStep == trueлюди не должны видеть задержку, если не сравнивать непосредственно с видимыми окнами мыши. Однако, установив для IsFixedTimeStep значение false при более низкой частоте кадров, он становится видимым для всех. О непосредственном вводе я считал, что возможность (в крайнем случае).
Сандер
Не уверен, что это то, что вы хотите, но как насчет изменения спрайта курсора, когда он находится в вашем окне, и затем сделать его всегда видимым?
Уильям Маригер
Это именно то, что я делаю, дело в том, что я хочу не только показывать это, но и знать точное положение мыши. Но то, что я получаю, немного отстает от того, что должно быть.
Сандер

Ответы:

17

То, что вы видите, не является входным лагом.

Чтобы получить положение курсора в Windows, вы можете использовать WM_MOUSEMOVEили GetCursorPos. Они оба обеспечивают положение курсора после того, как Windows его обработала, применяя такие вещи, как ускорение. Это позиция курсора, которую использует Windows, в том числе для рисования. И почти всегда то, что вы хотите использовать тоже.

Это то, что использует XNA . В частности GetCursorPos( MSDN ). Это означает, что при вызове Mouse.GetStateвы получаете истинную позицию курсора в том виде, в каком она видится в Windows - с практически нулевой задержкой.

Теперь вы можете использовать WM_INPUT(или DirectInput, хотя это не рекомендуется). Это дает вам необработанные данные указателя. Это происходит до того, как Windows может применить такие вещи, как ускорение, и обычно это не то, что вам нужно.

То, что вы видите, это выходной лаг.

Это из-за « аппаратного курсора ». Это небольшой спрайт, который отображается над экраном графическим процессором в самом конце конвейера. И я имею в виду конец . Он идет после буфера кадра - он вставляется в поток данных при отправке изображения на монитор. Причина, по которой он реагирует так быстро, заключается в том, что его положение задается путем прямой установки регистров GPU. Там нет ожидания вокруг в трубопроводе вообще.

С другой стороны, ваша игра должна проходить через конвейер GPU. Это добавляет множество задержек само по себе. Наиболее проблематичным из них для вас является двойная буферизация - которую (я почти уверен) вы не можете отключить в XNA 4. Вы не рисуете непосредственно в буфер кадра - вы рисуете в буфер. Который представлен каждые ~ 16 мс (при 60FPS). Это означает, что вы просматриваете минимум 16 мс между получением результата Mouse.GetStateполучения чего-либо на основе этого результата на экране. Наверное, дольше. Конечно, намного медленнее, чем аппаратный курсор.

Итак, каковы решения?

Одним из них является использование SetCursor( MSDN ), чтобы изменить изображение, отображаемое аппаратным курсором, на изображение, которое лучше подходит для вашей игры (или просто жить с курсором Windows).

А другой (гораздо проще) просто принять отставание и Game.IsMouseVisible = falseскрыть относительное отставание. Показать свой собственный курсор в игре. Это будет на 16 мс медленнее, чем скрытый курсор Windows - но кого это волнует? Геймеры привыкли к этому. И 16 мс почти визуально незаметны (поэтому мы делаем рендеринг с частотой 60 кадров в секунду).

Эндрю Рассел
источник
Спасибо за подробное объяснение этого вопроса. Скорее всего, я просто буду придерживаться мыши XNA. Будем немного подождать, прежде чем принимать, если у кого-то есть дополнительное решение.
Сандер
Я предполагаю, что согласие с задержкой - это вопрос, на который нужно ответить тестированием.
Зонко