模拟Python按键控制游戏
我正在尝试用Kinect和Python控制游戏(我测试的两个游戏是《半条命2》和《我的世界》)。除了一个问题,其他都正常。游戏可以响应模拟的鼠标事件和鼠标移动(鼠标事件是通过ctypes实现的,鼠标移动是用pywin32实现的)。但是,问题是游戏不识别模拟的按键输入。这两个游戏在聊天窗口(《我的世界》)或开发者控制台(《半条命2》)中能接收到模拟的按键输入,但在实际游戏中却不行。
我尝试了几种发送按键输入的方法:
import win32com.client as client
wsh = client.Dispatch('WScript.Shell')
wsh.AppActivate(gameName)
wsh.SendKeys(key)
还有:
import win32api
win32api.keybd_event(keyHexCode, 0, 0)
以及:
import ctypes
import time
SendInput = ctypes.windll.user32.SendInput
# C struct redefinitions
PUL = ctypes.POINTER(ctypes.c_ulong)
class KeyBdInput(ctypes.Structure):
_fields_ = [("wVk", ctypes.c_ushort),
("wScan", ctypes.c_ushort),
("dwFlags", ctypes.c_ulong),
("time", ctypes.c_ulong),
("dwExtraInfo", PUL)]
class HardwareInput(ctypes.Structure):
_fields_ = [("uMsg", ctypes.c_ulong),
("wParamL", ctypes.c_short),
("wParamH", ctypes.c_ushort)]
class MouseInput(ctypes.Structure):
_fields_ = [("dx", ctypes.c_long),
("dy", ctypes.c_long),
("mouseData", ctypes.c_ulong),
("dwFlags", ctypes.c_ulong),
("time",ctypes.c_ulong),
("dwExtraInfo", PUL)]
class Input_I(ctypes.Union):
_fields_ = [("ki", KeyBdInput),
("mi", MouseInput),
("hi", HardwareInput)]
class Input(ctypes.Structure):
_fields_ = [("type", ctypes.c_ulong),
("ii", Input_I)]
# Actuals Functions
def PressKey(hexKeyCode):
extra = ctypes.c_ulong(0)
ii_ = Input_I()
ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0, 0, ctypes.pointer(extra) )
x = Input( ctypes.c_ulong(1), ii_ )
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
def ReleaseKey(hexKeyCode):
extra = ctypes.c_ulong(0)
ii_ = Input_I()
ii_.ki = KeyBdInput( hexKeyCode, 0x48, 0x0002, 0, ctypes.pointer(extra) )
x = Input( ctypes.c_ulong(1), ii_ )
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
我得说明一下,最后那段代码不是我写的,是Stack Overflow上另一个问题的内容。
有没有人知道为什么这些方法都不管用,以及正确的做法是什么呢?
2 个回答
7
这个游戏可能在使用DirectInput设备。
所以,游戏在期待DirectInput的按键输入。根据这个论坛帖子的最后一条信息,DirectInput是根据扫描码(ScanCodes)来响应的,而不是虚拟键码(VKs)。你可以试着用这个工具来发送DirectInput的按键输入。开发者还提供了源代码和详细的说明。
如果这样有效,你可以尝试发送合适的扫描码,而不是虚拟键码,具体可以参考这个扫描码列表。
还有一个较老的项目叫DirectPython,它可以让你与DirectX/DirectInput进行交互。
50
我刚刚遇到了同样的问题,想在《半条命2》中模拟按键。正如Robin所说,解决办法是使用扫描码,而不是虚拟键码。
我修改了你最后的代码示例,让它使用扫描码。我在《半条命2》中试过,效果很好:
import ctypes
import time
SendInput = ctypes.windll.user32.SendInput
# C struct redefinitions
PUL = ctypes.POINTER(ctypes.c_ulong)
class KeyBdInput(ctypes.Structure):
_fields_ = [("wVk", ctypes.c_ushort),
("wScan", ctypes.c_ushort),
("dwFlags", ctypes.c_ulong),
("time", ctypes.c_ulong),
("dwExtraInfo", PUL)]
class HardwareInput(ctypes.Structure):
_fields_ = [("uMsg", ctypes.c_ulong),
("wParamL", ctypes.c_short),
("wParamH", ctypes.c_ushort)]
class MouseInput(ctypes.Structure):
_fields_ = [("dx", ctypes.c_long),
("dy", ctypes.c_long),
("mouseData", ctypes.c_ulong),
("dwFlags", ctypes.c_ulong),
("time",ctypes.c_ulong),
("dwExtraInfo", PUL)]
class Input_I(ctypes.Union):
_fields_ = [("ki", KeyBdInput),
("mi", MouseInput),
("hi", HardwareInput)]
class Input(ctypes.Structure):
_fields_ = [("type", ctypes.c_ulong),
("ii", Input_I)]
# Actuals Functions
def PressKey(hexKeyCode):
extra = ctypes.c_ulong(0)
ii_ = Input_I()
ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008, 0, ctypes.pointer(extra) )
x = Input( ctypes.c_ulong(1), ii_ )
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
def ReleaseKey(hexKeyCode):
extra = ctypes.c_ulong(0)
ii_ = Input_I()
ii_.ki = KeyBdInput( 0, hexKeyCode, 0x0008 | 0x0002, 0, ctypes.pointer(extra) )
x = Input( ctypes.c_ulong(1), ii_ )
ctypes.windll.user32.SendInput(1, ctypes.pointer(x), ctypes.sizeof(x))
# directx scan codes http://www.gamespp.com/directx/directInputKeyboardScanCodes.html
while (True):
PressKey(0x11)
time.sleep(1)
ReleaseKey(0x11)
time.sleep(1)