P-1.4.3 Python实现OW自动瞄准的起与落

在逐渐舍弃Pyuserinput与PIL模块并使用pywin32来截图与操作后,我的程序成功地达到了每秒59次的执行次数
以下的代码已经有了基本的实用价值

from math import pi,asin#载入数学符号
import win32api,win32con,win32gui,win32ui#载入pywin32模块,pywin32有许多功能,是windows提供的接口,追求效率或追求控制更深层的系统功能的话,这是值得学习的
e=1#改变函数f的弯曲方向,f是一个指数函数(e小于1时是凸函数)
ke=(1*2000/303)**(1/e-1)#函数f的系数,(2000/33)决定了对于函数f当指针与目标相距50像素时f(x)=x,(这是一个指数函数,画张图就非常明白了)。2000/33前的系数(1)用于妥善增大或减小50这个值
f= lambda x:(ke*x)**e if x >=0 else -(-ke*x)**e#这是一个‘指数函数’,只是在x小于0时它的图像是对称于x大于0时的图像的。

hwnd=0#目标窗口的句柄,0是全屏
x=0
y=0#为x,y设置一个初始值
xs=1920#xs,ys代表窗口大小
ys=1080#由于代码多次修改,并没有做到只要修改xs、ys就能使程序匹配新的窗口大小,还需做很多调整
x0=xs/2
y0=ys/2#x0,y0是窗口的中点
nx=0
ny=0#为x0,y0设置一个初始值
while 1==1:
    ni=0
    flag=0
    ki=0#以上三个数据用于重启找色部

    #截图部
    hwndDC=win32gui.GetWindowDC(0) 
    mfcDC=win32ui.CreateDCFromHandle(hwndDC)
    saveDC=mfcDC.CreateCompatibleDC()
    saveBitMap=win32ui.CreateBitmap()#以下几行均是在这个bitmap中作画的代码
    saveBitMap.CreateCompatibleBitmap(mfcDC,300,300)#检测范围可以再大点吗
    saveDC.SelectObject(saveBitMap)
    saveDC.BitBlt((0, 0), (300, 300), mfcDC, (810,390), win32con.SRCCOPY) #第一个二元数对是画作左上角在bitmap中的位置,第二个是画作与画作源的大小,第三个是画作源左上角在屏幕中的位置
    data=saveBitMap.GetBitmapBits() #获取bitmap中每个点的R.G.B.alpha值构成的一个元组(顺序是G.B.R.alpha)。已知的图像大小的情况下,这个有序元组结合二维空间中每一个点的色彩值信息



    #saveDC.DeleteDC()
    #mfcDC.DeleteDC()
    #win32gui.ReleaseDC(hwnd, hwndDC)
    #win32gui.DeleteObject(saveBitMap.GetHandle()) 
    #输出data后便可清空截图的缓存。由于未系统学习win32,原理不明。耗时极短。作为可选项
    #耗时小于0.0005s/100次






    #找色部 
    while flag == 0:

        try:#其实这种遍历方式我也不是很喜欢,所以问:是否可以一次只取第一个19,不符合就直接返回未找到?←决定其,我可以在找到合适的函数后看一看这个遍历起到了多大的作用,共占用了多少的时间,最长占用多少时间。

    #目前的感受是,在测试中找色部没有影响程序的流畅感。
    #完成于凌晨4点,无法正常思考所以实现方式冗杂。尚未改进(便不作太多注释了),但似乎没有太大影响。          
            ni=data[ki:].index(19) #
            ki=ki+ni

            if ki%4==0 and data[ki+1]==0 and data[ki+2]==-1: #此处的if也可以精简
                flag=1

                x=(ki/4)%300+810+50
                y=(ki/4)//300+390+75
            else:
                ki=ki+1

        except:
            x,y=960,540
            flag=1
    #此时,输出了x,y



    #计算部
    #将x,y转化为鼠标需要移动的像素数与方向
    #基本思想是在已知视角为90度且通过实验得到鼠标移动1像素在某灵敏度下[角度→像素]的关系的情况下,将目标在屏幕x轴上的比值转化为偏移角度,再将角度转化为鼠标需要移动的方向与像素数,y轴同理
    #可以简化,但本身用时极少
    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))#OW 20灵敏度下,x一圈2727像素 y一圈2700像素    
    nx=round(f(xt*2727/(360*2*3.51)))#round后误差仅在1像素即0.1度左右,是可以接受的误差
    #分母(小,大)→速度(快,慢),过头程度(高,较低),建议y轴过头少x速度快
    ny=round(yt*2700/(360*2*3.51))#nx与ny的分母已经过调试(理论上2可减小到1.5),使得过头程度几乎为0,在这个基础上可以改变函数f或者分母的系数来调整nx与ny的大小

    #操作部
    #使用win32api的mouse event才能模拟鼠标拖动而不是重新设置鼠标位置。后一个方法是不能操作FPS游戏的。
    win32api.mouse_event(win32con.MOUSEEVENTF_MOVE,nx,ny)

    #循环

转载请标:http://mp.blog.csdn.net/mdeditor/index/79123918
整体用时:1.6740703582763672s/100次 →帧率:59.7346

在决定使用pywin32截图之前,我尝试过打开PIL.ImageGrab的module文件并查看.grab()的源代码,发现其包含了很多我用不上的命令,删去后程序获得了略微的提速。在希望加快程序运行速度又因为技术限制无法自己用低级语言实现的时候,不妨打开函数的module文件,看看能不能删掉一点无关的指令。

在OW的训练模式中,程序有了出色的表现,59的帧率非常流畅。但是在PVE甚至PVP中,OW似乎刻意地不允许截图(进入PVE、PVP后才会出现状况),无论窗口化、无边框还是全屏截图结果只有OW窗口为黑色。
这应该涉及到了Overlay Surfaces相关的问题。

下面这位网友解释得比较全面准确,当然也有可能是因为OW对窗体设置了IsScreenCaptureEnabled属性
P-1.4.3 Python实现OW自动瞄准的起与落_第1张图片

解决方案一:使用HDMI采集卡直接采集显示器的数据并返回到我的程序中。
解决方案二:想办法获取Overlay Surfaces中的图像数据(或使用别的截图方式)

不管哪种解决方案,win32的截图方式似乎在OW这款游戏中是起不到作用了,但是P-1.4.x的实践让我收获了许多宝贵的经验与方法。

2.12.18备注:
1.OW新春更新后可以直接截图了
2.改进了找色部的方法

#找色部
try:

        ni=data.index(19)#先快速检索 


        if ni%4==0 and data[ni+1]==0 and data[ni+2]==-1: 

            x=(ni/4)%300+810+60
            y=(ni/4)//300+390+50#这里修改了偏移量

        else:#若第一个快速检索结果不符合要求

            try:#则使用精确检索,占用约0.02s,使瞬间帧率降低为30帧,可以接受。
                dat=np.array(data)#将数组转为np数组
                dat=dat.reshape(90000,4)#reshape数组
                ni=np.where((dat==[19,0,-1,-1]).all(1)==True)[0][0]
                #np数组==a 这个指令返回一个布尔值组成的数组,若a是一个数,则np数组的每一个元素被替换为布尔值,若a是一个顺序从0~n数组,则二维np数组每一行的数从0~n地一一与数组中的第0~n个数进行比较并用布尔值替换
                #np数组.all(),若np数组元素全为True则返回True反之返回False;括号中输入1则按行比较,并分别返回T/F,输入0则按列比较,并分别返回T/F
                #np.where(a@b,c,d),a是一个np数组,@是<、>、==、!=等比较符号中的一种,c是用来替代输出中True的元素,d是用来替代输出中False的元素。c与d为空时,这个函数返回a中满足a@b关系的元素在a中的坐标。c与d不为空时,这个函数返回一个c代表T,d代表F的数组,这个数组将体现每一个a中的元素是否满足a@b关系
                x=ni%300+810+60 #此时的ni是90000个像素中的位置,所以不需要除以4
                y=ni//300+390+50

            except:
                x,y=960,540#若精确搜索也无结果,则输出这样的xy
#此时,输出了x,y

你可能感兴趣的:(Python)