1、起因
之前用Java中的Robot类写了一个自动化的游戏脚本,但是在其中一些脚本步骤中经常会遇到需要等待的事件。
比如使用背包中的某个物品,需要先打开背包,而利用脚本打开背包到背包界面被打开之间可能会因为网络或者游戏问题而有延迟,从而容易发生错误,如背包界面还没打开就点击了物品位置;
在之前一直都是利用等待的功能来避免这个的发生(即Robot.sleep(),但是由于网络与游戏的延迟不是固定的,这样子的效率不是很高,也会发生意想不到的错误。
2、思路
利用Java中的线程机制,在脚本执行到某个步骤A时,暂停脚本,判断此时游戏环境是否满足某个条件(背包界面是否打开),借以确定是否执行下一步骤B(点击使用物品)。
即创建一个额外的条件检查类CheckThread,继承并重写Thread类的run()方法,在run()方法中写检查某个条件是否满足的代码。
然后在脚本执行到某一步时,创建该类的实例化对象,调用start()方法启动该线程进行条件检查,并利用join(MaxTime)来保证在条件检查完成前,脚本后续的步骤不会被执行,即脚本暂停。
其中的MaxTime参数指定了该线程被允许的最大检查时间,超过这个时间还没检查条件满足则终止检查,以防一直处于脚本暂停阶段。
3、代码
辅助类PBean.java
保存某个屏幕像素点的位置、颜色信息;
import java.awt.Color;
public class PBean {
public PBean(){}
public PBean(int x,int y,int r,int g,int b){
this.setX(x);
this.setY(y);
this.setC(new Color(r,g,b));
}
int x = -1;
int y = -1;
Color c;
public Color getC() {
return c;
}
public void setC(Color c) {
this.c = c;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
条件检查类CheckThread.java
用于检查当前游戏环境中的条件是否满足(某点颜色是否为期望的颜色,如背包被打开时右上角的某个点应该是红色的);
import java.awt.AWTException;
import java.awt.Robot;
class CheckThread extends Thread
{
/* @PBean:期望的点;
* @flag:期望是否符合;
* 采用异或方式判断,如果期望与点不同,则继续等待。如果期望与点相同,则退出;
* */
PBean pb;
Boolean flag;
public void run() {
try
{
while(flag^(this.pb.c.equals(new Robot().getPixelColor(this.pb.getX(), this.pb.getY()))))
{
Thread.sleep((int)(1000*Math.random()));//每隔一定时间检测一下条件是否满足
}
} catch (InterruptedException | AWTException e) {e.printStackTrace();}
}
/*@检查是否符合条件的对外接口函数check()
* @PBean作为期望点,
* @flag作为期望,即要点符合条件还是不符合条件;
* @MaxTime作为线程等待最长时间,如果超过这个时间还没满足则自动退出,以防卡住;
* 然后调用线程执行代码判断是否符合条件,
* 期间主线程处于“暂停”状态;
* */
public void check(PBean pb,Boolean flag,int MaxTime){
CheckThread ct = new CheckThread();
ct.pb = pb;
ct.flag = flag;
ct.start();
try {ct.join(MaxTime);} //join(long MaxTime)指定了线程最长等待的时间;
catch (InterruptedException e) {e.printStackTrace();}
}
}
check()函数的参数解读
第一个参数:PBean pb为期望的点信息,即我希望该位置的点颜色是什么样的;
第二个参数:Boolean flag为期望判断,即我希望当前条件是符合还是不符号,以颜色为例,我是希望某点颜色是xxx还是希望它不是xxx;
第三个参数:int MaxTime为条件检查的最大超时,即超过了这个时间条件仍然不成立的话就跳出条件检查的线程,继续脚本步骤的执行,后续可以进行一些回退,或者重新执行脚本之类的异常处理,从而防止一直卡死在检查某个条件是否满足的线程中。
★条件判断中的异或判断问题
这一点是个人觉得比较好的一个条件判断方法,同时也加深了我对异或判断的理解;
在条件判断函数check()中传入了两个与判断相关的参数,一个辅助类PBean存储了期望的点信息,如我希望点(100,100)的颜色信息是xxx,则创建一个PBean对象并设置坐标为(100,100),颜色是xxx,然后传入;
另一个是判断期望,即我希望某点是满足这个颜色,还是希望它不满足;
假设run()条件检查中代码如下:(省略了异常处理代码)
public void run() {
Boolean colorMatch = flag^(this.pb.c.equals(new Robot().getPixelColor(this.pb.getX(), this.pb.getY())));
while(flag^colorMatch)
{
Thread.sleep(1000);
//此处为检查间隔,即每隔一段时间检查一下条件是否满足;
}
}
//当条件满足时
如上代码,flag指示我希望颜色满足还是不满足,colorMatch指示某点颜色是否符合传入的PBean期望的颜色;
在我希望颜色满足时(flag为true),且该点颜色确实满足时(colorMatch为true),则while的异或判断flag^colorMatch为假,跳出循环,即跳出了条件检查的线程,回到脚本中继续执行后续步骤。
这是因为异或判断时,不同为真,相同为假, 我是这么理解异或判断的:两边意见一致则是错的,意见不一致则是对的;
回到我们的脚本条件判断中,可以确定我们需要判断的是游戏条件是否和我们“意见一致”,当“意见不一致时”,while()中的条件判断为对,则while循环就会一直执行下去,进行条件的检查,直到我们“意见一致”时跳出循环。
4、实例:在脚本的某一步之后进行条件检查
下面的例子中,假设输出语句为脚本步骤,然后在执行步骤A后,我需要判断在(1398,277)该点的像素值是否为[231,68,44],如果满足这个条件,则继续后续步骤B,否则一直检查直到满足该条件为止或者超过所设置的3000毫秒检查时间为止。
CheckThread ct = new CheckThread();
System.out.println("=======执行步骤A!=======");
ct.check(new PBean(1398,277,231,68,44),true,3000);
System.out.println("=======执行步骤B!=======");
PBean的构造函数,
前两个参数为坐标XY值,后三个参数为颜色RGB值;
当然关于条件检查的check()函数可以自定义任意的条件检查,这里我是要检查某像素点颜色,所以传入一个PBean类的对象,如果要检查其他东西,则根据需求在CheckThread中进行修改,或者自定义一个新的辅助类,如PBean;