web-dev-qa-db-fra.com

Simuler Python touches pour contrôler un jeu

J'essaie de contrôler un jeu (mes deux jeux de test sont Half Life 2 et Minecraft) en utilisant mon Kinect et Python. Tout fonctionne sauf une chose. Le jeu répondra aux événements de souris simulés et aux mouvements de souris simulés (les événements de souris se font via des ctypes et le mouvement de la souris se fait en utilisant pywin32). Le problème est cependant que les jeux ignorent les frappes de touches simulées. Les deux récupéreront les touches simulées dans la fenêtre de discussion (Minecraft) ou la console de développeur (Half Life 2) mais pas en jouant au jeu réel.

J'ai essayé plusieurs façons d'envoyer les touches:

import win32com.client as client
wsh = client.Dispatch('WScript.Shell')
wsh.AppActivate(gameName)
wsh.SendKeys(key)

et:

import win32api
win32api.keybd_event(keyHexCode, 0, 0)

et:

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))

Je dois souligner que le code dans le dernier n'est pas le mien, c'est une autre question ici sur Stack Overflow.

Est-ce que quelqu'un sait pourquoi aucun de ces travaux ne fonctionne et quelle est la bonne façon de procéder?

30
user573949

J'ai juste eu le même problème en essayant de simuler des pressions de touches dans Half-Life 2. Comme l'a dit Robin, la solution est d'utiliser des ScanCodes au lieu de VK.

J'ai modifié votre dernier exemple de code de sorte qu'il utilise ScanCodes. Je l'ai essayé avec Half-Life 2 et ça marche très bien:

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)
30
hodka

Il est probable que le jeu utilise des périphériques DirectInput.

Ainsi, le jeu attend des appuis sur les touches DirectInput. Selon le dernier message de ce fil de discussion , DirectInput répond aux ScanCodes, pas aux VK. Vous pouvez essayer d'envoyer des pressions de touches DirectInput en utilisant cet outil . Le développeur fournit également la source et une explication détaillée.

Si cela fonctionne, vous pouvez simplement essayer d'envoyer des ScanCodes appropriés au lieu de VK (liste des scancodes) .

Il existe également un ancien projet appelé DirectPython qui vous permet d'interfacer avec DirectX/DirectInput.

3
Robin