概述
本文讲如何通过python发送键盘控制命令控制游戏
后篇:深度强化学习制作森林冰火人游戏AI(四)获取窗口部分界面
获取窗口句柄
窗口句柄就像是进程唯一process id 一样,应该是每个窗口有唯一的handle
获取窗口句柄的方法
window_name="FlashPlay"
handle=windll.user32.FindWindowW(None, window_name)
嗯,没错,复制上篇文章的
查询句柄的子句柄
一个句柄下面可能还有其他的句柄,就像是程序下面挂着几个其他的子程序一样
#handle为父窗口句柄id
def get_child_windows(handle):
'''
获得parent的所有子窗口句柄
返回子窗口句柄列表
'''
if not handle:
return
hwndChildList = []
win32gui.EnumChildWindows(handle, lambda hwnd, param: param.append(hwnd), hwndChildList)
return hwndChildList
为什么要有这部分,是因为后续在控制的时候博主发现通过名字找到的这个窗口句柄并不能直接用于控制游戏
但是它的孩子可以
查询句柄类型
现在我们有一个父亲句柄,还有他的若干孩子句柄
但是只有其中一部分能用来控制游戏,那么是那部分有用呢?
这里就用到查询句柄类型
handle_type=win32gui.GetClassName(handle)
将所有句柄进行查询,发现结果如下
父亲句柄类型:Qt5QWindowIcon
孩子句柄类型:CefBrowserWindow,Chrome_WidgetWin_0,Chrome_RenderWidgetHostHWND
我没有查到这些类型的区别在哪……
最终经过测试(测试方法在后面),发现只有向 Chrome_WidgetWin_0 和 Chrome_RenderWidgetHostHWND 两个类型的句柄发送控制命令才能控制游戏角色
发送键盘控制命令
# 获取句柄
handle = windll.user32.FindWindowW(None, window_name)
w2hd=win32gui.FindWindowEx(handle, None, None, None)
#选中窗口
win32gui.SetForegroundWindow(w2hd)
#按下按键D
win32api.PostMessage(handle, win32con.WM_KEYDOWN,ord('D'), 0)
#延时
time.sleep(self.time_sleep)
#松开按键D
win32api.PostMessage(handle, win32con.WM_KEYUP, ord('D'), 0)
其中 ord(“D”)可以替换成其他按键,具体是哪些按键可以查表
虚拟键表
选择句柄
刚刚说过了,如果只是通过“FlashPlayer”找到的句柄是无法控制游戏的,真正控制游戏的句柄另有其柄
hwndChildList = []
win32gui.EnumChildWindows(andle, lambda hwnd, param: param.append(hwnd), hwndChildList)
for i in hwndChildList:
if win32gui.GetClassName(i)=='Chrome_WidgetWin_0' :
handle=i
break
if win32gui.GetClassName(i)=='Chrome_RenderWidgetHostHWND' :
handle=i
break
print(handle)
这里两个似乎都可以控制游戏,所以我都加上去了,找到其中之一的就可以用制游戏
最终测试
from ctypes import windll
import time
import win32api
import win32con
import win32gui
class Player():
def __init__(self,player,window_name,time_sleep=0.1):
self.time_sleep=time_sleep
self.__handle = windll.user32.FindWindowW(None, window_name)
self.__w2hd=win32gui.FindWindowEx(self.__handle, None, None, None)
hwndChildList = []
win32gui.EnumChildWindows(self.__handle, lambda hwnd, param: param.append(hwnd), hwndChildList)
for i in hwndChildList:
if win32gui.GetClassName(i)=='Chrome_WidgetWin_0' :
self.__handle=i
break
if win32gui.GetClassName(i)=='Chrome_RenderWidgetHostHWND' :
self.__handle=i
break
if player=='red':
self.__right=ord('D')
self.__left=ord('A')
self.__up=ord('W')
self.__down=ord('S')
elif player=='blue':
self.__right=39
self.__left=37
self.__up=38
self.__down=40
else:
print("没有这个角色")
#horizontal 三个离散值,1右走,0不动,-1左走
#jump 两个离散 0不跳,1跳
def move(self,horizontal,jump):
if horizontal==1:
press_key=self.__right
elif horizontal==-1:
press_key=self.__left
else:
press_key=None
win32gui.SetForegroundWindow(self.__w2hd)
if press_key!=None:
win32api.PostMessage(self.__handle, win32con.WM_KEYDOWN, press_key, 0)
if jump==1:
win32api.PostMessage(self.__handle, win32con.WM_KEYDOWN,self.__up, 0)
time.sleep(self.time_sleep)
win32gui.SetForegroundWindow(self.__w2hd)
if press_key!=None:
win32api.PostMessage(self.__handle, win32con.WM_KEYUP, press_key, 0)
if jump==1:
win32api.PostMessage(self.__handle, win32con.WM_KEYUP,self.__up, 0)
if __name__ == "__main__":
window_name='FlashPlay'
fire_man=Player(player='blue',window_name=window_name,time_sleep=0.2)
for i in range(10):
fire_man.move(-1,1)
博主已经集成了控制部分的算法,并根据森林冰火人中冰娃火娃的操控方式做好了控制
最终效果
放不了视频,随便截个图凑数吧