手游辅助软件开发

最近沉迷于一款老手游,每天都花费大量的时间、资源去完成游戏的任务。每天做着重复的动作,实在是浪费青春啊!作为一个程序猿怎么受的了,我们不就是为有解决重复动作而生的么!经过沉痛的反思,下决心我要写一个辅助软件去攻略这个游戏,从每天的重复动作中解脱出来...

思路

说干就干,这就开启了咱的辅助开发之路。
说到开发,思路最重要,只有理清了思路,才能知道代码要怎么写。我们玩游戏主要的操作就是:找到相应的按钮,然后点击它。从这里可以看到两个关键点 点击屏幕查找按钮坐标位置,那我们怎么实现它呢。这就要用到 Android 的开发工具 ADB,做 Android 开发的同学应该都知道,我们可以通过 ADB 工具模拟点击,滑动等一系列的动作。

运行命令

我们需要在我们的代码里运行 cmd,从而达到运行 adb 命令。

# 运行命令
def run_cmd(self, cmd):
    time.sleep(0.1)
    log.d(u'执行命令:%s' % cmd)
    execute = os.popen(cmd, 'r')
    result = execute.read()
    log.w(result)
    return result

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

模拟操作

这里使用的 Mumu 模拟器来充当的手机,首页我们需要使用 adb 连接我们的设备,命令: 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)

连接设备之后就是我们的模拟点击和滑动操作啦

# 点击屏幕 point
def click(self, point, delay=2):
    # 延迟执行,避免被不确定因素影响
    time.sleep(delay)
    # 执行点击命令
    self.run_adb_shell(r'input tap %s %s' % (point.x, point.y))

# 滑动
def swipe(self, point_start, point_end, delay=1):
    # 延迟执行,避免被不确定因素影响
    time.sleep(delay)
    # 执行点击命令
    self.run_adb_shell(r'input swipe %s %s %s %s' % (point_start.x, point_start.y, point_end.x, point_end.y))

按钮在屏幕中的位置查找

查找按钮的坐标位置,是整个辅助软件的重中之重,它直接关系到了整个软件的可用性。这里采用了图像的模版匹配方法,也就是将我们的目标按钮图片与游戏当前的图像进行匹配,找一个重合率最高的位置。

安装 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])

这样我们就可以通过 match_img 方法找到我们需要的按钮位置,并进而使用 click 方法来模拟点击屏幕,从而拥有使用辅助软件消灭重复操作的能力。

完整代码

class.py

"""
点
"""
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 断开连接
    def disconnect(self):
        result = self.run_cmd('adb disconnect %s:%s' % (HOST, PORT))
        log.d(u'adb 已断开连接')

    # 运行命令
    def run_cmd(self, cmd):
        time.sleep(0.1)
        log.d(u'执行命令:%s' % cmd)
        execute = os.popen(cmd, 'r')
        result = execute.read()
        log.w(result)
        return result

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

    # 点击屏幕 point
    def click(self, point, delay=2):
        # 延迟执行,避免被不确定因素影响
        time.sleep(delay)
        # 执行点击命令
        self.run_adb_shell(r'input tap %s %s' % (point.x, point.y))

    # 滑动
    def swipe(self, point_start, point_end, delay=1):
        # 延迟执行,避免被不确定因素影响
        time.sleep(delay)
        # 执行点击命令
        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('点(%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])

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容