QT跳一跳外挂总结

qi.wei

发布于 2018.03.17 00:23 阅读 2835 评论 1

QT跳一跳外挂总结

 

文章分为以下几个部分:

1. 怎么通过电脑控制手机?

2. 第一版外挂(用直尺)

3. 第二版外挂(用鼠标点击)

4. 第三版外挂(自动)

5. 总结

 

 

 

怎么通过电脑控制手机?

          QT的程序是运行在PC端的,但是跳一跳游戏是要在手机端运行的,那么如何让PC端的程序对手机端进行操作呢?所以首先介绍一下,这个外挂所用到的一个很关键的工具—adb工具包。这个工具由谷歌公司提供,它的全称为Android Debug Bridg,借助这个工具,可以通过命令的方式在电脑上对手机进行操作。

下面说几条写程序要用到的命令:

 

1.截取屏幕保存为screen.png并将图片放在手机/sdcard目录下:

adb shell screencap -p /sdcard/screen.png    

 

2.将/sdcard目录下的screen.png上传到电脑(程序运行的当前目录):

adb pull /sdcard/screen.png

 

3.在(200,500)处按下手机屏幕,并在(200,500)处抬起,时长1秒:

adb shell input swipe 200 500 200 500 1000

 

 

 

             我们有了电脑到手机的桥梁,那么现在来说一下写跳一跳外挂的思路。

 

我的第一版外挂(用直尺)

             我们知道,跳一跳这个游戏操作非常简单,只需要按屏幕,根据按屏幕的时间长短来决定跳出去的位置。我们现在有了adb工具,那么只需要根据人与盒子之间的距离长短向手机发送一条按屏幕的指令,但是到底按多久?

          按屏幕的时间长短和距离是成正比的,虽然我们不知道按屏幕的时间长短具体是多少,但是我们可以获取到人与盒子之间的距离,得到这个距离乘上一个固定的值(这个值自己测试一下就能得到)就是按屏幕的时间。

          所以刚开始我用直尺去量小人到盒子中间的距离,然后输入距离,程序计算出按屏幕的时间,再向手机发送指令,人就会老老实实地跳到盒子上去。

代码示例:

#include <stdio.h>

#include <Windows.h>

#define RATE 4.1  //这个值自己测试得到

int main()
{

    double distance;

    double time;

    char str[100];

    while (true) {

         printf("请输入距离:");

         scanf("%f", &distance);

         time = distance * RATE;   //根据比率得到时间

         sprintf(str, "adb shell input swipe 200 500 200 500 %d", (int)time);   //生成命令字符串

         system(str); //执行命令

    }
    return 0;
}

通过这种方式,只要用尺子量精确了,那么跳的也是相当精确的,但是缺点就是非常累人,要手动测量,再把测量的值输入到程序,效率比较低。

 

 

 

我的第二版外挂(用鼠标点击)

             由于第一版效率太低,还要用直尺去测量,为了偷懒所以想到了一个更好的办法。我们可以通过获取截图的方式把跳一跳的游戏界面通过图片形式上传到电脑,然后通过QT显示出这张图的图像并对这张图进行操作。

          原本我们用直尺去量人和盒子的距离,那么现在,只需要通过鼠标点击一下人,记录下人的坐标,然后点击一下盒子,记录下盒子的坐标,通过这两个点的坐标计算出这两个点的距离,这样通过两次点击就得到了人与盒子的距离,也不用手动测量和手动输入显然是省了很多力气。

示例代码(部分):

/*
//鼠标点击的点结构体
struct PointClick{
    float x;
    float y;
};
*/
//获取鼠标点击事件
void Widget::mouseReleaseEvent(QMouseEvent *event)
{
    char str[100];

    //如果第一个点还未点击
    if(!FirstIsClick){
        point_click.x=x;
        point_click.y=y;
        FirstIsClick=true;
    }
    else{
        //计算两个点的距离
        double distance=sqrt((double)((x-point_click.x)*(x-point_click.x)+(y-point_click.y)*(y-point_click.y)));
        //得出时间
        double time=distance*4.1;
        //生成命令字符串
        sprintf(str,"adb shell input swipe 200 500 200 500 %d",(int)time);
        system(str);    //执行命令

        Sleep(800);
        init();
        FirstIsClick=false;
    }

}
//初始化函数
void Widget::init()
{
    system("adb shell screencap -p /sdcard/screen.png");    //获取截图到手机
    system("adb pull /sdcard/screen.png");  //将截图上传到电脑

    //加载图片
    pixmap.load( "screen.png" );
    //测试手机像素为1280*720,所以显示的时候缩小两倍显示
    pixmap = pixmap.scaled(w_width/2, w_heigh/2,Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
    palette.setBrush(QPalette::Window, QBrush(pixmap));
    this->setPalette(palette);
}

             第二版相较于第一版确实是省了不少力气,但是,毕竟还是需要手动点击,需要人一直坐在电脑前,离了人不行。那么就有了跳一跳外挂的第三版——全自动跳一跳。

 

 

 

我的第三版外挂(自动)

             首先面临一个最重要的问题如何让程序完全脱离人工实现自动呢?那么需要让程序具备分析的能力。我们从“像素”这一个角度切入。

             不难发现跳一跳游戏的背景色和盒子或者小人的颜色差距是很大的,那么我们选一个点只可能是背景的点,获取这个像素的RGB并记录下来,然后对这张图的中间部分,只可能出现盒子和人的区域进行“地毯式”扫描,从上到下一行一行地逐个像素地获取颜色与背景色进行比较,如果颜色相似,那就证明是背景,那就继续向下扫描,直到出现一个与背景色差别很大的像素,那么这个点一定是将要跳的盒子的最顶点。

             根据这顶点的坐标,向右130像素然后沿Y轴由上向下扫描70个像素,如果某一个像素和背景色差别很大,那么这个点就是盒子的最右角,根据最盒子顶点的Y轴坐标和最右点的X轴坐标我们就可以确定盒子的中心坐标。

             得到了盒子的坐标,那么人的坐标怎么获得呢?我们可以看到人的颜色是固定的,那么我们就在中间区域自下向上一行一行地扫描,扫到和人的颜色相同的像素那么这个点就是人的底部的坐标。

             根据这两个坐标就可以计算出距离,从而也就可以得到按屏幕的时间了。循环此步骤,截图然后分析,然后执行分析后的数据。这就使得跳一跳外挂可以自动运行了。

示例代码:

autojump.h

#ifndef AUTOJUMP_H
#define AUTOJUMP_H
#include <QImage>
#include <QColor>


class AutoJump
{
public:
    AutoJump();
    void init();
    void work();
    bool colorRight(QColor color1,QColor color2,double diff);

    QImage bkImg;
    QColor stopColor;
    QColor bkColor;
    QColor nowbkColor;
    QColor peopleColor;
    int boxX;
    int boxY;
    int peopleX;
    int peopleY;
    int lastPeopleX;
    int lastPeopleY;
    float distance;
    float time;
    char str[100];
};

#endif // AUTOJUMP_H

 

autojump.cpp

#include "autojump.h"
#include <QDebug>
#include <windows.h>

AutoJump::AutoJump()
{
    peopleColor.setRgb(55,60,102);
    stopColor.setRgb(42,42,50);
    lastPeopleX = 0;
    lastPeopleY = 0;
}

void AutoJump::init()
{
    while(true){
        work();
    }

}

void AutoJump::work()
{
    qDebug()<<"获取截图......";
    //获取截图
    system("adb shell screencap -p /sdcard/screen.png");
    system("adb pull /sdcard/screen.png");

    //加载
    bkImg.load("screen.png");

    qDebug()<<"分析图片......";
    //获取背景色
    bkColor = bkImg.pixel(300,180);

    //判断是不是游戏结束,结束自动重启游戏
    if(colorRight(stopColor,bkColor,1))
    {
        qDebug()<<"游戏结束 ";
        system("adb shell input tap 355 1060");
        qDebug()<<"游戏启动 ";
        Sleep(3000);
        qDebug()<<"获取截图......";
        system("adb shell screencap -p /sdcard/screen.png");
        system("adb pull /sdcard/screen.png");
        bkImg.load("screen.png");
        bkColor = bkImg.pixel(300,180);
    }

    boxX = 0;
    boxY = 0;
    //获取盒子顶部的位置,确定x
    int topY = 0;
    for(int y = 200; y < 800; y+=2)
    {
        for(int x = 80; x < 650; x+=2)
        {
            nowbkColor = bkImg.pixel(x,y);
            if(!colorRight(bkColor,nowbkColor,6))
            {
                if(colorRight(peopleColor,nowbkColor,2)){
                    continue;
                }else{
                    boxX = x;
                    topY = y;
                    break;
                }
            }
        }
        if(boxX != 0)
        {
            break;
        }
    }
    qDebug()<<"盒子顶点坐标:"<< boxX <<","<< topY;

    //获取盒子右角的位置,确定y
    int rightX = 0;
    for(int x = boxX+130;x>boxX;x-=2 )
    {
        for(int y = topY;y <topY+70;y+=2)
        {
            nowbkColor = bkImg.pixel(x,y);
            if(!colorRight(bkColor,nowbkColor,6))
            {
                if(colorRight(peopleColor,nowbkColor,20)){
                    continue;
                }else{
                    rightX = x;
                    boxY = y;
                    break;
                }
            }
        }
        if(boxY != 0)
        {
            break;
        }
    }
    qDebug()<<"盒子右角坐标:"<< rightX <<","<< boxY;

    //获取人的位置
    peopleX = 0;
    peopleY = 0;
    for(int y = 750; y > 500; y-=2)
    {
        for(int x = 80; x < 650; x+=2)
        {
            nowbkColor = bkImg.pixel(x,y);
            if(colorRight(peopleColor,nowbkColor,0))
            {
                peopleX = x;
                peopleY = y;
                break;
            }
        }
        if(peopleX != 0)
        {
            break;

        }
    }
    peopleX += 5;
    qDebug()<<"人坐标:"<< peopleX <<","<< peopleY;

    //计算按屏幕时间
    distance=sqrt((boxX-peopleX)*(boxX-peopleX)+(boxY-peopleY)*(boxY-peopleY));
    time=distance*2.03;

    sprintf(str,"adb shell input swipe 200 500 200 500 %d",(int)time);
    system(str);
    Sleep(800);
    //system("pause");
}

bool AutoJump::colorRight(QColor color1,QColor color2,double diff)
{
    //判断一下这两个颜色rgb值在三维坐标中的位置差值
    if(sqrt(double((color1.red()-color2.red())+(color1.green()-color2.green())+(color1.blue()-color2.blue()))) <= diff)  
        return true;
    return false;
}
 

 

 

总结

       第一版就是用C语言写的,第二版第三版才是真正需要用到QT的,但是QT仅仅是一种方式,其他还有很多途径也有很多方法可以实现跳一跳外挂。这里只分享下思路。
       
            这三版跳一跳由简到难,但是人工干预也由多变无,想少干活就得多动脑筋。这三版连起来看,我感觉对于思路的梳理还是相当有帮助的。