Тема: AHK:Умная мышь или да здравствует хэдшот!
Известно что для более точного прицеливания нужно устанавливать меньшее DPI. С другой стороны при низком DPI перемещение курсора затратное(трудное) и становится сложно попадать по движущимся целям так как тупо не успеваешь за целью. Я постарался для себя игровой процесс сделать комфортным и за одно улучшить свои игровые достижения. Данный скрипт позволяет модифицировать мышь и сделать управление более гибким и удобным(эффективным). Что мне не нравилось в базовом интерфейсе(и что я устранил для себя):
1)Во время прицеливания чувствительность камеры в играх меняется слабо, не так как хотелось бы (претензии к разработчикам игр).
2)Первый пункт хотелось бы решить аппаратным способом, а именно изменяя настройки мыши(было бы хорошо если бы при нажатии ПКМ или даже клавиатуры чувствительность мыши снижалась требуемым образом -но таких мышей я не видел/претензии к разработчикам мышей).
3)Даже если бы и был возможен второй пункт то снижение чувствительности не решает проблему точного прицеливания так как в играх нужно стрелять по движущимся целям. А это сложно при низкой скорости мыши.Парадокс.
Наблюдая за собой я увидел что прицеливание я осуществляю в два этапа: 1)Приблизительное(быстрое) прицеливание;2)"Доцеливание"-точное прицеливание. Во время первого этапа мышь перемещается по коврику значительно -изменяя свое положение. Во время второго этапа мышь замирает и либо смещается незначительно либо вообще не двигается когда ловишь прицел с помощью WASD. Тут меня и осенило: что если скрипт сам будет проводить статистику поведения мыши и будет сам вычислять второй этап-для которого автоматически будет выбрано низкое DPI. Так мой код изменяет базовое значение DPI=800 на 400 во время прицеливания и позволяет ускорить мышь (DPI=800) при стрельбе по быстрым целям. Алгоритм следующий: в режиме прицеливания если мышь я смещаю слабо (DPI=400), если я ускоряю мышь выше заданного порога (хочу догнать цель) мышь ускоряется (DPI=800).После того как я обгоняю цель(упреждение) наступает второй этап -мышь замирает(DPI снова снижается до 400).Все -завершаю прицеливание и стреляю.
На англоязычном сайте нашел код который корректно измеряет движения мыши в играх.Он фиксирует пакеты которые формирует мышь и которые в итоге смещают камеру (MouseGetPos в играх работает неправильно).Немного доработал его.
Здесь нужно обратить внимание: что при использовании скрипта частоту опроса мыши нужно выставлять на минимум: так для моей мыши по умолчанию стоит 1000Гц -пакеты получаются маленькие, я выставил 125Гц -скрипт начал работать.
Код разбит на две части основной файл и подгружаемый "MouseDelta.ahk" который нужно расположить в директории основного.
Основной:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; --Умная мышь-- ;;;
;;; EasyHard, 29/11/2017г. ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SMX:=OldSMX:=SMY:=OldSMY:=DSX:=DSY:=0
#SingleInstance force
#Persistent
#include MouseDelta.ahk
md:=new MouseDelta("MouseEvent").Start()
md.SetState(0)
SetTimer, SpeedMouse, 100
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; Вычисление скорости перемещения физической мыши ;;;
;;; /автоматический выбор режима(авт.смена DPI) ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
SpeedMouse:
DSX:=abs(OldSMX-SMX),OldSMX:=SMX,DSY:=abs(OldSMY-SMY),OldSMY:=SMY
if (DSX>30 or DSY>30) ;настраиваемые параметры-можно поэкспериментировать
Focus:=0 ;"быстрая мышь"-"режим-упреждение"-DPI базовый(настройка чувств.мыши/у меня 800DPI)
else
Focus:=1 ;"точная мышь"-"режим-прицеливание"-измененный DPI(800/2=400)
Return
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; ПКМ-Включение режима прицеливания ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
*RButton::md.SetState(1)
*RButton up::md.SetState(0)
End::ExitApp
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; Мышь-Регулирование скорости обзора ;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
MouseEvent(MouseID, x := 0, y := 0){
global SMX,SMY,Focus
if (MouseID)
{
SMX+=x,SMY+=y
if Focus
{
ShiftX:=x//2,ShiftY:=y//2
DllCall("mouse_event",uint,1,int,-ShiftX,int,-ShiftY,uint,0,int,0)
}
}
}
MouseDelta.ahk:
; Instantiate this class and pass it a func name or a Function Object
; The specified function will be called with the delta move for the X and Y axes
; Normally, there is no windows message "mouse stopped", so one is simulated.
; After 10ms of no mouse movement, the callback is called with 0 for X and Y
Class MouseDelta {
State := 0
__New(callback){
;~ this.TimeoutFn := this.TimeoutFunc.Bind(this)
this.MouseMovedFn := this.MouseMoved.Bind(this)
this.Callback := callback
}
Start(){
static DevSize := 8 + A_PtrSize, RIDEV_INPUTSINK := 0x00000100
; Register mouse for WM_INPUT messages.
VarSetCapacity(RAWINPUTDEVICE, DevSize)
NumPut(1, RAWINPUTDEVICE, 0, "UShort")
NumPut(2, RAWINPUTDEVICE, 2, "UShort")
NumPut(RIDEV_INPUTSINK, RAWINPUTDEVICE, 4, "Uint")
; WM_INPUT needs a hwnd to route to, so get the hwnd of the AHK Gui.
; It doesn't matter if the GUI is showing, it still exists
Gui +hwndhwnd
NumPut(hwnd, RAWINPUTDEVICE, 8, "Uint")
this.RAWINPUTDEVICE := RAWINPUTDEVICE
DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
OnMessage(0x00FF, this.MouseMovedFn)
this.State := 1
return this ; allow chaining
}
Stop(){
static RIDEV_REMOVE := 0x00000001
static DevSize := 8 + A_PtrSize
OnMessage(0x00FF, this.MouseMovedFn, 0)
RAWINPUTDEVICE := this.RAWINPUTDEVICE
NumPut(RIDEV_REMOVE, RAWINPUTDEVICE, 4, "Uint")
DllCall("RegisterRawInputDevices", "Ptr", &RAWINPUTDEVICE, "UInt", 1, "UInt", DevSize )
this.State := 0
return this ; allow chaining
}
SetState(state){
if (state && !this.State)
this.Start()
else if (!state && this.State)
this.Stop()
return this ; allow chaining
}
Delete(){
this.Stop()
;~ this.TimeoutFn := ""
this.MouseMovedFn := ""
}
; Called when the mouse moved.
; Messages tend to contain small (+/- 1) movements, and happen frequently (~20ms)
MouseMoved(wParam, lParam){
Critical
; RawInput statics
static DeviceSize := 2 * A_PtrSize, iSize := 0, sz := 0, pcbSize:=8+2*A_PtrSize, offsets := {x: (20+A_PtrSize*2), y: (24+A_PtrSize*2)}, uRawInput
static axes := {x: 1, y: 2}
; Get hDevice from RAWINPUTHEADER to identify which mouse this data came from
VarSetCapacity(header, pcbSize, 0)
If (!DllCall("GetRawInputData", "UPtr", lParam, "uint", 0x10000005, "UPtr", &header, "Uint*", pcbSize, "Uint", pcbSize) or ErrorLevel)
Return 0
ThisMouse := NumGet(header, 8, "UPtr")
; Find size of rawinput data - only needs to be run the first time.
if (!iSize){
r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", 0, "UInt*", iSize, "UInt", 8 + (A_PtrSize * 2))
VarSetCapacity(uRawInput, iSize)
}
sz := iSize ; param gets overwritten with # of bytes output, so preserve iSize
; Get RawInput data
r := DllCall("GetRawInputData", "UInt", lParam, "UInt", 0x10000003, "Ptr", &uRawInput, "UInt*", sz, "UInt", 8 + (A_PtrSize * 2))
x := 0, y := 0 ; Ensure we always report a number for an axis. Needed?
x := NumGet(&uRawInput, offsets.x, "Int")
y := NumGet(&uRawInput, offsets.y, "Int")
this.Callback.(ThisMouse, x, y)
;~ ; There is no message for "Stopped", so simulate one
;~ fn := this.TimeoutFn
;~ SetTimer, % fn, -50
}
;~ TimeoutFunc(){
;~ this.Callback.("", 0, 0)
;~ }
}