社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

绝地求生 压枪 Python 版

Python编程 • 1 年前 • 633 次点击  

来自:掘金,作者:LookOutTheBush

链接:https://juejin.cn/post/7232253274056785957

本文仅供技术学习参考,仅做学习交流,非盈利

一、概述

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(1780125614570)
    cv2.imwrite("./resource/shotcut/screen.bmp", screen1)

2.4 素材准备

屏幕截图处理后如上,在装备识别之前,我们需要先准备很多素材图片用来对比。比如:武器名、枪托、握把、枪口

武器名:


枪托


2.5 裁剪图片

为了方便图片对比,我们需要将截取的装备栏部分的图片裁剪成和素材一样大小的图片。

比如,我们要检测武器一的名字:

#读取之前的截屏
screen = cv2.imread("./resource/shotcut/screen.bmp"0)
#裁剪出武器1名字
screenWepon1 = screen[0:4045: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 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,感兴趣的一起交流。

https://gitee.com/lookoutthebush/PUBG

---END---


推荐↓↓↓

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/156136
 
633 次点击