模拟用于控制游戏的 Python 按键
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14489013/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Simulate Python keypresses for controlling a game
提问by user573949
I'm trying to control a game (my two test games are Half Life 2 and Minecraft) using my Kinect and Python. Everything works except for one thing. The game will respond to simulated mouse events and simulated mouse movement (mouse events are done via ctypes and mouse movement is done using pywin32). The problem however is that the games ignore simulated keypresses. Both of them will pick up the simulated keypresses in either the chat window (Minecraft) or the developer console (Half Life 2) but not while playing the actual game.
我正在尝试使用 Kinect 和 Python 控制游戏(我的两个测试游戏是半条命 2 和 Minecraft)。除了一件事,一切都有效。游戏将响应模拟鼠标事件和模拟鼠标移动(鼠标事件通过 ctypes 完成,鼠标移动使用 pywin32 完成)。然而,问题是游戏忽略了模拟按键。他们都会在聊天窗口 (Minecraft) 或开发者控制台 (Half Life 2) 中获取模拟按键,但不会在玩实际游戏时获取。
I've tried several ways of sending the keypresses:
我尝试了几种发送按键的方法:
import win32com.client as client
wsh = client.Dispatch('WScript.Shell')
wsh.AppActivate(gameName)
wsh.SendKeys(key)
and:
和:
import win32api
win32api.keybd_event(keyHexCode, 0, 0)
and:
和:
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))
I should point out the code in the last one isn't mine, it's off another question here on Stack Overflow.
我应该指出最后一个中的代码不是我的,它与 Stack Overflow 上的另一个问题无关。
Does anyone know why none of these work and what the correct way to do this is?
有谁知道为什么这些都不起作用以及正确的方法是什么?
回答by Robin
It's likely that the game is using DirectInput devices.
游戏很可能使用 DirectInput 设备。
So, the game is expecting DirectInput key presses. According to the last post of this forum thread, DirectInput responds to ScanCodes, not VKs. You can try sending DirectInput key presses using this tool. The dev also supplies the source and a detailed explanation.
因此,游戏需要 DirectInput 按键。根据这个论坛帖子的最后一篇文章,DirectInput 响应的是 ScanCodes,而不是 VKs。您可以尝试使用此工具发送 DirectInput 按键。开发人员还提供了源代码和详细说明。
If this works, you could just try sending appropriate ScanCodes instead of VKs (list of scancodes).
如果这有效,您可以尝试发送适当的 ScanCodes 而不是 VKs (list ofscancodes )。
There's also an older project called DirectPythonthat allows you to interface with DirectX/DirectInput.
还有一个名为DirectPython的旧项目,它允许您与 DirectX/DirectInput 交互。
回答by hodka
I just had the same problem trying to simulate keypresses in Half-Life 2. As Robin said, the solution is to use ScanCodes instead of VKs.
我只是在尝试模拟半条命 2 中的按键时遇到了同样的问题。正如 Robin 所说,解决方案是使用 ScanCodes 而不是 VKs。
I edited your last code example such that it uses ScanCodes. I tried it with Half-Life 2 and it works just fine:
我编辑了您的最后一个代码示例,使其使用 ScanCodes。我在《半条命 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)

