V3版本回顾
在V3版本中,实现了无人机的自动索敌追踪、并在扫描到入侵者后将信息“广播”给其他无人机,其他巡逻状态的无人机可以协同该无人机进行“围剿”行为。
V4版本提升
- 为入侵者加入了可视化的血条,便于玩家更直观的看到入侵者血量减少的过程
- 加入了“任务系统”,现在玩家可以通过点击守卫区中的位置来指挥最近的巡逻状态无人机前往该位置
- 将无人机的move方法内部的庞大代码分成几个小的方法,并通过不同的限定条件来调用不同的方法。同时为了便于后期功能的添加,现在将无人机移动方案的限定条件改为由状态码的不同来决定无人机的移动方式。
V4版本的具体实现
可视化血条的添加
- 思路:只需要在入侵者的正上方画出一个矩形边框和一个实心矩形即可,通过改变实心矩形的宽度来达到显示血量的效果
- 具体实现Color color3 =new Color(0, 0, 0);
g.setColor(color3);
g.drawRect(x, y–5, size, 6);
Color color4 =new Color(124, 244, 21);
g.setColor(color4);
g.fillRect(x+1,y–4,(int) (blood/200.0*(size–1)),5);
任务系统的添加
- 思路及代码:
import java.awt.*;
public class Task {
int x;
int y;
int size=10;
int state;//0未被领取的任务;1被领取的任务;2已完成的任务
public Task(int state, int x, int y) {
this.state = state;
this.x = x;
this.y = y;
}
public void draw(Graphics g) {
g.setColor(Color.yellow);
g.fillRect(x–size/2,y–size/2,size,size);
}
}
int x=e.getX();
int y=e.getY();
Task task = new Task(0,x,y);
//对任务点进行判断,不得超出守卫区的范围
if(!(task.x<200+ task.size/2+50||task.x>1000– task.size/2–50||task.y<100+ task.size+50||task.y>700–task.size–50)) {
tasks.add(task);
}
}
import java.util.ArrayList;
public class TaskThread extends Thread {
ArrayList<Task> tasks;
ArrayList<Drone> drones;
@Override
public void run() {
for (; ; ) {
//System.out.println("进入TaskThread");
if (tasks != null) {
if (drones != null) {
for (int i = tasks.size() – 1; i >= 0; i—) {
Task task = tasks.get(i);
if (task.state != 1) {
int nearestIndex = –1;
double minDis = 1000.0;
for (int j = drones.size() – 1; j >= 0; j—) {
Drone drone = drones.get(j);
//找到没有领取任务的无人机(只有巡逻状态的无人机可以接任务)
if (drone.state == 0) {
//计算距离并找出最近的
double currentDis = caculateDis(task, drone);
if (currentDis <= minDis) {
minDis = currentDis;
nearestIndex = j;//把最近的无人机编号取出来
}
}
}
if (nearestIndex != –1) {
drones.get(nearestIndex).state = 2;//无人机状态设为正在执行任务
drones.get(nearestIndex).targetTask = task;
System.out.println("任务被领取");
task.state = 1;//任务状态设为已被领取
} else {
System.out.println("没有可指挥的无人机");
task.state = 0;
}
}
}
}
}
try {
Thread.sleep(16);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
double caculateDis(Task task, Drone drone) {
double dis = Math.sqrt((task.x – drone.x) * (task.x – drone.x) + (task.y – drone.y) * (task.y – drone.y) * 1.0);
return dis;
}
}
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
if(task.state==2){
tasks.remove(i);
}else{
task.draw(bg);
}
}
}
int dx = targetTask.x+targetTask.size/2–(x+scanSize/2);
int dy = targetTask.y+targetTask.size/2–(y+scanSize/2);
double distance = Math.sqrt(dx*dx+dy*dy);
if(distance>20){
huntingSpeedx=(int)(dx/distance*huntingSpeed);
huntingSpeedy=(int)(dy/distance*huntingSpeed);
}else{
huntingSpeedx=0;
huntingSpeedy=0;
System.out.println("完成任务");
state=0;//完成任务
targetTask.state=2;
targetTask=null; //任务完成后清除任务目标
}
//硬限位,防止出界
int newx = x+huntingSpeedx;
int newy = y+huntingSpeedy;
x = Math.max(200, Math.min(1000 – scanSize, newx));
y = Math.max(100, Math.min(700 – scanSize, newy));
}
无人机move方法的优化
- 在上个版本中,无人机的move方法过于庞大且判定条件没有统一化,导致后续再添加其他功能时非常麻烦,在V4版本中我改进了move方法,让move方法通过状态码来调用对应的移动方法来完成操作。代码实现如下public void move() {
if(state == 2){
moveToTask();
}else if(state == 1){
//先判断目标是否在守卫区外,若在守卫区外则不在追逐
if(target!=null){
if(target.x+target.size/2<200||target.x+target.size/2>900||target.y+target.size/2<100||target.y+target.size/2>600){
state=0;
target=null;
}else{
moveToHunt();
}
}else{
state=0;
}
}else if(state == 0){
moveToFind();
}
}
public void moveToTask(){
int dx = targetTask.x+targetTask.size/2–(x+scanSize/2);
int dy = targetTask.y+targetTask.size/2–(y+scanSize/2);
double distance = Math.sqrt(dx*dx+dy*dy);
if(distance>20){
huntingSpeedx=(int)(dx/distance*huntingSpeed);
huntingSpeedy=(int)(dy/distance*huntingSpeed);
}else{
huntingSpeedx=0;
huntingSpeedy=0;
System.out.println("完成任务");
state=0;//完成任务
targetTask.state=2;
targetTask=null; //任务完成后清除任务目标
}
//硬限位,防止出界
limit();
}
public void moveToHunt(){
int dx = target.x+target.size/2–(x+scanSize/2);
int dy = target.y+target.size/2–(y+scanSize/2);
double distance = Math.sqrt(dx*dx+dy*dy);
if(distance>20){
huntingSpeedx=(int)(dx/distance*huntingSpeed);
huntingSpeedy=(int)(dy/distance*huntingSpeed);
}else{
huntingSpeedx=0;
huntingSpeedy=0;
}
//硬限位,防止出界
limit();
}
public void moveToFind(){
x += speedx;
y += speedy;
if (x < 200 || x > 900) {
speedx = –speedx;
}
if (y < 100 || y > 600) {
speedy = –speedy;
}
}
public void limit(){
int newx = x+huntingSpeedx;
int newy = y+huntingSpeedy;
x = Math.max(200, Math.min(1000 – scanSize, newx));
y = Math.max(100, Math.min(700 – scanSize, newy));
}
V4版本完整代码如下
package com.nwu.by0129.droneV4;
import java.awt.*;
import java.awt.image.BufferedImage;
public class Drone {
int x;
int y;
int stateSize = 16;
int scanSize = 100;
int size = 30;
int state =0;// 0巡逻中;1追踪中;2执行任务中
int speedx;
int speedy;
int huntingSpeedx;
int huntingSpeedy;
int rSquare=scanSize*scanSize/4;
int huntingSpeed = 4;//设置追逐速度
Intruder target = null;
Task targetTask = null;
public Drone(int x, int y, int speedx, int speedy) {
this.x = x;
this.y = y;
this.speedx = speedx;
this.speedy = speedy;
}
public void drawDrone(Graphics g) {
Color color1 = new Color(52, 146, 241, 75);
g.setColor(color1);
g.fillOval(x, y, scanSize, scanSize);
Color color2 = new Color(111, 200, 34);
g.setColor(color2);
g.fillOval(x + 35, y + 35, size, size);
Color color3 = new Color(243, 82, 82);
if(state == 0){
g.setColor(color3);
g.fillOval(x + 42, y + 42, stateSize, stateSize);
}else if(state == 1||state == 2){
Color color4 = new Color (5, 25, 246);
g.setColor(color4);
g.fillOval(x + 42, y + 42, stateSize, stateSize);
}
}
public void move() {
if(state == 2){
moveToTask();
}else if(state == 1){
//先判断目标是否在守卫区外,若在守卫区外则不在追逐
if(target!=null){
if(target.x+target.size/2<200||target.x+target.size/2>900||target.y+target.size/2<100||target.y+target.size/2>600){
state=0;
target=null;
}else{
moveToHunt();
}
}else{
state=0;
}
}else if(state == 0){
moveToFind();
}
}
public ScanResult scan(int x, int y, int size, BufferedImage img) {
int distanceSquare;
for (int i = x; i < x + size; i++) {
for (int j = y; j < y + size; j++) {
int roundx=x + scanSize / 2;
int roundy=y + scanSize / 2;
distanceSquare = (roundx – i) * (roundx – i) + (roundy – j) * (roundy – j);
if (distanceSquare < rSquare){
try{
int colorNum = img.getRGB(i,j);
Color color = new Color(colorNum);
Color targetColor1 = new Color(213, 15, 15);
Color targetColor2 = new Color(0,0,0);
if(isIntruderColor(color,targetColor1,25)||isIntruderColor(color,targetColor2,30)){
//System.out.println("扫描到入侵者");
ScanResult sr= new ScanResult(i,j);
return sr ;
}
}catch(Exception e){
// 处理超出图像范围的情况
continue;
}
}
}
}
return null;
}
//颜色判断方法
public boolean isIntruderColor (Color c1, Color c2, int tolerance){
return Math.abs(c1.getRed()–c2.getRed()) <= tolerance &&
Math.abs(c1.getGreen()–c2.getGreen()) <= tolerance &&
Math.abs(c1.getBlue()–c2.getBlue()) <= tolerance;
}
public void moveToTask(){
int dx = targetTask.x+targetTask.size/2–(x+scanSize/2);
int dy = targetTask.y+targetTask.size/2–(y+scanSize/2);
double distance = Math.sqrt(dx*dx+dy*dy);
if(distance>20){
huntingSpeedx=(int)(dx/distance*huntingSpeed);
huntingSpeedy=(int)(dy/distance*huntingSpeed);
}else{
huntingSpeedx=0;
huntingSpeedy=0;
System.out.println("完成任务");
state=0;//完成任务
targetTask.state=2;
targetTask=null; //任务完成后清除任务目标
}
//硬限位,防止出界
limit();
}
public void moveToHunt(){
int dx = target.x+target.size/2–(x+scanSize/2);
int dy = target.y+target.size/2–(y+scanSize/2);
double distance = Math.sqrt(dx*dx+dy*dy);
if(distance>20){
huntingSpeedx=(int)(dx/distance*huntingSpeed);
huntingSpeedy=(int)(dy/distance*huntingSpeed);
}else{
huntingSpeedx=0;
huntingSpeedy=0;
}
//硬限位,防止出界
limit();
}
public void moveToFind(){
x += speedx;
y += speedy;
if (x < 200 || x > 900) {
speedx = –speedx;
}
if (y < 100 || y > 600) {
speedy = –speedy;
}
}
public void limit(){
int newx = x+huntingSpeedx;
int newy = y+huntingSpeedy;
x = Math.max(200, Math.min(1000 – scanSize, newx));
y = Math.max(100, Math.min(700 – scanSize, newy));
}
}
package com.nwu.by0129.droneV4;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Random;
public class DroneListener implements ActionListener, MouseListener {
Random rand = new Random();
ArrayList<Drone> drones;
ArrayList<Intruder> intruders;
ArrayList<Task> tasks ;
@Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals("生产无人机")) {
int x = rand.nextInt(700)+200;
int y = rand.nextInt(500)+100;
int speedx = rand.nextInt(5)–2;
while(speedx==0) {
speedx = rand.nextInt(5)–2;
}
int speedy = rand.nextInt(5)–2;
while(speedy==0) {
speedy = rand.nextInt(5)–2;
}
Drone drone = new Drone(x,y,speedx,speedy);
drones.add(drone);
}else if(e.getActionCommand().equals("生产入侵者")) {
int x = rand.nextInt(1160);
int y = rand.nextInt(760);
while(true){
if(y<60||y>700||x<160||x>1000) {
break;
}
x = rand.nextInt(1160);
y = rand.nextInt(760);
}
int speedx = rand.nextInt(5)–2;
while(speedx==0) {
speedx = rand.nextInt(5)–2;
}
int speedy = rand.nextInt(5)–2;
while(speedy==0) {
speedy = rand.nextInt(5)–2;
}
Intruder intruder = new Intruder(x,y,speedx,speedy);
intruders.add(intruder);
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
int x=e.getX();
int y=e.getY();
Task task = new Task(0,x,y);
//对任务点进行判断
if(!(task.x<200+ task.size/2+50||task.x>1000– task.size/2–50||task.y<100+ task.size+50||task.y>700–task.size–50)) {
tasks.add(task);
}
/*for (int i = 0; i < tasks.size(); i++) {
System.out.println(tasks.get(i).x);
}*/
//System.out.println(tasks.size());
//System.out.println("task添加了");
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
package com.nwu.by0129.droneV4;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
public class DroneThread extends Thread {
Graphics g;
ArrayList<Drone> drones;
ArrayList<Intruder> intruders;
ArrayList<Intruder> foundedIntruders = new ArrayList<>(); //将所有被扫描到的入侵者存入这里
ArrayList<Task> tasks;
public DroneThread(Graphics g) {
this.g = g;
}
@Override
public void run() {
for (; ; ) {
BufferedImage image = new BufferedImage(1200, 800, 2);
Graphics bg = image.getGraphics();
bg.setColor(Color.white);
bg.fillRect(0, 0, 1200, 800);
bg.setColor(Color.red);
bg.drawRect(200, 100, 800, 600);
if (drones != null) {
for (int i = 0; i < drones.size(); i++) {
Drone drone = drones.get(i);
drone.drawDrone(bg);
drone.move();
}
}
if (intruders != null) {
for (int i = 0; i < intruders.size(); i++) {
Intruder intruder = intruders.get(i);
intruder.drawIntruder(bg);
intruder.move();
}
}
//在这里用bg画笔画出任务点
if (tasks != null) {
//System.out.println("进入if");
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
if(task.state==2){
tasks.remove(i);
}else{
task.draw(bg);
}
}
}
if (drones.size() > 0) {
for (int k = 0; k < drones.size(); k++) {
Drone drone = drones.get(k);
ScanResult sr = drone.scan(drone.x, drone.y, drone.scanSize, image);
if (sr != null) {//如果扫到目标
if(drone.state==2){
continue;//正在执行任务中就不处理入侵者
}
Intruder intruder = getTargetIntruder(sr.targetX, sr.targetY);//通过坐标把这个目标找出来
if (intruder != null) {//找到目标后
drone.target = intruder;//把自己的目标设置为这个
drone.state = 1;
foundedIntruders.add(intruder);//把这个目标传入动态数组
}
} else {//如果没扫描到目标
if(drone.state==0 && drone.target==null){
if (foundedIntruders != null) {//看看动态数组里面有没有别人发现的目标
for (int i = foundedIntruders.size() – 1; i >= 0; i—) {
Intruder intruder = foundedIntruders.get(i);
if (intruder != null) {
if (intruder.blood <= 0 || !isInRange(intruder)) {//先判断这个目标还是否存在或在范围内
foundedIntruders.remove(i);//删掉死亡的或者逃跑的
} else {
drone.target = intruder;//把自己的目标设置成这个目标
drone.state = 1;
}
}
}
}
}
}
}
}
if (intruders.size() > 0) {
//如果该入侵者被某个无人机列为目标,就进行扣血
for (int k = 0; k < intruders.size(); k++) {
Intruder intruder = intruders.get(k);
for (int i = 0; i < drones.size(); i++) {
if (intruder == drones.get(i).target) {
intruder.underAttack();
System.out.println("第" + k + "个入侵者被扫到,血量剩余:" + intruder.blood);
if (intruder.blood <= 0 || !isInRange(intruder)) {//消灭入侵者
intruders.remove(k);
for (Drone drone : drones) {
if (drone.target == intruder) {
drone.target = null;
if(drone.state==1){
drone.state = 0;
}
}
}
}
}
}
}
}
g.drawImage(image, 0, 0, null);
try {
Thread.sleep(16);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public Intruder getTargetIntruder(int scannedI, int scannedJ) {
for (int k = 0; k < intruders.size(); k++) {
Intruder intruder = intruders.get(k);
int distanceSquare;
for (int i = intruder.x; i < intruder.x + intruder.size; i++) {
for (int j = intruder.y; j < intruder.y + intruder.size; j++) {
int roundx = intruder.x + intruder.size / 2;
int roundy = intruder.y + intruder.size / 2;
distanceSquare = (roundx – i) * (roundx – i) + (roundy – j) * (roundy – j);
if (distanceSquare < intruder.rSquare) {
if (i == scannedI && j == scannedJ) {
return intruder;
}
}
}
}
}
return null;
}
//判断入侵者是否在守卫区内
public boolean isInRange(Intruder intruder) {
if (intruder.x < 200 + intruder.size || intruder.x > 1000 – intruder.size || intruder.y < 100 + intruder.size || intruder.y > 700 – intruder.size) {
return false;
} else {
return true;
}
}
}
package com.nwu.by0129.droneV4;
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class DroneUI extends JFrame {
ArrayList<Drone> drones=new ArrayList<>();
ArrayList<Intruder> intruders=new ArrayList<>();
ArrayList<Task> tasks=new ArrayList<>();
public DroneUI() {
setTitle("智能无人机平台4.0");
setSize(1200,900);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
JButton button1 = new JButton("生产无人机");
JButton button2 = new JButton("生产入侵者");
JPanel panel = new JPanel();
panel.setBackground(Color.lightGray);
panel.add(button1);
panel.add(button2);
add(panel,BorderLayout.SOUTH);
setVisible(true);
Graphics g=this.getGraphics();
//创建按钮监听器
DroneListener droneListener = new DroneListener();
//在创建监听器后立刻为集合赋值,让集合真正可用
droneListener.drones=drones;
droneListener.intruders=intruders;
droneListener.tasks=tasks;
button1.addActionListener(droneListener);
button2.addActionListener(droneListener);
//创建鼠标监听器
addMouseListener(droneListener);
//创建无人机线程
DroneThread droneThread = new DroneThread(g);
//创建任务线程
TaskThread taskThread = new TaskThread();
//将共享内存的数组传入到两个线程中
droneThread.drones=drones;
droneThread.intruders=intruders;
droneThread.tasks=tasks;//要先将DroneUI类中的tasks传入给droneThread,这样才能在执行DroneThread的时候保证此时的tasks不是null
taskThread.drones=drones;
taskThread.tasks=tasks;
droneThread.start();
taskThread.start();
}
@Override
public void paint(Graphics g) {
super.paint(g);
}
public static void main(String[] args) {
new DroneUI();
}
}
package com.nwu.by0129.droneV4;
import java.awt.*;
public class Intruder {
int x;
int y;
int speedx;
int speedy;
int size=30;
int blood=200;
int rSquare=size*size/4;
public Intruder(int x, int y, int speedx, int speedy) {
this.x = x;
this.y = y;
this.speedx = speedx;
this.speedy = speedy;
}
public void drawIntruder(Graphics g) {
if (blood>0){
Color color1 =new Color(0, 0, 0);
g.setColor(color1);
g.fillOval(x, y, size, size);
Color color2 =new Color(213, 15, 15);
g.setColor(color2);
g.fillOval(x+5, y+5, size–10, size–10);
Color color3 =new Color(0, 0, 0);
g.setColor(color3);
g.drawRect(x, y–5, size, 6);
Color color4 =new Color(124, 244, 21);
g.setColor(color4);
g.fillRect(x+1,y–4,(int) (blood/200.0*(size–1)),5);
}
}
public void move(){
x+=speedx;
y+=speedy;
if(x<=0||x>=1160){
speedx=–speedx;
}
if(y<=0||y>=760){
speedy=–speedy;
}
}
public void underAttack(){
this.blood—;
}
}
package com.nwu.by0129.droneV4;
public class ScanResult{
int targetX;
int targetY;
public ScanResult(int targetX,int targetY) {
this.targetY = targetY;
this.targetX = targetX;
}
}
package com.nwu.by0129.droneV4;
import java.awt.*;
public class Task {
int x;
int y;
int size=10;
int state;//0未被领取的任务;1被领取的任务;2已完成的任务
public Task(int state, int x, int y) {
this.state = state;
this.x = x;
this.y = y;
}
public void draw(Graphics g) {
g.setColor(Color.yellow);
g.fillRect(x–size/2,y–size/2,size,size);
}
}
package com.nwu.by0129.droneV4;
import java.util.ArrayList;
public class TaskThread extends Thread {
ArrayList<Task> tasks;
ArrayList<Drone> drones;
@Override
public void run() {
for (; ; ) {
//System.out.println("进入TaskThread");
if (tasks != null) {
if (drones != null) {
for (int i = tasks.size() – 1; i >= 0; i—) {
Task task = tasks.get(i);
if (task.state != 1) {
int nearestIndex = –1;
double minDis = 1000.0;
for (int j = drones.size() – 1; j >= 0; j—) {
Drone drone = drones.get(j);
//找到没有领取任务的无人机(只有巡逻状态的无人机可以接任务)
if (drone.state == 0) {
//计算距离并找出最近的
double currentDis = caculateDis(task, drone);
if (currentDis <= minDis) {
minDis = currentDis;
nearestIndex = j;//把最近的无人机编号取出来
}
}
}
if (nearestIndex != –1) {
drones.get(nearestIndex).state = 2;//无人机状态设为正在执行任务
drones.get(nearestIndex).targetTask = task;
System.out.println("任务被领取");
task.state = 1;//任务状态设为已被领取
} else {
System.out.println("没有可指挥的无人机");
task.state = 0;
}
}
}
}
}
try {
Thread.sleep(16);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
double caculateDis(Task task, Drone drone) {
double dis = Math.sqrt((task.x – drone.x) * (task.x – drone.x) + (task.y – drone.y) * (task.y – drone.y) * 1.0);
return dis;
}
}
运行结果截图及视频演示


智能无人机V4视频演示
总结
在本次V4版本的开发中,我加深了对于面向对象的理解,对于大部分重复的代码,我们可以通过一个方法来将其封装起来,重复调用即可;同时我也认识到了代码前期设计的重要性,前期的设计要规范化,要为之后的功能添加留出充足的拓展空间。
网硕互联帮助中心





评论前必须登录!
注册