- PVSM.RU - https://www.pvsm.ru -
Многие из нас проводили вечера перед теплым ламповым телевизором с друзьями, играя на приставках. Всегда особенно приятно было играть одновременно вдвоем, не ожидая своей очереди.
Ностальгия.
К сожалению, в настоящее время многие разработчики игр не добавляют подобного режима — новые игры со splitscreen можно пересчитать чуть ли не на пальцах одной руки. В один весенний вечер пришла идея в голову попробовать обойти ограничение, накладываемое разработчиками, и сделать игровой режим с разделением экрана более доступным.
В качестве подопытного кролика игры мною была выбрана «World of Tanks» по нескольким причинам:
1. Возможность играть вдвоем, данный режим называется "Взвод [1]".
2. Игра на минимальных настройках достаточна не требовательна — любой средний по производительности ПК должен тянуть 2 копии.
3. Геймплей танков достаточно незатейлив, хотя разработчиками и позиционируется как «массовая многопользовательская онлайн-игра в жанре action с элементами ролевой игры, шутера и стратегии» (Wikipedia [2]). Но как по мне — аркада для пыщь-пыщь.
4. Наверное, самая основная причина — я, по несколько часов в неделю, и мой младший сын любим пострелять. Со старшим мы иногда бегаем в Portal2, там режим разделенного экрана для ТВ есть.
Эти ребята надеюсь многим знакомы ;)
Более детальное изучение всех составляющих для танков вдвоем на ТВ привело к следующему:
1. Настроить клиент игры для возможного запуска двух копий
2. Необходимо разделить экран телевизора на два виртуальных.
3. Решить проблему отправки нажатий кнопок/отклонения стиков с геймпада в неактивное окно.
4. Отправить вибрацию [3] в разные геймпады с разных клиентов.
1. Запуск 2-х клиентов.
По-умолчанию, разработчики из Wargaming убрали возможность одновременного запуска двух копий. Не буду описывать все прелести «песочницы» — Sandboxie [4] Вам в помощь.
2. Разделение экрана телевизора на две части.
«WoT» в оконном режиме может иметь минимальное разрешение 1024х768, в случае разделения FullHD телевизора пополам, необходимо разрешение каждого окна минимум 960х1080, а учитывая рамки окна и заголовок и того меньше. Т.е. стандартными «горячими клавишами» через Snap [5] разнеся окна в разные стороны мы получаем частичное перекрытие окон. Любые другие утилиты для разделения рабочего стола на две части используют похожий функционал и никаким образом не могут повлиять на минимальное разрешения игры по ширине.
Перепробовав огромное количество, натолкнулся на Virtual Display Manager [6], подкупило отсутствие в названии слова desktop.
Утилита сделала нужное — добавив конфигурацию двух виртуальных дисплеев и перемещая окно в нужный — игра принимает нужное нам значение, а именно занимает ровно половину экрана. Кстати надо проверить разделение на большее количество.
3. Отправка нажатий клавиш в неактивное окно.
Это решение было для моего ума самым сложным. Два клиента запущены, окна разнесены в стороны и не перекрывают друг-дружку, но одно из окон активно, соответственно принимает нажатия кнопок и перемещения мышки, а вот второе не активно со всеми вытекающими.
Разрешение 1366х768.
К решению этой проблемы меня подтолкнуло знакомство с AutoHotkey [7]. Вот уж поистине «AutoHotkey — это свободная утилита под Windows с открытыми исходными кодами и скриптовый язык с огромными возможностями, в принципе даже не требующий установки.» (ссылка [8])
Причины, почему скрипт срабатывал, мне так и остались неизвестны.
После многих безуспешных попыток, решение нашлось. Через SendMessage сообщать окну, что оно активно и отправлять нажатия клавиш. Такой своеобразный обман.
Дальше пошло веселее, особенно хочу выразить благодарность Серому форуму [9], и отдельно модератору teadrinker. Спасибо!!!
Прошу конструктивно критиковать код совершенству нет предела.
;;;;;;;;;;;; убираем загловок окон
^!+s::
WinWait, WoT Client
WinSet, Style, -0xC00000
WinWait, [#] WoT Client [#]
WinSet, Style, -0xC00000
return
;;;;;;;;;;;; первый геймпад движение вперед/назад в неактивном окне и зум
WatchAxisFirstJoyMoveForwardAndZoom:
GetKeyState, 1JoyY, 1JoyY
GetKeyState, 1JoyZ, 1JoyZ
GetKeyState, 1Joy2, 1Joy2
GetKeyState, 1Joy3, 1Joy3
FirstJoyMoveForwardAndZoomPrev = %FirstJoyMoveForwardAndZoom%
if 1Joy2 = D
GoSub, FirstJoyConsumables
else if 1Joy3 = D
GoSub, FirstJoyConsumables
else
{
if 1JoyZ > 70
{
if 1JoyY < 30
FirstJoyMoveForwardAndZoom = PgDn
else if 1JoyY > 70
FirstJoyMoveForwardAndZoom = PgUp
else
FirstJoyMoveForwardAndZoom =
}
else if 1JoyY < 30
FirstJoyMoveForwardAndZoom = vk57
else if 1JoyY > 70
FirstJoyMoveForwardAndZoom = vk53
else
FirstJoyMoveForwardAndZoom =
}
if FirstJoyMoveForwardAndZoom = %FirstJoyMoveForwardAndZoomPrev%
return
SetKeyDelay -1
if FirstJoyMoveForwardAndZoom
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyMoveForwardAndZoom% down}, WoT Client
}
}
if FirstJoyMoveForwardAndZoomPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyMoveForwardAndZoomPrev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад движение влево/вправо в неактивном окне
WatchAxisFirstJoyMoveRotate:
GetKeyState, 1JoyX, 1JoyX
GetKeyState, 1Joy2, 1Joy2
GetKeyState, 1Joy3, 1Joy3
FirstJoyMoveRotatePrev = %FirstJoyMoveRotate%
if 1Joy2 = D
GoSub, SecondJoyConsumables
else if 1Joy3 = D
GoSub, SecondJoyConsumables
else
{
if 1JoyX > 80
FirstJoyMoveRotate = vk44
else if 1JoyX < 20
FirstJoyMoveRotate = vk41
else
FirstJoyMoveRotate =
}
if FirstJoyMoveRotate = %FirstJoyMoveRotatePrev%
return
SetKeyDelay -1
if FirstJoyMoveRotate
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyMoveRotate% down}, WoT Client
}
}
if FirstJoyMoveRotatePrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyMoveRotatePrev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад меню расходников в неактивном окне
FirstJoyConsumables:
FirstJoyConsumablesPrev = %FirstJoyConsumables%
if 1JoyX < 20
{
if 1JoyY < 20
FirstJoyConsumables = vk38
else if 1JoyY between 40 and 60
FirstJoyConsumables = vk37
else if 1JoyY > 80
FirstJoyConsumables = vk36
else FirstJoyConsumables =
}
else if 1JoyX between 40 and 60
{
if 1JoyY < 10
FirstJoyConsumables = vk31
else if 1JoyY > 90
FirstJoyConsumables = vk35
else FirstJoyConsumables =
}
else if 1JoyX > 80
{
if 1JoyY < 20
FirstJoyConsumables = vk32
else if 1JoyY between 40 and 60
FirstJoyConsumables = vk33
else if 1JoyY > 80
FirstJoyConsumables = vk34
else FirstJoyConsumables =
}
else FirstJoyConsumables =
if FirstJoyConsumables = %SFirstJoyConsumablesPrev%
return
SetKeyDelay -1
if FirstJoyConsumables
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyConsumables% down}, WoT Client
}
}
if FirstJoyConsumablesPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyConsumablesPrev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад обзор влево/вправо в неактивном окне
WatchAxisFirstJoyCameraRotateVert:
GetKeyState, 1JoyU, 1JoyU
GetKeyState, 1Joy5, 1Joy5
FirstJoyCameraRotateVertPrev = %FirstJoyCameraRotateVert%
if 1Joy5 = D
GoSub, FirstJoyCommandMenu
else
{
if 1JoyU > 70
FirstJoyCameraRotateVert = Right
else if 1JoyU < 30
FirstJoyCameraRotateVert = Left
else
FirstJoyCameraRotateVert =
}
if FirstJoyCameraRotateVert = %FirstJoyCameraRotateVertPrev%
return
SetKeyDelay -1
if FirstJoyCameraRotateVert
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyCameraRotateVert% down}, WoT Client
}
}
if FirstJoyCameraRotateVertPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyCameraRotateVertPrev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад обзор вверх/вниз в неактивном окне
WatchAxisFirstJoyCameraRotateHoriz:
GetKeyState, 1JoyR, 1JoyR
GetKeyState, 1Joy5, 1Joy5
FirstJoyCameraRotateHorizPrev = %FirstJoyCameraRotateHoriz%
if 1Joy5 = D
GoSub, FirstJoyCommandMenu
else
{
if 1JoyR > 70
FirstJoyCameraRotateHoriz = Down
else if 1JoyR < 30
FirstJoyCameraRotateHoriz = Up
else
FirstJoyCameraRotateHoriz =
}
if FirstJoyCameraRotateHoriz = %FirstJoyCameraRotateHorizPrev%
return
SetKeyDelay -1
if FirstJoyCameraRotateHoriz
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyCameraRotateHoriz% down}, WoT Client
}
}
if FirstJoyCameraRotateHorizPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyCameraRotateHorizPrev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад меню приказов
FirstJoyCommandMenu:
FirstJoyCommandMenuPrev = %FirstJoyCommandMenu%
if 1JoyU < 20
{
if 1JoyR < 20
FirstJoyCommandMenu = Numpad8
else if 1JoyR between 40 and 60
FirstJoyCommandMenu = Numpad7
else if 1JoyR > 80
FirstJoyCommandMenu = Numpad6
else FirstJoyCommandMenu =
}
else if 1JoyU between 40 and 60
{
if 1JoyR < 10
FirstJoyCommandMenu = vk54
else if 1JoyR > 90
FirstJoyCommandMenu = Numpad5
else FirstJoyCommandMenu =
}
else if 1JoyU > 80
{
if 1JoyR < 20
FirstJoyCommandMenu = Numpad2
else if 1JoyR between 40 and 60
FirstJoyCommandMenu = Numpad3
else if 1JoyR > 80
FirstJoyCommandMenu = Numpad4
else FirstJoyCommandMenu =
}
else FirstJoyCommandMenu =
if FirstJoyCommandMenu = %FirstJoyCommandMenuPrev%
return
SetKeyDelay -1
if FirstJoyCommandMenu
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyCommandMenu% down}, WoT Client
}
}
if FirstJoyCommandMenuPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyCommandMenuPrev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад выстрел в неактивном окне
WatchAxisFirstJoyShoot:
GetKeyState, 1JoyZ, 1JoyZ
FirstJoyShootPrev = %FirstJoyShoot%
if 1JoyZ < 30
FirstJoyShoot = LButton
else
FirstJoyShoot =
if FirstJoyShoot = %FirstJoyShootPrev%
return
SetKeyDelay -1
if FirstJoyShoot
{
IfWinNotActive, WoT Client
{
SendMessage, 0x201,,,, WoT Client
}
}
if FirstJoyShootPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x202,,,, WoT Client
}
}
return
;;;;;;;;;;;;; первый геймпад крестовина в неактивном окне
WatchFirstJoyPOV:
GetKeyState, 1JoyPOV, 1JoyPOV
FirstJoyPOVPrev = %FirstJoyPOV%
if 1JoyPOV = 0
FirstJoyPOV = vk52
else if 1JoyPOV = 18000
FirstJoyPOV = vk46
else if 1JoyPOV = 27000
FirstJoyPOV = vk58
else if 1JoyPOV = 9000
FirstJoyPOV = vk43
else FirstJoyPOV =
if FirstJoyPOV = %FirstJoyPOVPrev%
return
SetKeyDelay -1
if FirstJoyPOV
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyPOV% down}, WoT Client
}
}
if FirstJoyPOVPrev
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {%FirstJoyPOVprev% up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад LShift в неактивном окне
1Joy10::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vkA0 Down}, WoT Client
KeyWait, 1Joy10
ControlSend,, {vkA0 Up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад Space в неактивном окне
1Joy9::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk20 Down}, WoT Client
KeyWait, 1Joy9
ControlSend,, {vk20 Up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад выбор снарядов в неактивном окне
1Joy1::
Gosub, FirstSubToggle
Return
FirstSubToggle:
FirstToggle++
If FirstToggle = 1
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk31 down}, WoT Client
Sleep, 10
ControlSend,, {vk31 up}, WoT Client
Sleep, 10
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk31 down}, WoT Client
Sleep, 10
ControlSend,, {vk31 up}, WoT Client
}
}
If FirstToggle = 2
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk32 down}, WoT Client
Sleep, 10
ControlSend,, {vk32 up}, WoT Client
Sleep, 10
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk32 down}, WoT Client
Sleep, 10
ControlSend,, {vk32 up}, WoT Client
}
}
If FirstToggle = 3
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk33 down}, WoT Client
Sleep, 10
ControlSend,, {vk33 up}, WoT Client
Sleep, 10
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk33 down}, WoT Client
Sleep, 10
ControlSend,, {vk33 up}, WoT Client
}
FirstToggle = 0
}
return
;;;;;;;;;;;; первый геймпад огнетушитель в неактивном окне
1Joy4::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk35 Down}, WoT Client
KeyWait, 1Joy4
ControlSend,, {vk35 Up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад автоприцел в неактивном окне
1Joy6::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x204, 1,,, WoT Client
KeyWait, 1Joy6
SendMessage, 0x205, 1,,, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад меню в неактивном окне
1Joy8::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk1B Down}, WoT Client
KeyWait, 1Joy8
ControlSend,, {vk1B Up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад скрыть мини карту в неактивном окне
1Joy7::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk4D Down}, WoT Client
KeyWait, 1Joy7
ControlSend,, {vk4D Up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад ремонт в неактивном окне
1Joy3::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk34 Down}, WoT Client
KeyWait, 1Joy3
ControlSend,, {vk34 Up}, WoT Client
}
}
return
;;;;;;;;;;;; первый геймпад лечение в неактивном окне
1Joy2::
{
IfWinNotActive, WoT Client
{
SendMessage, 0x06, 1,,, WoT Client
ControlSend,, {vk36 Down}, WoT Client
KeyWait, 1Joy2
ControlSend,, {vk36 Up}, WoT Client
}
}
return
;;;;;;;;;;;; второй геймпад
;;;;;;;;;;;; второй геймпад движение вперед/назад в активном окне и зум
WatchAxisSecondJoyMoveForwardAndZoom:
GetKeyState, 2JoyY, 2JoyY
GetKeyState, 2JoyZ, 2JoyZ
GetKeyState, 2Joy2, 2Joy2
GetKeyState, 2Joy3, 2Joy3
SecondJoyMoveForwardAndZoomPrev = %SecondJoyMoveForwardAndZoom%
if 2Joy2 = D
GoSub, SecondJoyConsumables
else if 2Joy3 = D
GoSub, SecondJoyConsumables
else
{
if 2JoyZ > 70
{
if 2JoyY < 30
SecondJoyMoveForwardAndZoom = PgDn
else if 2JoyY > 70
SecondJoyMoveForwardAndZoom = PgUp
else
SecondJoyMoveForwardAndZoom =
}
else if 2JoyY < 30
SecondJoyMoveForwardAndZoom = vk57
else if 2JoyY > 70
SecondJoyMoveForwardAndZoom = vk53
else
SecondJoyMoveForwardAndZoom =
}
if SecondJoyMoveForwardAndZoom = %SecondJoyMoveForwardAndZoomPrev%
return
SetKeyDelay -1
if SecondJoyMoveForwardAndZoom
{
ControlSend,, {%SecondJoyMoveForwardAndZoom% down}, [#] WoT Client [#]
}
if SecondJoyMoveForwardAndZoomPrev
{
ControlSend,, {%SecondJoyMoveForwardAndZoomPrev% up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад движение влево/вправо в активном окне
WatchAxisSecondJoyMoveRotate:
GetKeyState, 2JoyX, 2JoyX
GetKeyState, 2Joy2, 2Joy2
GetKeyState, 2Joy3, 2Joy3
SecondJoyMoveRotatePrev = %SecondJoyMoveRotate%
if 2Joy2 = D
GoSub, SecondJoyConsumables
else if 2Joy3 = D
GoSub, SecondJoyConsumables
else
{
if 2JoyX > 80
SecondJoyMoveRotate = vk44
else if 2JoyX < 20
SecondJoyMoveRotate = vk41
else
SecondJoyMoveRotate =
}
if SecondJoyMoveRotate = %SecondJoyMoveRotatePrev%
return
SetKeyDelay -1
if SecondJoyMoveRotate
{
ControlSend,, {%SecondJoyMoveRotate% down}, [#] WoT Client [#]
}
if SecondJoyMoveRotatePrev
{
ControlSend,, {%SecondJoyMoveRotatePrev% up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад меню расходников
SecondJoyConsumables:
SecondJoyConsumablesPrev = %SecondJoyConsumables%
if 2JoyX < 20
{
if 2JoyY < 20
SecondJoyConsumables = vk38
else if 2JoyY between 40 and 60
SecondJoyConsumables = vk37
else if 2JoyY > 80
SecondJoyConsumables = vk36
else SecondJoyConsumables =
}
else if 2JoyX between 40 and 60
{
if 2JoyY < 10
SecondJoyConsumables = vk31
else if 2JoyY > 90
SecondJoyConsumables = vk35
else SecondJoyConsumables =
}
else if 2JoyX > 80
{
if 2JoyY < 20
SecondJoyConsumables = vk32
else if 2JoyY between 40 and 60
SecondJoyConsumables = vk33
else if 2JoyY > 80
SecondJoyConsumables = vk34
else SecondJoyConsumables =
}
else SecondJoyConsumables =
if SecondJoyConsumables = %SecondJoyConsumablesPrev%
return
SetKeyDelay -1
if SecondJoyConsumables
{
ControlSend,, {%SecondJoyConsumables% down}, [#] WoT Client [#]
}
if SecondJoyConsumablesPrev
{
ControlSend,, {%SecondJoyConsumablesPrev% up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад обзор и мышь в активном окне
WatchAxisSecondJoyCameraRotate:
MouseNeedsToBeMoved := false
SetFormat, float, 03
GetKeyState, 2JoyU, 2JoyU
GetKeyState, 2JoyR, 2JoyR
GetKeyState, 2Joy5, 2Joy5
if 2Joy5 = D
GoSub, SecondJoyCommandMenu
else if 2Joy5 = U
{
if 2JoyU > %JoyThresholdUpper%
{
MouseNeedsToBeMoved := true
DeltaU := 2JoyU — JoyThresholdUpper
}
else if 2JoyU < %JoyThresholdLower%
{
MouseNeedsToBeMoved := true
DeltaU := 2JoyU — JoyThresholdLower
}
else
DeltaU = 0
if 2JoyR > %JoyThresholdUpper%
{
MouseNeedsToBeMoved := true
DeltaR := 2JoyR — JoyThresholdUpper
}
else if 2JoyR < %JoyThresholdLower%
{
MouseNeedsToBeMoved := true
DeltaR := 2JoyR — JoyThresholdLower
}
else
DeltaR = 0
}
SetKeyDelay -1
if MouseNeedsToBeMoved
{
SetMouseDelay, -1; Makes movement smoother
x := (DeltaU/30) * (ABS(DeltaU)/30) * JoyMultiplier
y := (DeltaR/30) * (ABS(DeltaR)/30) * JoyMultiplier
DllCall(«mouse_event», uint, 1, int, x, int, y, uint, 0, int, 0)
}
return
;;;;;;;;;;;; второй геймпад меню приказов
SecondJoyCommandMenu:
SecondJoyCommandMenuPrev = %SecondJoyCommandMenu%
if 2JoyU < 20
{
if 2JoyR < 20
SecondJoyCommandMenu = Numpad8
else if 2JoyR between 40 and 60
SecondJoyCommandMenu = Numpad7
else if 2JoyR > 80
SecondJoyCommandMenu = Numpad6
else SecondJoyCommandMenu =
}
else if 2JoyU between 40 and 60
{
if 2JoyR < 10
SecondJoyCommandMenu = vk54
else if 2JoyR > 90
SecondJoyCommandMenu = Numpad5
else SecondJoyCommandMenu =
}
else if 2JoyU > 80
{
if 2JoyR < 20
SecondJoyCommandMenu = Numpad2
else if 2JoyR between 40 and 60
SecondJoyCommandMenu = Numpad3
else if 2JoyR > 80
SecondJoyCommandMenu = Numpad4
else SecondJoyCommandMenu =
}
else SecondJoyCommandMenu =
if SecondJoyCommandMenu = %SecondJoyCommandMenuPrev%
return
SetKeyDelay -1
if SecondJoyCommandMenu
{
ControlSend,, {%SecondJoyCommandMenu% down}, [#] WoT Client [#]
}
if SecondJoyCommandMenuPrev
{
ControlSend,, {%SecondJoyCommandMenuPrev% up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад выстрел в активном окне
WatchAxisSecondJoyShoot:
GetKeyState, 2JoyZ, 2JoyZ
SecondJoyShootPrev = %SecondJoyShoot%
if 2JoyZ < 30
SecondJoyShoot = LButton
else
SecondJoyShoot =
if SecondJoyShoot = %SecondJoyShootPrev%
return
SetKeyDelay -1
if SecondJoyShoot
{
Send, {%SecondJoyShoot% down}
}
if SecondJoyShootPrev
{
Send, {%SecondJoyShootPrev% up}
}
return
;;;;;;;;;;;;; второй геймпад крестовина в активном окне
WatchSecondJoyPOV:
GetKeyState, 2JoyPOV, 2JoyPOV
SecondJoyPOVPrev = %SecondJoyPOV%
if 2JoyPOV = 0
SecondJoyPOV = vk52
else if 2JoyPOV = 18000
SecondJoyPOV = vk46
else if 2JoyPOV = 27000
SecondJoyPOV = vk58
else if 2JoyPOV = 9000
SecondJoyPOV = vk43
else SecondJoyPOV =
if SecondJoyPOV = %SecondJoyPOVPrev%
return
SetKeyDelay -1
if SecondJoyPOV
{
ControlSend,, {%SecondJoyPOV% down}, [#] WoT Client [#]
}
if SecondJoyPOVPrev
{
ControlSend,, {%SecondJoyPOVprev% up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад LShift в активном окне
2Joy10::
{
ControlSend,, {vkA0 Down}, [#] WoT Client [#]
KeyWait, 2Joy10
ControlSend,, {vkA0 Up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад Space в активном окне
2Joy9::
{
ControlSend,, {vk20 Down}, [#] WoT Client [#]
KeyWait, 2Joy9
ControlSend,, {vk20 Up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад выбор снарядов в неактивном окне
2Joy1::
Gosub, SecondSubToggle
Return
SecondSubToggle:
SecondToggle++
If SecondToggle = 1
{
ControlSend,, {vk31 down}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk31 up}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk31 down}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk31 up}, [#] WoT Client [#]
}
If SecondToggle = 2
{
ControlSend,, {vk32 down}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk32 up}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk32 down}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk32 up}, [#] WoT Client [#]
}
If SecondToggle = 3
{
ControlSend,, {vk33 down}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk33 up}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk33 down}, [#] WoT Client [#]
Sleep, 10
ControlSend,, {vk33 up}, [#] WoT Client [#]
SecondToggle = 0
}
return
;;;;;;;;;;;; второй геймпад огнетушитель в активном окне
2Joy4::
{
ControlSend,, {vk35 Down}, [#] WoT Client [#]
KeyWait, 2Joy4
ControlSend,, {vk35 Up}, W[#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад автоприцел в активном окне
2Joy6::
{
Send, {RButton Down}
KeyWait, 2Joy6
Send, {RButton up}
}
return
;;;;;;;;;;;; второй геймпад меню в неактивном окне
2Joy8::
{
ControlSend,, {vk1B Down}, [#] WoT Client [#]
KeyWait, 2Joy8
ControlSend,, {vk1B Up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад скрыть мини карту в активном окне
2Joy7::
{
ControlSend,, {vk4D Down}, [#] WoT Client [#]
KeyWait, 2Joy7
ControlSend,, {vk4D Up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад скрыть мини карту в активном окне
2Joy5::
{
ControlSend,, {vk5A Down}, [#] WoT Client [#]
KeyWait, 2Joy5
ControlSend,, {vk5A Up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад ремонт
2Joy3::
{
ControlSend,, {vk34 Down}, [#] WoT Client [#]
KeyWait, 2Joy3
ControlSend,, {vk34 Up}, [#] WoT Client [#]
}
return
;;;;;;;;;;;; второй геймпад лечение
2Joy2::
{
ControlSend,, {vk36 Down}, [#] WoT Client [#]
KeyWait, 2Joy2
ControlSend,, {vk36 Up}, [#] WoT Client [#]
}
return
Разумеется, играть перед ТВ никто не собирался на клавиатурах/мышах. Управление танками происходит с помощью двух геймпадов от Xbox360. За основу было выбрано управление от версии для Xbox 360.
В общем, у меня получилось как то так.
Видео геймплея в режиме разделенного экрана:
Видео записывалось с черновым скриптом — когда было только управление танком, башней, снайперский режим и выстрел пробелом.
4. Настройка вибраций для геймпадов.
Здесь [3] я уже отписывал о добавлении вибраций в игру на геймпаде. Так как данная модификация игры использует веб-сервис для отправки вибраций, то для отправки во второй геймпад, нужно было просто изменить порт Flask.
Но, для игры в «Разделенном экране» на вибрирующих геймпадах нужно запускать в «песочнице» полную копию клиента (скопировать папку рядом с другим именем) со своим отдельным модом, также скопировать в «песочницу» Python27.
Думается, что данное решение можно попробовать применить ко многим играм. Решение получилось очень неудобным — много всяких «но». Но могу сказать что игра для «фана» удалась. Удачи всем в боях!
Автор: kharlashkin
Источник [10]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/testirovanie/63571
Ссылки в тексте:
[1] Взвод: https://ru.wargaming.net/support/Knowledgebase/Article/View/213/18/kk-igrt-vdvojom-vtrojomsozdt-vzvod-n-dvoikh-troikh
[2] Wikipedia: http://ru.wikipedia.org/wiki/World_of_Tanks
[3] вибрацию: http://habrahabr.ru/post/208478/
[4] Sandboxie: http://www.sandboxie.com/
[5] Snap: http://windows.microsoft.com/en-US/windows7/Arrange-windows-side-by-side-on-the-desktop-using-Snap
[6] Virtual Display Manager: http://www.ishadow.com/vdm/?doing_wp_cron=1402748491.5779800415039062500000
[7] AutoHotkey: http://www.autohotkey.com/
[8] ссылка: http://www.script-coding.com/AutoHotkeyTranslation.html
[9] Серому форуму: http://forum.script-coding.com/index.php
[10] Источник: http://habrahabr.ru/post/227749/
Нажмите здесь для печати.