最终代码如下:
from PIL.ImageGrab import grab #加载截图命令
from math import pi,asin #加载数学符号
from pykeyboard import PyKeyboard #加载键盘操作命令(配合‘某鼠标模拟器’)
from time import sleep #加载延时命令
pkb=PyKeyboard() #缩写对象PyKeyboard()
keyx= lambda x:6 if x>0 else 4 if x<0 else 7 #不同输入值对应不同的小键盘输出值
keyy= lambda x:5 if x>0 else 8 if x<0 else 7
xs=1920
ys=1080#设定屏幕大小
x0=xs/2
y0=ys/2#计算屏幕中点坐标
nx=0
ny=0
i=0
a=150
bb=150#a决定检索宽度,bb决定检索高度
while 1==1:
im=grab((xs/2-a,ys/2-bb,xs/2+a,ys/2+bb))#截取当前图像
pix=list(im.getdata())#将当前图像每一点的RGB值以一定顺序排列在列表pix中
try:
n=pix.index((255,0,19))#若能得到想要的RGB值在列表中的位置则→else
except:
x=xs/2
y=ys/2#若列表中没有这个RGB值,则将输出坐标设为屏幕中央(后期不需要移动鼠标
else:
x=(xs/2-a)+n%(2*a)+50
y=(ys/2-bb)+n//(2*a)+50#为输出坐标赋值,‘+50’是偏移量 用于瞄准身体而不是血条
ax=xs-x
ay=xs-(y+0.5*(xs-ys))
b=xs/((2)**0.5)
cx=(ax**2+b**2-((2)**0.5)*ax*b)**0.5
xt=45-360*(asin(ax*(2)**0.5/(2*cx))/(2*pi))
cy=(ay**2+b**2-((2)**0.5)*ay*b)**0.5
yt=45-360*(asin(ay*(2)**0.5/(2*cy))/(2*pi))#x一圈3636像素 y一圈3600像素
nx=round(xt*3636/360)#此处得xt与yt函数可以简化
ny=round(yt*3600/360)#round后误差仅在1像素即0.1度左右,是可以接受的误差
#以上用已知的90度视角结合已知的角度→像素关系,得出需要移动的像素值
pkb.tap_key(pkb.numpad_keys[keyx(nx)],n=abs(nx))
pkb.tap_key(pkb.numpad_keys[keyy(ny)],n=abs(ny))#操作部,结合keyx/keyy函数、某鼠标模拟器,使鼠标移动所需要的距离
sleep(0.05)#操作部控制鼠标模拟器有延迟,不进行sleep可能导致在鼠标移动前截图导致瞄准情况混乱
某鼠标模拟器1
运行结果:不好
为了减少list(im.getdata())的用时,我将检索范围缩小到鼠标周围边长为150的方块,程序整体用时为5.56s/100次(甚至没有加上sleep延时,0.05的延时就是加5s),17.98次/s,人在18帧的时候尚无法很好地瞄准,程序在运行的时候效果更差。
若想达到45次/s或更高速度,需要使程序整体用时降低到2.22s/100次或更少。
经过尝试,在检索区域为150*150时,grab()函数用时3.92s/100次,sleep(0.05)将用时5s/100次,这两个部分为核心问题。其次,list(im.getddata())函数用时0.9s左右,计算部与操作部用时极少,这三个部分是难以优化的。
试图解决核心问题一:
使用win32 APIs 截图,在检索区域为150*150时,用时为1.66s(区域为1*1时用时也接近1.66),即使检索区域为1920*1080,用时也仅为3.4s。示例如下:
import time
import win32gui, win32ui, win32con, win32api
def window_capture(filename):
hwnd = 0 # 窗口的编号,0号表示当前活跃窗口
# 根据窗口句柄获取窗口的设备上下文DC(Divice Context)
hwndDC = win32gui.GetWindowDC(hwnd)
# 根据窗口的DC获取mfcDC
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
# mfcDC创建可兼容的DC
saveDC = mfcDC.CreateCompatibleDC()
# 创建bigmap准备保存图片
saveBitMap = win32ui.CreateBitmap()
# 获取监控器信息
MoniterDev = win32api.EnumDisplayMonitors(None, None)
w = MoniterDev[0][2][2]
h = MoniterDev[0][2][3]
# print w,h #图片大小
# 为bitmap开辟空间
saveBitMap.CreateCompatibleBitmap(mfcDC, w, h)
# 高度saveDC,将截图保存到saveBitmap中
saveDC.SelectObject(saveBitMap)
# 截取从左上角(0,0)长宽为(w,h)的图片
saveDC.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY)
saveBitMap.SaveBitmapFile(saveDC, filename)
beg = time.time()#p.s.这里我学到了time.time()的用法,这是一个计时器,通过两次计时相减可得到某一段代码的用时
for i in range(10):
window_capture("haha.jpg")
end = time.time()
print(end - beg)
来源网络
发现,仅需改变saveDC.BitBlt((0, 0), (w, h), mfcDC, (0, 0), win32con.SRCCOPY)中的w、h即可降低用时,此句最少用时为0.5s/100次左右。
受此启发,为了更好地解决核心问题1、2,我决定先学习win32 APIs的用法
例1.尝试用win32 APIs解决核心问题1、2
- 楼月鼠标模拟器 ↩