1. 想法
突然想搞一個(gè)FGO的腳本委乌,但還是決定先從陰陽師開始入手
畢竟現(xiàn)在版本官方已經(jīng)有了自動(dòng)御魂、覺醒和探索的腳本了荣回。
既然是這樣那不如就搞一個(gè)抽卡的腳本好了遭贸,這樣大佬(土豪,歐皇)們
幾百連抽就不用畫符心软,和點(diǎn)SSR點(diǎn)到手軟了XD壕吹。
2. 實(shí)現(xiàn)
參考了CSDN上兩篇關(guān)于陰陽師探索和自動(dòng)御靈的文章
簡(jiǎn)單講一下思路:
通過圖像識(shí)別(使用cv2這個(gè)庫(kù))來識(shí)別開始抽的藍(lán)票、出現(xiàn)SSR(或SR)和十連抽結(jié)束這幾個(gè)需要點(diǎn)按的場(chǎng)景删铃。然后自動(dòng)點(diǎn)按就可以了耳贬。
不簡(jiǎn)單的講一下具體實(shí)現(xiàn)
(orz簡(jiǎn)書的markdown不支持流程圖)
- 通過adb截圖判斷是否在抽卡界面
- 在的話,使用matchTemplate()函數(shù)識(shí)別開始召喚的按鈕
- 點(diǎn)擊猎唁,畫符
- 同樣使用matchTemplate()函數(shù)和模板對(duì)比識(shí)別SSR和SR并點(diǎn)擊
- 若識(shí)別到“十連召喚”(即抽卡結(jié)束)點(diǎn)擊返回
這里主要通過adb來實(shí)現(xiàn)對(duì)手機(jī)的模擬點(diǎn)按和滑動(dòng)(手機(jī)
記得要開USB偵錯(cuò)啊QWQ)
import os
#模擬點(diǎn)按
def tap(x0, y0):
cmdTap = 'adb shell input tap {x1} {y1}'.format(
x1=x0,
y1=y0
)
print(cmdTap)
os.system(cmdTap)
#模擬滑動(dòng)(用來畫符的)
def swipe(x0, y0, x1, y1, delay0):
cmdSwipe = 'adb shell input swipe {x2} {y2} {x3} {y3} {delay1}'.format(
x2=x0,
y2=y0,
x3=x1,
y3=y1,
delay1=delay0
)
print(cmdSwipe)
os.system(cmdSwipe)
#截圖并返回圖片
def screenshot():
os.system('adb shell screencap -p /sdcard/sh.png')
os.system('adb pull /sdcard/sh.png .')
return "sh.png"
上面這個(gè)基本上是對(duì)手機(jī)的所有操作OWO咒劲。
(我將這個(gè)單獨(dú)保存在了項(xiàng)目目錄的/lib/ats.py中,這樣主程序就可以直接調(diào)用啦XD)
然后來講一下這里最核心的操作:圖像識(shí)別orz
#我們要用的其實(shí)就只有這一個(gè)庫(kù)
import cv2
#不過我們還是要。腐魂。帐偎。
import time
import random
import lib.ats #233 這個(gè)就是剛才那段代碼
import numpy as np
好的我們先來個(gè)例子,就從識(shí)別開始抽卡的按鈕開始蛔屹。削樊。。
def start(sh):
#一看就知道是導(dǎo)入圖片orz
imgSTART = cv2.imread(sh, 0)
templateSTART = cv2.imread('res/START.png', 0) #我的模板保存在了項(xiàng)目目錄的/res文件夾里
#和模板對(duì)比
resSTART = cv2.matchTemplate(imgSTART, templateSTART, cv2.TM_CCOEFF_NORMED)
thresholdSTART = 0.85
pos = []
#如果result大于threshold才可以執(zhí)行(不在界面你抽個(gè)啥)
if (resSTART >= thresholdSTART).any():
loc = np.where(resSTART >= thresholdSTART)
for pt in zip(*loc[::-1]): #剛學(xué)Python沒多久兔毒,我只知道這個(gè)壓縮后切片QAQ
pos.append(pt) #更新list
return pos #返回按鈕位置
else:
return 0
然后是抽到SSR的時(shí)候(其實(shí)這個(gè)完全可以不寫漫贞,因?yàn)镾SR根本不存在(手動(dòng)滑稽)XD)
def ssr(sh):
#這個(gè)不隨機(jī)點(diǎn)說不定有封號(hào)的可能。育叁。迅脐。
xSSR = random.randrange(800, 1200)
ySSR = random.randrange(400, 800)
#導(dǎo)入圖像和模板
imgSSR = cv2.imread(sh, 0)
templateSSR = cv2.imread('res/SR.png', 0)
#和模板對(duì)比
resSSR = cv2.matchTemplate(imgSSR, templateSSR, cv2.TM_CCOEFF_NORMED)
thresholdSSR = 0.30 #這里啊QAQ可能是我的模板不是很好,大于0.3基本識(shí)別不出來
#只要有SSR就行了擂红,然后點(diǎn)一下
if (resSSR >= thresholdSSR).any():
lib.ats.tap(xSSR, ySSR)
SR和這個(gè)代碼其實(shí)是一樣的仪际,無非就是模板不同orz.
但我的模板實(shí)在是太辣雞了所以我把threshold設(shè)成了0.3(也就是說只要30%匹配就行了orz)所以。昵骤。树碱。我的SSR和SR識(shí)別都混在了一起,也就是說無論出的是SR還是SSR都會(huì)按兩次QAQ(R的話想必也是這樣变秦,只是用了圖片測(cè)試成榜。。蹦玫。并沒有票給我測(cè)試QAQ
結(jié)束的識(shí)別和點(diǎn)擊其實(shí)也和這個(gè)也差不多赎婚。。樱溉。
好了基本上就只剩下主程序的代碼了OWO
主程序無非就是搞一個(gè)循環(huán)不斷地截圖挣输,判斷有沒有出SR或SSR還有是不是已經(jīng)抽完了而已
def main():
#里面的delay gap x1 y1 x2 y2 dly 都隨機(jī)產(chǎn)生這里就懶得寫了XD
#這里是截圖判斷是否在抽卡界面
sh = lib.ats.screenshot()
pos = start(sh)
if pos != 0:
lib.ats.tap(pos[0][0], pos[0][1]) #從剛才的代碼可以看到返回的是二維數(shù)組我們就選第一個(gè)好了
lib.ats.swipe(x1, y1, x2, y2, dly)
time.sleep(delay) #十連開始還有點(diǎn)動(dòng)畫的(感覺這個(gè)不加也行)
while lib.ats.screenshot(): #不斷的截圖Zzz...
gap = random.uniform(0.5, 1.5)
time.sleep(gap)
sh = lib.ats.screenshot()
if end(sh) == 1: #抽卡沒結(jié)束(匹配率低于threshold)返回1,否則返回0
ssr(sh)
sr(sh)
elif end(sh) == 0: #結(jié)束的時(shí)候隨機(jī)按一下
xEND = random.randrange(800, 1200)
yEND = random.randrange(400, 800)
lib.ats.tap(xEND, yEND)
print("Finished")
break
else:
print("Error") #如果不在抽卡界面的話福贞。撩嚼。。
OWO基本上整個(gè)程序的嗎都打上來了挖帘。完丽。。
整個(gè)完整的項(xiàng)目已經(jīng)放到了Github上面拇舀,想看完整的就自己去看看咯QWQ
3. 總結(jié)
我覺得其實(shí)沒什么好總結(jié)的orz
其實(shí)感覺抽卡的腳本真的沒什么用逻族。。骄崩。
如果有Bug和更好的想法記得告訴我哦