跳到主要内容智能无人机平台 V4 版本功能升级与实现 | 极客日志Javajava算法
智能无人机平台 V4 版本功能升级与实现
智能无人机平台 V4 版本的更新内容。主要改进包括为入侵者添加可视化血条,新增任务系统允许玩家指挥无人机前往指定位置,以及重构无人机移动方法使其基于状态码调用不同逻辑。文章提供了完整的 Java 代码实现,涵盖无人机、入侵者、任务管理及线程控制等核心模块。
无尘5 浏览 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);
任务系统的添加
思路及代码:
在上个版本中,无人机的 move 方法过于庞大且判定条件没有统一化,导致后续再添加其他功能时非常麻烦。在 V4 版本中改进了 move 方法,让 move 方法通过状态码来调用对应的移动方法来完成操作。代码实现如下
public void move() {
if (state == 2) {
moveToTask();
} else if (state == 1) {
if (target != null) {
(target.x + target.size / < || target.x + target.size / > || target.y + target.size / < || target.y + target.size / > ) {
state = ;
target = ;
} {
moveToHunt();
}
} {
state = ;
}
} (state == ) {
moveToFind();
}
}
{
targetTask.x + targetTask.size / - (x + scanSize / );
targetTask.y + targetTask.size / - (y + scanSize / );
Math.sqrt(dx * dx + dy * dy);
(distance > ) {
huntingSpeedx = ()(dx / distance * huntingSpeed);
huntingSpeedy = ()(dy / distance * huntingSpeed);
} {
huntingSpeedx = ;
huntingSpeedy = ;
System.out.println();
state = ;
targetTask.state = ;
targetTask = ;
}
limit();
}
{
target.x + target.size / - (x + scanSize / );
target.y + target.size / - (y + scanSize / );
Math.sqrt(dx * dx + dy * dy);
(distance > ) {
huntingSpeedx = ()(dx / distance * huntingSpeed);
huntingSpeedy = ()(dy / distance * huntingSpeed);
} {
huntingSpeedx = ;
huntingSpeedy = ;
}
limit();
}
{
x += speedx;
y += speedy;
(x < || x > ) {
speedx = -speedx;
}
(y < || y > ) {
speedy = -speedy;
}
}
{
x + huntingSpeedx;
y + huntingSpeedy;
x = Math.max(, Math.min( - scanSize, newX));
y = Math.max(, Math.min( - scanSize, newY));
}
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
if
2
200
2
900
2
100
2
600
0
null
else
else
0
else
if
0
public
void
moveToTask
()
int
dx
=
2
2
int
dy
=
2
2
double
distance
=
if
20
int
int
else
0
0
"完成任务"
0
2
null
public
void
moveToHunt
()
int
dx
=
2
2
int
dy
=
2
2
double
distance
=
if
20
int
int
else
0
0
public
void
moveToFind
()
if
200
900
if
100
600
public
void
limit
()
int
newX
=
int
newY
=
200
1000
100
700
在 Drone 类中构建 moveToTask 方法,让最近的无人机在接到任务后计算速度并前往,逻辑与追踪入侵者类似,并在无人机中心与任务点距离小于 20 时,将状态设置为任务完成,并清空任务目标。
无人机 move 方法的优化
在 DroneThread 线程中画出任务点,若任务完成,则移除任务对象且不进行绘制。具体代码实现如下
if (tasks != null) {
for (int i = 0; i < tasks.size(); i++) {
Task task = tasks.get(i);
if (task.state == 2) {
tasks.remove(i);
} else {
task.draw(bg);
}
}
}
创建任务线程类 TaskThread,在类中重写 run 方法。在 run 方法中遍历无人机与任务数组,将未被分配的任务分配给距离最近且处于巡逻状态的无人机,任务被分配后将任务的状态设置为已被领取,若此时没有空闲的无人机,则输出提示。
package com.drone.v4;
import java.util.ArrayList;
public class TaskThread extends Thread {
ArrayList<Task> tasks;
ArrayList<Drone> drones;
@Override
public void run() {
for (;;) {
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 = calculateDis(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 calculateDis(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;
}
}
创建鼠标监听器,并在 DroneListener 类中加入鼠标监听器的相关内容。重写 mousePressed 方法,将鼠标点击的坐标作为参数构建 task 对象,使用共享内存的思路,并存入 tasks 动态数组,便于其他无人机领取任务。
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);
}
}
创建 Task 类,将玩家发布的任务封装起来,类中包括该任务点的坐标、状态属性以及可视化的绘制方法。
package com.drone.v4;
import java.awt.*;
public class Task {
int x;
int y;
int size = 10;
int state;
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);
}
}
V4 版本完整代码如下
package com.drone.v4;
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;
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)) {
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.drone.v4;
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);
}
}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
}
package com.drone.v4;
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();
}
}
if (tasks != null) {
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.drone.v4;
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;
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.drone.v4;
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.drone.v4;
public class ScanResult {
int targetX;
int targetY;
public ScanResult(int targetX, int targetY) {
this.targetY = targetY;
this.targetX = targetX;
}
}
package com.drone.v4;
import java.awt.*;
public class Task {
int x;
int y;
int size = 10;
int state;
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.drone.v4;
import java.util.ArrayList;
public class TaskThread extends Thread {
ArrayList<Task> tasks;
ArrayList<Drone> drones;
@Override
public void run() {
for (;;) {
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 = calculateDis(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 calculateDis(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 版本开发重点在于面向对象的设计规范与代码复用。通过将重复逻辑封装为独立方法,提升了代码的可维护性。前期设计的规范化为后续功能扩展预留了充足空间,状态机模式有效管理了无人机的不同行为模式。