仅做学习交流,非盈利,侵联删(狗头保命)

一、概述

1.1 效果

总的来说,这种方式是经过图像辨认来完结的,不侵入游戏,不读取内存,安全不被检测。

1.2 前置知识

  1. 游戏中有各种不同的枪械,不同的枪械后坐力不相同,射速也不同。相同的枪械,装上不同的配件后,后坐力也会发生变化。
  2. 枪械的y轴上移是固定的,x轴是随机的,因而咱们程序只移动鼠标y轴。x轴游戏中手动操作。

1.3 完成原理简述

  1. 经过python中的pynput模块监听键盘鼠标。

监听鼠标左键按下,这个时分开端移动鼠标。左键抬起,终止移动。 监听键盘按键,比方tab键,这时翻开背包,截屏开端辨认配备栏。

  1. 经过python的pyautogui模块来截屏,能够截取屏幕指定方位。

  2. 经过python的opencv模块来处理截取的图片。

  3. 经过SSIM算法来比照图片类似度,获取到配备栏的兵器、配件。

  4. 经过python的pydirectinput操作鼠标移动。

二、详解

2.1 pynput监听键盘

import pynput.keyboard as keyboard
# 监听键盘
def listen_keybord():
    listener = keyboard.Listener(on_press=onPressed, on_release=onRelease)
    listener.start()

pynput的监听为异步事情,但是会被堵塞,所以如果事情处理事情过长,得用异步处理。

2.2 监听事情

创建了c_equipment类来封装兵器信息。 重点在tab键的监听,运用异步来检测配备信息。

def onRelease(key):
    try:
        if '1' == key.char:
            c_equipment.switch = 1 #主兵器1
        elif '2' == key.char:
            c_equipment.switch = 2 #主兵器2
        elif '3' == key.char:
            c_equipment.switch = 3 #手枪 switch=3的时分不压枪
        elif '4' == key.char:
            c_equipment.switch = 3 #刀具
        elif '5' == key.char:
            c_equipment.switch = 3 #手雷
    except AttributeError:
        if 'tab' == key.name:      #tab键异步操作检测
            asyncHandle()
        elif 'num_lock' == key.name:  #小键盘锁用来控制程序开关
            changeOpen()
        elif 'shift' == key.name:   
            c_contants.hold = False

2.3 pyautogui截屏

检测配备,首要要在翻开配备栏的时分,截屏。

pyautogui.screenshot(region=[x, y, w, h])

x,y别离表明坐标,w,h表明宽度和高度。 截取之后,为了便利比照图片,需求将图片二值化,然后保存到本地。

完好代码如下:

import pyautogui
def adaptive_binarization(img):
    #自适应二值化
    maxval = 255
    blockSize = 3
    C = 5
    img2 = cv2.adaptiveThreshold(img, maxval, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, blockSize, C)
    return img2
# 屏幕截图
def shotCut(x, y, w, h):
    im = pyautogui.screenshot(region=[x, y, w, h])
    screen = cv2.cvtColor(numpy.asarray(im), cv2.COLOR_BGR2GRAY)
    temp = adaptive_binarization(screen)
    return temp
def saveScreen():
    screen1 = shotCut(1780, 125, 614, 570)
    cv2.imwrite("./resource/shotcut/screen.bmp", screen1)

绝地求生 压枪python版

2.4 资料预备

屏幕截图处理后如上,在配备辨认之前,咱们需求先预备许多资料图片用来比照。 比方:兵器名、枪托、握把、枪口

绝地求生 压枪python版

兵器名:

绝地求生 压枪python版

枪托

绝地求生 压枪python版

2.5 裁剪图片

为了便利图片比照,咱们需求将截取的配备栏部分的图片裁剪成和资料相同巨细的图片。

比方,咱们要检测兵器一的姓名:

#读取之前的截屏
screen = cv2.imread("./resource/shotcut/screen.bmp", 0)
#裁剪出兵器1姓名
screenWepon1 = screen[0:40, 45:125]
#拿裁剪的图片和兵器资料的目录作为入参,进行比照
w1Name = compareAndGetName(screenWepon1, "./resource/guns/")

2.6 比照图片

#比照图片获取姓名
def compareAndGetName(screenImg, dir):
    #获取目录下一切文件
    content = os.listdir(dir)
    name = 'none'
    max = 0
    #遍历文件
    for fileName in content:
        #运用opencv读取文件
        curWepone = cv2.imread(dir + fileName, 0)
        #运用SSIM算法拿到图片类似度
        res = calculate_ssim(numpy.asarray(screenImg), numpy.asarray(curWepone))
        #获取类似度最大的
        if max < res and res > 0.5:
            max = res
            name = str(fileName)[:-4]
    return name

SSIM算法:

def calculate_ssim(img1, img2):
    if not img1.shape == img2.shape:
        raise ValueError('Input images must have the same dimensions.')
    if img1.ndim == 2:
        return ssim(img1, img2)
    elif img1.ndim == 3:
        if img1.shape[2] == 3:
            ssims = []
            for i in range(3):
                ssims.append(ssim(img1, img2))
            return numpy.array(ssims).mean()
        elif img1.shape[2] == 1:
            return ssim(numpy.squeeze(img1), numpy.squeeze(img2))
    else:
        raise ValueError('Wrong input image dimensions.')

到这,咱们就能获取到配备栏1方位的兵器姓名了。

2.7 操作鼠标

知道兵器姓名后,同理,咱们能够获取到配备的配件。 然后,监听鼠标左键按下,然后开端下移鼠标。

咱们以m762兵器为例:

射速:86, 每一发子弹距离86毫秒

后坐力: [42, 36, 36, 36, 42, 43, 42, 43, 54, 55, 54, 55, 54, 55, 54, 55, 62, 62, 62, 62, 62, 62, 62, 62,62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, 77, 78, 77, 78]

表明每发子弹打出后,需求在y轴下移的距离,用来与后坐力对冲。

def moveMouse():
    #从辨认的数据中,再更具当时挑选的兵器,获取此刻的兵器(比方按下1键,兵器配备栏1为m762,那么此时兵器就是m762)
    curWepone = getCurrentWepone()
    if (curWepone.name == 'none'):
        return
    #根底y轴补偿(没任何配件)
    basic = curWepone.basic
    #射速
    speed = curWepone.speed
    startTime = round(time.perf_counter(), 3) * 1000
    for i in range(curWepone.maxBullets):
        #是否能够开火,比方左键抬起,就中断。
        if not canFire():
            break
        #系数,比方按住shift屏气,就需求再本来根底上乘1.33
        holdK = 1.0
        if c_contants.hold:
            holdK = curWepone.hold
        #乘以系数后实践的移动距离
        moveSum = int(round(basic[i] * curWepone.k * holdK, 2))
        while True:
            if (moveSum > 10):
                #移动鼠标
                pydirectinput.move(xOffset=0, yOffset=10, relative=True)
                moveSum -= 10
            elif (moveSum > 0):
                pydirectinput.move(xOffset=0, yOffset=moveSum, relative=True)
                moveSum = 0
            elapsed = (round(time.perf_counter(), 3) * 1000 - startTime)
            if not canFire() or elapsed > (i + 1) * speed + 10:
                break
            time.sleep(0.01)

代码中的while循环:

其实再榜首发子弹射出后,咱们只需求下移42的距离,然后计算出需求等待的时刻(0.086-移动鼠标的时刻),然后第二发子弹射出,以此类推。

while循环的作用是避免屏幕抖动太凶猛。因为直接移动42的距离,游戏中抖的凶猛,所以咱们再86毫秒的距离里分了多次来移动鼠标。

python中的sleep函数不精确,所以咱们要自己来计时,避免错过每发子弹的时刻距离。 不精确还有个优点,随机,正好不用自己来随机避免检测了。

三、最麻烦的部分

每个枪的后坐力都不相同,咱们需求自己去游戏的训练场,一发发子弹的调试,获取精确的补偿数据。

四、最终

代码上传到gitee,感兴趣的一起交流。 gitee.com/lookouttheb…