仅做学习交流,非盈利,侵联删(狗头保命)
一、概述
1.1 效果
总的来说,这种方式是通过图像识别来完成的,不侵入游戏,不读取内存,安全不被检测。
1.2 前置知识
- 游戏中有各种不同的q械,不同的q械后坐力不一样,射速也不同。相同的q械,装上不同的配件后,后坐力也会发生变化。
- q械的y轴上移是固定的,x轴是随机的,因此我们程序只移动鼠标y轴。x轴游戏中手动操作。
1.3 实现原理简述
- 通过python中的pynput模块监听键盘鼠标。
监听鼠标左键按下,这个时候开始移动鼠标。左键抬起,终止移动。
监听键盘按键,比如tab键,这时打开背包,截屏开始识别装备栏。
- 通过python的pyautogui模块来截屏,可以截取屏幕指定位置。
- 通过python的opencv模块来处理截取的图片。
- 通过SSIM算法来对比图片相似度,获取到装备栏的武器、配件。
- 通过python的pydirectinput操作鼠标移动。
二、详解
2.1 pynput监听键盘
import pynput.keyboard as keyboard
# 监听键盘deflisten_keybord():
listener = keyboard.Listener(on_press=onPressed, on_release=onRelease)
listener.start()
pynput的监听为异步事件,但是会被阻塞,所以如果事件处理事件过长,得用异步处理。
2.2 监听事件
创建了c_equipment类来封装武器信息。
重点在tab键的监听,使用异步来检测装备信息。
defonRelease(key):try:if'1'== key.char:
c_equipment.switch =1#主武器1elif'2'== key.char:
c_equipment.switch =2#主武器2elif'3'== key.char:
c_equipment.switch =3#手q switch=3的时候不压qelif'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
defadaptive_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
# 屏幕截图defshotCut(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
defsaveScreen():
screen1 = shotCut(1780,125,614,570)
cv2.imwrite("./resource/shotcut/screen.bmp", screen1)
2.4 素材准备
屏幕截图处理后如上,在装备识别之前,我们需要先准备很多素材图片用来对比。
比如:武器名、q托、握把、q口
武器名:
q托
2.5 裁剪图片
为了方便图片对比,我们需要将截取的装备栏部分的图片裁剪成和素材一样大小的图片。
比如,我们要检测武器一的名字:
#读取之前的截屏
screen = cv2.imread("./resource/shotcut/screen.bmp",0)#裁剪出武器1名字
screenWepon1 = screen[0:40,45:125]#拿裁剪的图片和武器素材的目录作为入参,进行对比
w1Name = compareAndGetName(screenWepon1,"./resource/guns/")
2.6 对比图片
#对比图片获取名字defcompareAndGetName(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))#获取相似度最大的ifmax< res and res >0.5:max= res
name =str(fileName)[:-4]return name
SSIM算法:
defcalculate_ssim(img1, img2):ifnot 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 inrange(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轴下移的距离,用来与后坐力对冲。
defmoveMouse():#从识别的数据中,再更具当前选择的武器,获取此刻的武器(比如按下1键,武器装备栏1为m762,那么此时武器就是m762)
curWepone = getCurrentWepone()if(curWepone.name =='none'):return#基础y轴补偿(没任何配件)
basic = curWepone.basic
#射速
speed = curWepone.speed
startTime =round(time.perf_counter(),3)*1000for i inrange(curWepone.maxBullets):#是否可以开火,比如左键抬起,就中断。ifnot canFire():break#系数,比如按住shift屏息,就需要再原来基础上乘1.33
holdK =1.0if c_contants.hold:
holdK = curWepone.hold
#乘以系数后实际的移动距离
moveSum =int(round(basic[i]* curWepone.k * holdK,2))whileTrue:if(moveSum >10):#移动鼠标
pydirectinput.move(xOffset=0, yOffset=10, relative=True)
moveSum -=10elif(moveSum >0):
pydirectinput.move(xOffset=0, yOffset=moveSum, relative=True)
moveSum =0
elapsed =(round(time.perf_counter(),3)*1000- startTime)ifnot canFire()or elapsed >(i +1)* speed +10:break
time.sleep(0.01)
代码中的while循环:
其实再第一发子弹射出后,我们只需要下移42的距离,然后计算出需要等待的时间(0.086-移动鼠标的时间),然后第二发子弹射出,以此类推。
while循环的作用是防止屏幕抖动太厉害。因为直接移动42的距离,游戏中抖的厉害,所以我们再86毫秒的间隔里分了多次来移动鼠标。
python中的sleep函数不准确,所以我们要自己来计时,防止错过每发子弹的时间间隔。
不准确还有个好处,随机,正好不用自己来随机防止检测了。
三、最麻烦的部分
每个q的后坐力都不一样,我们需要自己去游戏的训练场,一发发子弹的调试,获取准确的补偿数据。
四、最后
代码上传到gitee,感兴趣的一起交流。
https://gitee.com/lookoutthebush/PUBG
版权归原作者 七号公园的忧伤 所有, 如有侵权,请联系我们删除。