手游輔助軟件開(kāi)發(fā)

最近沉迷于一款老手游姜挺,每天都花費(fèi)大量的時(shí)間、資源去完成游戲的任務(wù)彼硫。每天做著重復(fù)的動(dòng)作炊豪,實(shí)在是浪費(fèi)青春啊拧篮!作為一個(gè)程序猿怎么受的了词渤,我們不就是為有解決重復(fù)動(dòng)作而生的么!經(jīng)過(guò)沉痛的反思串绩,下決心我要寫一個(gè)輔助軟件去攻略這個(gè)游戲缺虐,從每天的重復(fù)動(dòng)作中解脫出來(lái)...

思路

說(shuō)干就干,這就開(kāi)啟了咱的輔助開(kāi)發(fā)之路礁凡。
說(shuō)到開(kāi)發(fā)高氮,思路最重要慧妄,只有理清了思路,才能知道代碼要怎么寫剪芍。我們玩游戲主要的操作就是:找到相應(yīng)的按鈕塞淹,然后點(diǎn)擊它。從這里可以看到兩個(gè)關(guān)鍵點(diǎn) 點(diǎn)擊屏幕查找按鈕坐標(biāo)位置罪裹,那我們?cè)趺磳?shí)現(xiàn)它呢饱普。這就要用到 Android 的開(kāi)發(fā)工具 ADB,做 Android 開(kāi)發(fā)的同學(xué)應(yīng)該都知道状共,我們可以通過(guò) ADB 工具模擬點(diǎn)擊套耕,滑動(dòng)等一系列的動(dòng)作。

運(yùn)行命令

我們需要在我們的代碼里運(yùn)行 cmd峡继,從而達(dá)到運(yùn)行 adb 命令冯袍。

# 運(yùn)行命令
def run_cmd(self, cmd):
    time.sleep(0.1)
    log.d(u'執(zhí)行命令:%s' % cmd)
    execute = os.popen(cmd, 'r')
    result = execute.read()
    log.w(result)
    return result

# 運(yùn)行 adb shell 命令
def run_adb_shell(self, cmd):
    adb_cmd = 'adb shell %s' % cmd
    result = self.run_cmd(adb_cmd)
    return result

模擬操作

這里使用的 Mumu 模擬器來(lái)充當(dāng)?shù)氖謾C(jī),首頁(yè)我們需要使用 adb 連接我們的設(shè)備鬓椭,命令: adb connect host:prot颠猴。

# adb 連接
def connect(self):
    for x in range(1, 10):
        result = self.run_cmd('adb connect %s:%s' % (HOST, PORT))
        if 'already connected' in result:
            log.d(u'已連接')
            break
        elif 'connected' in result:
            log.d(u'連接成功')
            break
        time.sleep(1)
        log.d(u'adb 正在重試連接 %s ...' % x)

連接設(shè)備之后就是我們的模擬點(diǎn)擊和滑動(dòng)操作啦

# 點(diǎn)擊屏幕 point
def click(self, point, delay=2):
    # 延遲執(zhí)行,避免被不確定因素影響
    time.sleep(delay)
    # 執(zhí)行點(diǎn)擊命令
    self.run_adb_shell(r'input tap %s %s' % (point.x, point.y))

# 滑動(dòng)
def swipe(self, point_start, point_end, delay=1):
    # 延遲執(zhí)行小染,避免被不確定因素影響
    time.sleep(delay)
    # 執(zhí)行點(diǎn)擊命令
    self.run_adb_shell(r'input swipe %s %s %s %s' % (point_start.x, point_start.y, point_end.x, point_end.y))

按鈕在屏幕中的位置查找

查找按鈕的坐標(biāo)位置翘瓮,是整個(gè)輔助軟件的重中之重,它直接關(guān)系到了整個(gè)軟件的可用性裤翩。這里采用了圖像的模版匹配方法资盅,也就是將我們的目標(biāo)按鈕圖片與游戲當(dāng)前的圖像進(jìn)行匹配,找一個(gè)重合率最高的位置踊赠。

安裝 python-opencv

pip install python-opencv

游戲畫面截圖

# 截屏
def screen_shot(self):
    self.run_adb_shell(r'screencap -p /sdcard/screen.png')
    self.run_cmd(r'adb pull /sdcard/screen.png')

按鈕位置匹配

# 匹配圖片
def match_img(self, origin_uri, target_uri, threshold=0.9):
    methods = ['TM_CCOEFF', 'TM_CCOEFF_NORMED', 'TM_CCORR', 'TM_CCORR_NORMED', 'TM_SQDIFF', 'TM_SQDIFF_NORMED']
    result = self._match_img(origin_uri, target_uri, methods[3], threshold)
    return result

# 匹配圖片
def _match_img(self, origin_uri, target_uri, method, threshold=0.9):
    origin_img = cv2.imread(origin_uri, 0)
    target_img = cv2.imread(target_uri, 0)
    method = eval('cv2.%s' % method)
    w, h = target_img.shape[::-1]

    origin_img_copy = origin_img.copy()
    res = cv2.matchTemplate(origin_img_copy, target_img, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
    log.d(u"最小匹配值:%s  最大匹配值:%s" % (min_val, max_val))

    if max_val < threshold:
        log.d(u'未查詢到配置位置')
        return None

    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)
    return Box(top_left[0], top_left[1], bottom_right[0], bottom_right[1])

這樣我們就可以通過(guò) match_img 方法找到我們需要的按鈕位置呵扛,并進(jìn)而使用 click 方法來(lái)模擬點(diǎn)擊屏幕,從而擁有使用輔助軟件消滅重復(fù)操作的能力筐带。

完整代碼

class.py

"""
點(diǎn)
"""
class Point:

    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y


"""
方框
"""
class Box:

    def __init__(self, left=0, top=0, right=0, bottom=0):
        self.top = top
        self.left = left
        self.bottom = bottom
        self.right = right

adb.py

import os
import time
import log

HOST = '127.0.0.1'
PORT = 7555


class ADB:

    def __init__(self):
        self.connect()

    # adb 連接
    def connect(self):
        for x in range(1, 10):
            result = self.run_cmd('adb connect %s:%s' % (HOST, PORT))
            if 'already connected' in result:
                log.d(u'已連接')
                break
            elif 'connected' in result:
                log.d(u'連接成功')
                break
            time.sleep(1)
            log.d(u'adb 正在重試連接 %s ...' % x)

    # adb 斷開(kāi)連接
    def disconnect(self):
        result = self.run_cmd('adb disconnect %s:%s' % (HOST, PORT))
        log.d(u'adb 已斷開(kāi)連接')

    # 運(yùn)行命令
    def run_cmd(self, cmd):
        time.sleep(0.1)
        log.d(u'執(zhí)行命令:%s' % cmd)
        execute = os.popen(cmd, 'r')
        result = execute.read()
        log.w(result)
        return result

    # 運(yùn)行 adb shell 命令
    def run_adb_shell(self, cmd):
        adb_cmd = 'adb shell %s' % cmd
        result = self.run_cmd(adb_cmd)
        return result

    # 點(diǎn)擊屏幕 point
    def click(self, point, delay=2):
        # 延遲執(zhí)行今穿,避免被不確定因素影響
        time.sleep(delay)
        # 執(zhí)行點(diǎn)擊命令
        self.run_adb_shell(r'input tap %s %s' % (point.x, point.y))

    # 滑動(dòng)
    def swipe(self, point_start, point_end, delay=1):
        # 延遲執(zhí)行,避免被不確定因素影響
        time.sleep(delay)
        # 執(zhí)行點(diǎn)擊命令
        self.run_adb_shell(r'input swipe %s %s %s %s' % (point_start.x, point_start.y, point_end.x, point_end.y))

    # 截屏
    def screen_shot(self):
        self.run_adb_shell(r'screencap -p /sdcard/screen.png')
        self.run_cmd(r'adb pull /sdcard/screen.png')

image.py

import log
from clazz import Box
import cv2
import os
import numpy as np


class ImgHelper:

    def __init__(self):
        pass

    # 獲取灰度值
    def get_gray_level(self, img_uri, point):
        img = cv2.imread(img_uri)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        log.d('點(diǎn)(%s,%s)灰度值為:%s' % (point.x, point.y, gray[point.y][point.x]))
        return gray[point.y][point.x]

    # 圖片裁剪
    def cut(self, origin_uri, box, save_name):
        origin_img = cv2.imread(origin_uri)
        cropped = origin_img[box.top:box.bottom, box.left:box.right]
        save_dir = 'tmp'
        if not os.path.exists(save_dir):
            os.makedirs(save_dir)
        save_path = save_dir + os.path.sep + save_name
        cv2.imwrite(save_path, cropped)
        return save_path

    # 匹配圖片
    def match_img(self, origin_uri, target_uri, threshold=0.9):
        methods = ['TM_CCOEFF', 'TM_CCOEFF_NORMED', 'TM_CCORR', 'TM_CCORR_NORMED', 'TM_SQDIFF', 'TM_SQDIFF_NORMED']
        result = self._match_img(origin_uri, target_uri, methods[3], threshold)
        return result

    # 匹配圖片
    def _match_img(self, origin_uri, target_uri, method, threshold=0.9):
        origin_img = cv2.imread(origin_uri, 0)
        target_img = cv2.imread(target_uri, 0)
        method = eval('cv2.%s' % method)
        w, h = target_img.shape[::-1]

        origin_img_copy = origin_img.copy()
        res = cv2.matchTemplate(origin_img_copy, target_img, method)
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
        log.d(u"最小匹配值:%s  最大匹配值:%s" % (min_val, max_val))

        if max_val < threshold:
            log.d(u'未查詢到配置位置')
            return None

        if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
            top_left = min_loc
        else:
            top_left = max_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)
        return Box(top_left[0], top_left[1], bottom_right[0], bottom_right[1])

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末伦籍,一起剝皮案震驚了整個(gè)濱河市蓝晒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌帖鸦,老刑警劉巖芝薇,帶你破解...
    沈念sama閱讀 212,383評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異作儿,居然都是意外死亡洛二,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,522評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)晾嘶,“玉大人妓雾,你說(shuō)我怎么就攤上這事”淝埽” “怎么了君珠?”我有些...
    開(kāi)封第一講書人閱讀 157,852評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵寝志,是天一觀的道長(zhǎng)娇斑。 經(jīng)常有香客問(wèn)我,道長(zhǎng)材部,這世上最難降的妖魔是什么毫缆? 我笑而不...
    開(kāi)封第一講書人閱讀 56,621評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮乐导,結(jié)果婚禮上苦丁,老公的妹妹穿的比我還像新娘。我一直安慰自己物臂,他們只是感情好旺拉,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,741評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著棵磷,像睡著了一般蛾狗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上仪媒,一...
    開(kāi)封第一講書人閱讀 49,929評(píng)論 1 290
  • 那天沉桌,我揣著相機(jī)與錄音,去河邊找鬼算吩。 笑死留凭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的偎巢。 我是一名探鬼主播蔼夜,決...
    沈念sama閱讀 39,076評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起惠遏,我...
    開(kāi)封第一講書人閱讀 37,803評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤闹获,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后该默,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,265評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,582評(píng)論 2 327
  • 正文 我和宋清朗相戀三年梧躺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,716評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡掠哥,死狀恐怖巩踏,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情续搀,我是刑警寧澤塞琼,帶...
    沈念sama閱讀 34,395評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站禁舷,受9級(jí)特大地震影響彪杉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牵咙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,039評(píng)論 3 316
  • 文/蒙蒙 一派近、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洁桌,春花似錦渴丸、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,798評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至吠谢,卻和暖如春土童,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背囊卜。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,027評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工娜扇, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人栅组。 一個(gè)月前我還...
    沈念sama閱讀 46,488評(píng)論 2 361
  • 正文 我出身青樓雀瓢,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親玉掸。 傳聞我的和親對(duì)象是個(gè)殘疾皇子刃麸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,612評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容