利用yolov5和模板匹配制作游戏自动化脚本

大家好,今天为大家带来原神自动刷副本的脚本,先看看效果。

ae4ff506c648d35478957ce3830f0e75.gif

1、脚本思路:

1、找到进入副本的特征,根据这个特征让角色移动到挑战地点。

2、当角色到达挑战地点,找到副本挑战开始的信号。

3、挑战开始后,根据队伍预设的指令进行攻击,一直循环直到挑战成功。

4、找到挑战成功的标识,以此来结束攻击循环。

5、识别周围环境,寻找领奖位置

6、移动到领奖处,识别可以领取奖励的标志,并按照指令领奖。

 

2、代码前的准备:

        由脚本思路中,我们需要准备通关副本的各种数据(脚本思路下划线部分)。

        首先,对于其中一些简单的标识,在电脑屏幕上持续时间长且容易识别,采用模板匹配算法

        模板匹配就是将特征图在一张图片里不停的移动并进行匹配,根据匹配相似度得到最高值,具体就不在这里说了。说回到简单的标识,比如,开启副本的标识,挑战位置的标识,等等。这些都是固定的标识。这些标识如下:

4b22de22b14b45d293a6534db1cec317.png

        得到了这些标识,也就是所谓特征。那么我们需要将标识在一张图片里移动,找到相似度最高的值(模板匹配算法)。那么这个图片在哪里来呢?当然是我们的屏幕,但是把全屏放进去是不好的,这样运算速度比较慢,我们选择将屏幕部分区域截取下来,让这些特征在区域内进行匹配,这样大大节省了时间。

        但是这些区域怎么获得呢?这个根据自己的显示屏大小来确定了。以我的为例:

602994712dbcd4d92479d77d34b7d8be.png

        tree_pos为石化古树的位置(874,278),(1042,321)分别代表了这个区域左上角和右下角点在屏幕上的位置坐标。这样,将所有需要匹配的特征加载进来,已经这些匹配区域的位置信息得到,就能进行识别。

        上面说完了简单的标识,对于不那么简单的标识,模板匹配就不那么好用了。例如在战斗过后,找寻领奖位置。在战斗中难免有碰撞,导致位置不一样,包括视角转动这些,在进行匹配就不那么好找了,当然可以选择降低阈值,但很多时候仍然不容易找到。选择用YOLO进行识别。效果图如下:

a873957b009ffe2300b56602be6df91f.png

        至此,简单和困难的标识都得到了解决,下面着手代码的编写。

3、代码

1、对于简单标识,我们需要用模板匹配识别,先定义一个识别函数:

#匹配函数值
def get_match_value(path,pos):
    area = src_area(pos)
    tar = cv2.imread(path)
    res = cv2.matchTemplate(area,tar,cv2.TM_CCOEFF_NORMED)
    _,max_value,_,_= cv2.minMaxLoc(res)
    return max_value

        函数输入值path,pos就是上面图里面的每一个特征的path和pos。

这里的src_area函数如下定义:

def src_area(xyxy):
    im = ImageGrab.grab()
    r,g,b = cv2.split(np.array(im))
    img = cv2.merge([b,g,r])
    if xyxy==None:
        return img
    return img[xyxy[1]:xyxy[3],xyxy[0]:xyxy[2]]

        有了这个函数,我们就基本可以完成副本挑战一半的任务了。

2、利用识别函数识别,定义在识别后需要进行的操作

        已经说了,用这个识别函数就能做到很多事了,如下:

        1、关闭副本提示

#关闭副本提示(标志)    
def close_tip(thr):
    value = get_match_value(place_tip_path,place_tip_pos)
    if value>=thr:
        time.sleep(1)
        mouse.click()
        return True
    return False

        2、去往挑战处

#前往挑战处
def go_to_start(thr):
    key.kevent_down('w')
    while True:
        value=get_match_value(start_path,start_pos)
        if value>=thr:
            key.kevent_up('w')
            key.kevent_down('f')
            key.kevent_up('f')
            break

        3、判断挑战是否成功

#判断是否挑战成功
def win_the_challenge(thr):
    value = get_match_value(end_path,end_pos)
    if value>=thr:
        return True
    return False

        4、去往领奖处

#前往领奖处
def go_to_reward(thr):
    while True:
        key.kevent_down('w')
        value=get_match_value(reward_path,reward_pos)
        if value>=thr:
            key.kevent_up('w')
            key.kevent_down('f')
            key.kevent_up('f')
            break
        time.sleep(0.5)
        key.kevent_up('w')

        5、是否有足够体力进行下一次副本

#是否有足够体力
def have_sz(thr):
    value = get_match_value(tree_path,tree_pos)
    if value>=thr:
        return False
    return True

        好了,到此一些简单标识能做的任务我们已经完成了。

3、定义战斗函数

        在《识别后操作》部分1、2、3中,我们通过1、2部分已经可以成功走到挑战位置并开启挑战,这时在挑战开启后执行战斗函数并通过3部分判断是否战斗胜利。战斗函数比较随意,可以根据自己的设置来配置,我的战斗函数为:

#预设战斗指令
def fight():
    time.sleep(0.4)
    key.kevent_down('e')
    time.sleep(1)
    key.kevent_up('e')
    time.sleep(0.5)
    key.kevent_down('2')
    key.kevent_up('2')
    time.sleep(0.5)
    key.kevent_down('e')
    key.kevent_up('e')
    time.sleep(1)
    key.kevent_down('3')
    key.kevent_up('3')
    time.sleep(0.5)
    key.kevent_down('e')
    time.sleep(0.4)
    for i in range(10):
        mouse.move_mouse((800,0))
        time.sleep(0.05)
    key.kevent_up('e')
    time.sleep(0.5)
    key.kevent_down('4')
    key.kevent_up('4')
    time.sleep(0.4)
    key.kevent_down('e')
    key.kevent_up('e')
    time.sleep(0.9)
    key.kevent_down('1')
    key.kevent_up('1')

4、战斗胜利后找寻领奖位置

        在《识别后操作》4部分,我们已经有了去往领奖处的函数了,但是找到领奖处却要在这里定义,这是因为领奖识别比较简单,用模板匹配容易,而找到这个位置需要用YOLO。接定义战斗函数部分后,在战斗后,我们已经识别了战斗胜利了,这时还没有操作,我们让计算机转动视角,同时将每转动一次的图像传递给YOLO模型,进行识别,识别到领奖位置后,停止转动视角。代码如下:

#寻找领奖位置
def find_goal(model):
    area = src_area(yolo_pos)
    xywh,conf = model.inference(area)
    return xywh,conf

        这里的model如下定义:

class yolo_model():
    def __init__(self):
        device = ''
        weights = 'yolov5/runs/train/exp12/weights/best.pt'  # pt
        data = 'yolov5/ys_data/ys.yaml'  # yaml
        half = False
        dnn = False
        imgsz = (640, 640)
        self.augment = False
        self.max_det = 1000
        self.agnostic_nms = False
        self.classes = None
        self.conf_thres = 0.25  # confidence threshold
        self.iou_thres = 0.45

        # Load model
        device = select_device(device)
        self.model = DetectMultiBackend(weights, device=device, dnn=dnn, data=data, fp16=half)
        self.stride, names, self.pt = self.model.stride, self.model.names, self.model.pt
        self.imgsz = check_img_size(imgsz, s=self.stride)  # check image size

        # Run inference
        self.model.warmup(imgsz=(1, 3, *imgsz))  # warmup
        self.dt = (Profile(), Profile(), Profile())

    def inference(self,im0):
        im = letterbox(im0, self.imgsz, stride=self.stride, auto=self.pt)[0]  # padded resize
        im = im.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
        im = np.ascontiguousarray(im)

        with self.dt[0]:
            im = torch.from_numpy(im).to(self.model.device)
            im = im.half() if self.model.fp16 else im.float()  # uint8 to fp16/32
            im /= 255  # 0 - 255 to 0.0 - 1.0
            if len(im.shape) == 3:
                im = im[None]  # expand for batch dim

        # Inference
        with self.dt[1]:
            visualize = False
            pred = self.model(im, augment=self.augment, visualize=visualize)

        # NMS
        with self.dt[2]:
            pred = non_max_suppression(pred, self.conf_thres, self.iou_thres, self.classes, self.agnostic_nms, max_det=self.max_det)

        det = pred[0]
        gn = torch.tensor(im0.shape)[[1, 0, 1, 0]]  # normalization gain whwh
        cof = None
        xywh = None
        if len(det):
            for *xyxy, conf, cls in reversed(det):
                xywh = (xyxy2xywh(torch.tensor(xyxy).view(1, 4)) / gn).view(-1).tolist()
                cof = conf
        return xywh,cof

        寻找到位置后,结合《识别后操作》4部分,我们成功来到领奖位置处并获得奖励。

5、函数汇总 

        以下是源文件,执行就可以自动化了

import cv2
import sys
sys.path.append(r'./yolov5')
from my_utils.screen_capture import src_area
from model import yolo_model
from my_utils.mouse_control import *
import my_utils.keyboard as key
import time
mouse=Mouse()

#匹配图形位置及路径
tree_pos = [874,278,1042,321]
tree_path = './img/no_sz.png'
use_nssz_pos = (747,744)
place_tip_pos=[853,673,1055,709]
place_tip_path='./img/place_tip.png'
start_pos=[1163,516,1292,561]
start_path='./img/start_button.png'
reward_pos = [1208,515,1349,556]
reward_path = './img/reward.png'
end_pos = [956,941,1082,973]
end_path='./img/end.png'
yolo_pos = [690,9,1214,856]


#获得鼠标在屏幕的位置坐标
def get_src_pos():
    while True:
        if win32api.GetAsyncKeyState(0x04)<0:
            pos = mouse.get_position()
            print(pos)
            while win32api.GetAsyncKeyState(0x04)<0:
                pass


#匹配函数值
def get_match_value(path,pos):
    area = src_area(pos)
    tar = cv2.imread(path)
    res = cv2.matchTemplate(area,tar,cv2.TM_CCOEFF_NORMED)
    _,max_value,_,_= cv2.minMaxLoc(res)
    return max_value
'''
    if max_value>thr:
        print('got the target')
    else:
        print('can not find the target')

'''    
#是否有足够体力
def have_sz(thr):
    value = get_match_value(tree_path,tree_pos)
    if value>=thr:
        return False
    return True
    
#关闭副本提示(标志)    
def close_tip(thr):
    value = get_match_value(place_tip_path,place_tip_pos)
    if value>=thr:
        time.sleep(1)
        mouse.click()
        return True
    return False


#前往挑战处
def go_to_start(thr):
    key.kevent_down('w')
    while True:
        value=get_match_value(start_path,start_pos)
        if value>=thr:
            key.kevent_up('w')
            key.kevent_down('f')
            key.kevent_up('f')
            break
#前往领奖处
def go_to_reward(thr):
    while True:
        key.kevent_down('w')
        value=get_match_value(reward_path,reward_pos)
        if value>=thr:
            key.kevent_up('w')
            key.kevent_down('f')
            key.kevent_up('f')
            break
        time.sleep(0.5)
        key.kevent_up('w')
#预设战斗指令
def fight():
    time.sleep(0.4)
    key.kevent_down('e')
    time.sleep(1)
    key.kevent_up('e')
    time.sleep(0.5)
    key.kevent_down('2')
    key.kevent_up('2')
    time.sleep(0.5)
    key.kevent_down('e')
    key.kevent_up('e')
    time.sleep(1)
    key.kevent_down('3')
    key.kevent_up('3')
    time.sleep(0.5)
    key.kevent_down('e')
    time.sleep(0.4)
    for i in range(10):
        mouse.move_mouse((800,0))
        time.sleep(0.05)
    key.kevent_up('e')
    time.sleep(0.5)
    key.kevent_down('4')
    key.kevent_up('4')
    time.sleep(0.4)
    key.kevent_down('e')
    key.kevent_up('e')
    time.sleep(0.9)
    key.kevent_down('1')
    key.kevent_up('1')
    
#判断是否挑战成功
def win_the_challenge(thr):
    value = get_match_value(end_path,end_pos)
    if value>=thr:
        return True
    return False

#寻找领奖位置
def find_goal(model):
    area = src_area(yolo_pos)
    xywh,conf = model.inference(area)
    return xywh,conf

#退出游戏
def log_out():
    key.kevent_down('menu')
    time.sleep(0.01)
    key.kevent_down('f4')
    time.sleep(0.01)
    key.kevent_up('f4')
    time.sleep(0.01)
    key.kevent_up('menu')

def main():
    model = yolo_model()
    print('press P to start')
    while win32api.GetAsyncKeyState(ord('P'))>=0:
        pass
    while have_sz(0.9):
    
        time.sleep(0.2)
        while True:
            if close_tip(0.95):
                break
        time.sleep(0.5)
        go_to_start(0.9)
        time.sleep(0.5)
        key.kevent_down('1')
        key.kevent_up('1')
        time.sleep(0.5)
        while  not win_the_challenge(0.9):
            fight()
            time.sleep(7)

        flag = True
        while flag:
            xywh,conf = find_goal(model)
            if conf!=None and conf>0.4:
                flag = False
            mouse.move_mouse((40,0))
            time.sleep(0.02)
        time.sleep(0.5)
        go_to_reward(0.9)
        time.sleep(0.5)
        mouse.set_position((747,744))#鼠标移动到浓缩树脂处
        time.sleep(0.02)
        mouse.click()
        time.sleep(10)
        mouse.set_position((1191,993))#鼠标移动到继续挑战处
        time.sleep(0.05)
        mouse.click()
        time.sleep(1)
    time.sleep(1)
    log_out()


if __name__=='__main__':
    
    main()
    #get_src_pos()

如果您看到了这里,真的非常感谢!!!

       请见谅,关于怎么部署这个项目脚本,其实还有许多问题没能在这里清楚讲完,比如my_utils这个包我并没有在这里列出,我也不明白怎么在CSDN上发出项目文件。如果你对这个项目脚本感兴趣,可以发送邮件至我的邮箱:fancyc2000@outlook.com。

      感谢!

 

 

 

 

 

  • 28
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值