美文网首页
手游辅助软件开发

手游辅助软件开发

作者: xiaobu | 来源:发表于2019-03-01 16:56 被阅读0次

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

    思路

    说干就干,这就开启了咱的辅助开发之路。
    说到开发,思路最重要,只有理清了思路,才能知道代码要怎么写。我们玩游戏主要的操作就是:找到相应的按钮,然后点击它。从这里可以看到两个关键点 点击屏幕查找按钮坐标位置,那我们怎么实现它呢。这就要用到 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])
    
    

    相关文章

      网友评论

          本文标题:手游辅助软件开发

          本文链接:https://www.haomeiwen.com/subject/qmnduqtx.html