大家好,今天为大家带来原神自动刷副本的脚本,先看看效果。
1、脚本思路:
1、找到进入副本的特征,根据这个特征让角色移动到挑战地点。
2、当角色到达挑战地点,找到副本挑战开始的信号。
3、挑战开始后,根据队伍预设的指令进行攻击,一直循环直到挑战成功。
4、找到挑战成功的标识,以此来结束攻击循环。
5、识别周围环境,寻找领奖位置。
6、移动到领奖处,识别可以领取奖励的标志,并按照指令领奖。
2、代码前的准备:
由脚本思路中,我们需要准备通关副本的各种数据(脚本思路下划线部分)。
首先,对于其中一些简单的标识,在电脑屏幕上持续时间长且容易识别,采用模板匹配算法。
模板匹配就是将特征图在一张图片里不停的移动并进行匹配,根据匹配相似度得到最高值,具体就不在这里说了。说回到简单的标识,比如,开启副本的标识,挑战位置的标识,等等。这些都是固定的标识。这些标识如下:
得到了这些标识,也就是所谓特征。那么我们需要将标识在一张图片里移动,找到相似度最高的值(模板匹配算法)。那么这个图片在哪里来呢?当然是我们的屏幕,但是把全屏放进去是不好的,这样运算速度比较慢,我们选择将屏幕部分区域截取下来,让这些特征在区域内进行匹配,这样大大节省了时间。
但是这些区域怎么获得呢?这个根据自己的显示屏大小来确定了。以我的为例:
tree_pos为石化古树的位置(874,278),(1042,321)分别代表了这个区域左上角和右下角点在屏幕上的位置坐标。这样,将所有需要匹配的特征加载进来,已经这些匹配区域的位置信息得到,就能进行识别。
上面说完了简单的标识,对于不那么简单的标识,模板匹配就不那么好用了。例如在战斗过后,找寻领奖位置。在战斗中难免有碰撞,导致位置不一样,包括视角转动这些,在进行匹配就不那么好找了,当然可以选择降低阈值,但很多时候仍然不容易找到。选择用YOLO进行识别。效果图如下:
至此,简单和困难的标识都得到了解决,下面着手代码的编写。
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。
感谢!