【问题标题】:Simulate Python keypresses for controlling a game模拟 Python 按键以控制游戏
【发布时间】:2013-01-07 11:32:54
【问题描述】:

我正在尝试使用我的 Kinect 和 Python 来控制一个游戏(我的两个测试游戏是《半条命 2》和《我的世界》)。一切正常,除了一件事。游戏将响应模拟鼠标事件和模拟鼠标移动(鼠标事件通过 ctypes 完成,鼠标移动使用 pywin32 完成)。然而问题是游戏忽略了模拟按键。他们都会在聊天窗口 (Minecraft) 或开发者控制台 (Half Life 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 上的另一个问题。

有谁知道为什么这些都不起作用以及正确的方法是什么?

【问题讨论】:

  • 我是否建议检查 Minecraft 的来源,看看在聊天和游戏之间收集和处理键盘事件的方式是否存在差异?我回家后会自己看看。
  • 我将不得不撤回我之前关于它实际上不适用于 Minecraft 的声明,它使用 ctypes 系统。我现在要仔细检查《半条命 2》...
  • 是的,它仍然无法正常工作。
  • 更多实验揭示了以下行为: Minecraft:鼠标移动、按键、无鼠标点击。黑光报应:全部。部落崛起:所有。半条命2:鼠标移动,鼠标点击,无按键。有谁知道这些不一致的原因或如何解决它们?
  • Blacklight 和 Tribes 都是虚幻引擎,所以我假设您可以完全兼容所有虚幻游戏。我找不到任何关于 Source 如何处理键盘事件的信息,我也没有使用它。 Minecraft 是“开源”的,您只需对 .jar 进行去混淆处理并查看它们如何处理鼠标点击。

标签: python python-2.7 keypress simulate


【解决方案1】:

我在尝试模拟《半条命 2》中的按键时遇到了同样的问题。正如 Robin 所说,解决方案是使用 ScanCodes 而不是 VK。

我编辑了您的最后一个代码示例,使其使用 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)

【讨论】:

  • 我知道这是非常老的答案,但值得一试。首先,对于这个明确的答案,您比您还多,但我不知道如何为鼠标移动自定义它并单击某个位置进行游戏。感谢您提供任何帮助
  • 谁能指导我如何使用 MouseInput 类
  • 如果可能的话,我还想看看一个带有 MouseInput 的例子
【解决方案2】:

游戏可能正在使用 DirectInput 设备。

因此,游戏需要 DirectInput 按键。根据this forum thread 的上一篇文章,DirectInput 响应的是 ScanCodes,而不是 VKs。您可以尝试使用this tool 发送 DirectInput 按键。开发人员还提供了源代码和详细说明。

如果可行,您可以尝试发送适当的 ScanCodes 而不是 VKs (list of scancodes)

还有一个名为 DirectPython 的旧项目允许您与 DirectX/DirectInput 交互。

【讨论】:

  • 该工具的下载链接已失效,您知道如何使用 DirectPython 实际发送 DirectInput 按键吗?它似乎只是设置为监听它们,而不是发送它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多