1

Тема: 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г.              ;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
#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)
	;~ }
 
}

2

Re: AHK:Умная мышь или да здравствует хэдшот!

Под какую игру делался скрипт?

3

Re: AHK:Умная мышь или да здравствует хэдшот!

Dworkin пишет:

Под какую игру делался скрипт?

Непринципиально, под любую.

4

Re: AHK:Умная мышь или да здравствует хэдшот!

Проверить можно в тире. Я проверял в The Evil Within 2. Результат на 2000 больше набил чем без скрипта.