1

Тема: AHK: Замедление мыши

Мне было интересно найти код который помог бы мне в процессе игры облегчить прицеливание- а именно во время прицеливания снижать чувствительность мыши. Кто-то возразит мне что зачем изобретать велосипед, если на игровых девайсах уже предусмотрена возможность изменять DPI? Кому как удобно.
Для себя я придумал "внешний" способ переключения DPI, а именно-удерживая какую-то конкретную клавишу клавиатуры я уменьшаю чувствительность мыши, при отпускании клавиши чувствительность восстанавливается. Искал решения по этому вопросу на форуме, что-то похожее было:


F1::
SPI_GETMOUSESPEED = 0x70
SPI_SETMOUSESPEED = 0x71
; Retrieve the current speed so that it can be restored later:
DllCall("SystemParametersInfo", UInt, SPI_GETMOUSESPEED, UInt, 0, UIntP, OrigMouseSpeed, UInt, 0)
; Now set the mouse to the slower speed specified in the next-to-last parameter (the range is 1-20, 10 is default):
DllCall("SystemParametersInfo", UInt, SPI_SETMOUSESPEED, UInt, 0, UInt, 3, UInt, 0)
KeyWait F1 ; This prevents keyboard auto-repeat from doing the DllCall repeatedly.
return

F1 up::DllCall("SystemParametersInfo", UInt, 0x71, UInt, 0, UInt, OrigMouseSpeed, UInt, 0) ; Restore the original speed.
return

Но проблема в том что данный код не работает в играх!
Мое решение состояло в том чтобы по таймеру через период времени компенсировать 50% движения мыши (если мышь сдвинулась за период на 10 пикселей, то скрипт пусть вернет половину-5 пикселей назад. Таким способом я снижаю чувствительность вдвое).
Проще всего было бы использовать таймер, но оказалось что таймер не дает требуемую частоту опроса, так при заявленном 5 мс -
SetTimer, s1, 5 на деле таймер дает 15,6 мс. Это легко увидеть:


SetBatchLines -1
#Persistent
SetTimer, s1, 5 

s1:
++DR
if (DR=100)
{
stop:=A_TickCount,DR:=0
dT:=(stop-start)/100
start:=A_TickCount
ToolTip, продолжительность периода=%dT%  
}
Return 


С такой низкой частотой мой код работал плохо, мышь перемещалась скачкообразно. Чтобы сделать эти скачки невидимыми глазу нужно увеличить частоту опроса. Оказывается есть способ снизить время периода но при этом нужно прибегнуть к DLL и отказаться от таймера.


SetBatchLines -1  ; Ensures maximum effectiveness of this method.

SleepDuration = 1  ; This can sometimes be finely adjusted (e.g. 2 is different than 3) depending on the value below.
TimePeriod = 3 ; Try 7 or 3.  See comment below.
; On a PC whose sleep duration normally rounds up to 15.6 ms, try TimePeriod=7 to allow
; somewhat shorter sleeps, and try TimePeriod=3 or less to allow the shortest possible sleeps.

DllCall("Winmm\timeBeginPeriod", uint, TimePeriod)  ; Affects all applications, not just this script's DllCall("Sleep"...), but does not affect SetTimer.
Iterations = 50
StartTime := A_TickCount

Loop %Iterations%
    DllCall("Sleep", UInt, SleepDuration)  ; Must use DllCall instead of the Sleep command.

DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)  ; Should be called to restore system to normal.
MsgBox % "Sleep duration = " . (A_TickCount - StartTime) / Iterations

Вот что у меня получилось:


SetBatchLines -1
SleepDuration:=1,TimePeriod:=3 
DllCall("Winmm\timeBeginPeriod", uint, TimePeriod)
Loop
{ 
DllCall("Sleep", UInt, SleepDuration)  
Gosub, s1
}

s1:
MouseGetPos, X, Y
if GetKeyState("n","P")
{
   If (ABS(OldX-X)>=2)
   dx:=Floor((OldX-X)/2)
   else
   dx:=0
   
   If (ABS(OldY-Y)>=2)
   dy:=Floor((OldY-Y)/2)
   else
   dy:=0
   
   if (dx or dy)
   DllCall("mouse_event", uint, 1, int, dx, int, dy, uint, 0, int, 0)
} 
MouseGetPos, OldX, OldY
return

*n::Return 

#Esc::
DllCall("Winmm\timeEndPeriod", UInt, TimePeriod)
ExitApp  

При таком способе мне удалось снизить длительность периода до 0,9-1 мс. При нажатии клавиши N курсор замедляется и это справедливо не только в Windows но и в играх. Скачки практически незаметны. Я доволен, может кому-то тоже пригодится:).

2

Re: AHK: Замедление мыши

Если я правильно понял, то при нажатии определенной клавиши, включается специальный режим, который после того, как вы сместите мышку например на 10 прикселей вправо, сместит после этого её на половину (5 пикселей) влево? С таким скриптом легко бан получить в играх.

Когда вы говорите что не можете сделать, вам всего-лишь не хватает фантазии придумать какой-нибудь костыль.

3 (изменено: stealzy, 2017-08-13 22:31:28)

Re: AHK: Замедление мыши

Проще всего было бы использовать таймер, но оказалось что таймер не дает требуемую частоту опроса, так при заявленном 5 мс -
SetTimer, s1, 5 на деле таймер дает 15,6 мс.

Встроенные команды языка Sleep и таймеры всегда кратны 10-15 мс промежутку и не могут быть меньше.
Вот так придумали с таймерами, и еще одна альтернатива Sleep:

+ Sub 10ms Sleep
SetBatchLines -1
sle := new pSleep
st := A_TickCount
Loop 100
	sle.do(5)
MsgBox % A_TickCount - st

class pSleep
{
	static F:=pSleep.getQPF(), res:=pSleep.getTimerResolution()
	do(t) {
		crit:=A_IsCritical
		Critical On
		DllCall("QueryPerformanceCounter", "Int64P", sT1)
		res:=this.res, dt:=0
		; Do regular sleep before qpc loop, if t is long enough.
		if (t>res)
			(DllCall("Sleep", "Int", t-res), DllCall("QueryPerformanceCounter", "Int64P", sT2), dt:=(sT2-sT1)*1000/this.F)
		t-=dt, DllCall("QueryPerformanceCounter", "Int64P", loopStart), loopTic:=loopStart
		while (loopTic-loopStart<t*this.F/1000)
			DllCall("QueryPerformanceCounter", "Int64P", loopTic)
		Critical % crit
	}
	getTimerResolution() {
		DllCall("ntdll.dll\NtQueryTimerResolution", "UPtr*", MinimumResolution, "UPtr*", MaximumResolution, "UPtr*", CurrentResolution)
		return Ceil(CurrentResolution/10000) ; Resolutions are reported as 100-nanoseconds
	}
	getQPF() {
		DllCall("QueryPerformanceFrequency", "Int64P", F)
		return F
	}
}

4

Re: AHK: Замедление мыши

stealzy, ShorterSleep

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

5

Re: AHK: Замедление мыши

Это явно сложнее.

stealzy пишет:

Э, видимо из контекста неясно, что я говорю про встроенные команды языка.

А, я просто не понял, к чему, в том примере встроенный Sleep и не используется.

Разработка AHK-скриптов:
e-mail dfiveg@mail.ru
Telegram jollycoder

6

Re: AHK: Замедление мыши

stealzy
Настолько сложно, что напоминает алжирский. Зачем вообще были придуманы классы? Чем они лучше простых функций?
Как вы к ним пришли? И что меня, простого смертного отделяет от этого понимания?

7 (изменено: stealzy, 2017-08-14 00:01:38)

Re: AHK: Замедление мыши

sabir.yanin2014
Класс не мой, и в нем фишки класса практически и не используются, единственное — при инициализации класса вычисляются статические переменные, с ф-ией они вычисляются лишь при первом вызове, что займет время, что в данном случае неприемлемо.

Если спрашиваете вообще за классы, то к ним приходят, когда становится тесно в функциях.
Пример: пул ф-ий, которые делят м/у собой общие данные. Делать эти данные глобальными - неудобно, передавать из ф-ии в ф-ию в параметрах - тоже неудобно, не всегда возможно. И вообще, когда ф-ий становится несколько десятков, их приходится организовывать.
Функции при этом никуда не деваются, потому что классы состоят из ф-ий и данных.

8

Re: AHK: Замедление мыши

stealzy, спасибо. Может подскажите где можно почитать, чтобы более-менее доступным языком было написано про классы?

9

Re: AHK: Замедление мыши

На офф. форуме есть темы про объяснение классов для чайников.
В оригинальной справке (которая в меню пуск) написано тоже, поиском найдете.

10

Re: AHK: Замедление мыши

Вот класс выкладывал тут тоже.

11 (изменено: sabir.yanin2014, 2017-08-30 02:55:23)

Re: AHK: Замедление мыши

По основной теме (замедление мыши) значительно продвинулся. За счет использования нового подхода добился улучшения общего качества работы скрипта (снизилась нагрузка на процессор, увеличилась плавность движения мыши -таймер 500 мкс). Играл по сети в DOOM, никаких проблем не возникало. Приятно теперь играть - в режиме прицеливания мышь двигается мягко, без скачков.


#SingleInstance force
#Persistent
#include CLR.ahk
asm := CLR_LoadLibrary("WaitableTimer\WaitableTimer.dll")

wt1 := asm.CreateInstance("WaitableTimer")
MyTimer1 := wt1.Create(Func("Test"), 1)
MyTimer1.Start() ;создаем первый таймер -1мс

loop 
{
++ZX
	if (ZX=1200) ;выдержка 500 мкс (кол-во циклов находим опытным путем)
	{
	ZX:=0
	Break   
	}
}	
wt2 := asm.CreateInstance("WaitableTimer")
MyTimer2 := wt2.Create(Func("Test"), 1)
MyTimer2.Start() ;через 500 микросекунд создаем второй таймер на 1 мс
return

Test(){ ;на выходе получаем частоту обращения в два раза выше(период 500 микросекунд вместо 1мс)
Gosub, s1
}
 
s1:
MouseGetPos, X, Y
if Joy7
{
   if (ABS(OldX-X)>=2)
   dx:=2*(OldX-X)//3
   else
   dx:=0
   
   if (ABS(OldY-Y)>=2)
   dy:=2*(OldY-Y)//3
   else
   dy:=0
   
   if (dx or dy)
   DllCall("mouse_event", uint, 1, int, dx, int, dy, uint, 0, int, 0)
} 
MouseGetPos, OldX, OldY
Return
Post's attachments

Папка_с_Кодом.rar 5.78 kb, 11 downloads since 2017-08-30 

You don't have the permssions to download the attachments of this post.

12 (изменено: stealzy, 2017-09-02 20:33:59)

Re: AHK: Замедление мыши

Можно использовать хук для той же цели:

SetBatchLines, -1
SetMouseDelay, -1
OnExit, Exit

hHookMouse := DllCall("SetWindowsHookEx" . (A_IsUnicode ? "W" : "A")
	, Int, WH_MOUSE_LL := 14
	, Ptr, RegisterCallback("LowLevelMouseProc", "Fast")
	, Ptr, DllCall("GetModuleHandle", UInt, 0, Ptr)
	, UInt, 0, Ptr)
Return

Esc::
Exit:
	DllCall("UnhookWindowsHookEx", Ptr, hHookMouse)
	ExitApp

; F12::MouseMove(20, 10)

LowLevelMouseProc(nCode, wParam, lParam)
{
	static x_prev, y_prev, stop
	Critical
	CoordMode Mouse, Screen

	if (nCode < 0 || wParam != 0x200 || stop)   ; WM_MOUSEMOVE = 0x200
		Return DllCall("CallNextHookEx", Ptr, 0, Int, nCode, UInt, wParam, UInt, lParam)

	x := NumGet(lParam+0, 0), y := NumGet(lParam+0, 4)

	y_prev := (y_prev = "") ? y : y_prev
	x_prev := (x_prev = "") ? x : x_prev

	MouseMove, Round((-x+x_prev)/2), Round((-y+y_prev)/2), 0, R

	; If (abs(x-x_prev)>140 || abs(y-y_prev)>140) { ; ненормальные смещения
	; 	stop := true
	; 	MouseGetPos _x, _y
	; 	OutputDebug % x-x_prev " x=" x ", " y-y_prev " y=" y ". "  x " " y " - " _x " " _y
	; 	ExitApp
	; }
	; Else
		; MouseMove(Round((-x+x_prev)/2), Round((-y+y_prev)/2))

	x_prev := x, y_prev := y
	Return 1
}

MouseMove(x, y) {
	DllCall("mouse_event", UInt, MOUSEEVENTF_MOVE:=0x1, Int, x, Int, y, UInt, 0, Int, 0)
}

Но с перемещением мыши посредством DllCall mouse_event проблема — при резком движении хук выдает неверные координаты и перемещение идет вразнос.
Кто понимает в чем причина?