该坦克游戏项目用了多线程进行画面更新和敌人生成,并涉及了地图绘制、碰撞检测、键盘控制等常见游戏开发功能。
1.TankUI:用户界面入口
- 创建了一个窗口(JFrame),设置大小、标题、关闭行为。
- 添加了一个用于绘图的 JPanel。
- 初始化键盘监听器 GameListener 并请求焦点以接收按键事件。
- 是整个游戏的启动类,通过 main() 方法运行。
package tankgame;
import javax.swing.*;
import java.awt.*;
public class TankUI {
public void initUI() {
//JFrame默认是BorderLayout
JFrame jf = new JFrame();
jf.setTitle("线程游戏");
jf.setSize(1200,1080);
jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jf.setLocationRelativeTo(null);
//面板对象
JPanel gamePanel = new JPanel();
jf.add(gamePanel, BorderLayout.CENTER);
jf.setVisible(true);
GameListener listener = new GameListener(gamePanel.getGraphics());
gamePanel.addKeyListener(listener);
gamePanel.requestFocus();
}
public static void main(String[] args) {
new TankUI().initUI();
}
}
2.GameListener:键盘输入监听器
- 实现 KeyListener 接口,处理玩家方向键输入(WASD)。
- 控制主坦克的移动速度(setSpeedX, setSpeedY)
package tankgame; import java.awt.*; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.ArrayList; public class GameListener implements KeyListener { private Graphics g; private TankGameThread gt; private MTank mTank; private int [] [] map; private ArrayList<EnemyTank> enemyTankList = new ArrayList<>(); public GameListener(Graphics g){ this.g = g; //0.空白 1 墙 2 砖 3 草 4 总部 map = new int[][]{{0,0,0,0,0,0,0,0,0,0,0,0}, {0,2,2,2,2,2,2,2,2,2,0}, {0,0,3,0,0,0,0,0,3,0,0}, {0,0,3,1,1,1,1,1,3,0,0}, {0,0,3,0,0,0,0,0,3,0,0}, {0,0,3,0,0,0,0,0,3,0,0}, {0,2,2,2,2,2,2,2,2,2,0}, {0,0,0,0,0,0,0,0,0,0,0}, {0,2,2,0,2,2,2,0,2,2,0}, {0,2,2,0,2,4,2,0,2,2,0} }; } public void keyPressed(KeyEvent e){ int key = e.getKeyCode(); System.out.println("key"); switch (key){ case KeyEvent.VK_SPACE: if(gt==null){ //创建玩家对象 mTank = new MTank(300,900,map); gt = new TankGameThread(mTank,g,map,enemyTankList); new Thread(gt).start(); //启动定时任务 TimeThread timeThread = new TimeThread(map,enemyTankList); new Thread((Runnable) timeThread).start(); } break; case KeyEvent.VK_A: mTank.setDirection(0); mTank.setSpeedX(-5); break; case KeyEvent.VK_D: mTank.setDirection(2); mTank.setSpeedX(5); break; case KeyEvent.VK_W: mTank.setDirection(1); mTank.setSpeedY(-5); break; case KeyEvent.VK_S: mTank.setDirection(3); mTank.setSpeedY(5); break; } } public void keyReleased(KeyEvent e){ int key = e.getKeyCode(); switch (key){ case KeyEvent.VK_A: mTank.setSpeedX(0); break; case KeyEvent.VK_D: mTank.setSpeedX(0); break; case KeyEvent.VK_W: mTank.setSpeedY(0); break; case KeyEvent.VK_S: mTank.setSpeedY(0); break; } } public void keyTyped(KeyEvent e){ } }
3.MTtank:玩家坦克类
- 继承自 ObjectMove,具备位置、速度、方向、大小等属性。
- 提供 drawMTank(Graphics g) 方法用于绘制坦克。
- 重写了 move() 方法,结合地图数据进行 碰撞检测。
- 使用不同方向的图片资源实现动画效果
- drawMTank(Graphics g):根据方向绘制坦克图像。
- move():根据速度更新坐标,若无碰撞则移动。
- collision(int x, int y):判断是否碰到障碍物(砖墙/铁墙)。
package tankgame;
import javax.swing.*;
import java.awt.*;
public class MTank extends ObjectMove{
private ImageIcon[] imageArr = new ImageIcon[4];
private int[][]map;
public MTank(int x, int y, int[][] map){
super(x,y);
this.map = map;
setSize(60);
setDirection(1);
//加载图片
for(int i = 0;i<4;i++){
imageArr[i] = new ImageIcon("src\\img\\tank\\tank_"+i+".png");
}
}
public void drawMTank(Graphics g){
switch (getDirection()){
case 0://左
g.drawImage(imageArr[0].getImage(),getX(),getY(),getSize(),getSize(),null);
break;
case 1://上
g.drawImage(imageArr[1].getImage(),getX(),getY(),getSize(),getSize(),null);
break;
case 2://右
g.drawImage(imageArr[2].getImage(),getX(),getY(),getSize(),getSize(),null);
break;
case 3://下
g.drawImage(imageArr[3].getImage(),getX(),getY(),getSize(),getSize(),null);
break;
}
move();
}
public void move(){
int nextX = getX() + getSpeedX();
int nextY = getY() + getSpeedY();
if(!collision(nextX,nextY)){
setX(nextX);
setY(nextY);
}
}
//碰撞检测
public boolean collision(int x,int y){
//计算坦克在地图中的位置
int mapX = x/100;
int mapY = y/100;
int endX = (x+getSize() - 1)/100;
int endY = (y+getSize() - 1)/100;
//边界
if (mapX < 0|| endX > map[0].length || mapY < 0 || endY > map.length){
return true;
}
for (int i = mapX; i <= endX; i++) {
for (int j = mapY; j <= endY; j++) {
if(map[j][i] == 2 || map[j][i] ==1){
return true;
}
}
}
return false;
}
}
4. EenmyTank:敌方坦克类
- 同样继承 ObjectMove,具备类似移动逻辑。
- 图片资源为不同方向的敌人坦克。
package tankgame; import javax.swing.*; import java.awt.*; public class EnemyTank extends ObjectMove { private ImageIcon[] imageArr = new ImageIcon[4]; public EnemyTank(int x, int y, int [][] map) { setX(x); setY(y); setSize(100); setSpeedY(5); setDirection(3); //加载图片 for (int i = 0; i < 4; i++) { imageArr[i] = new ImageIcon("image//rank" + i + ".png"); } } public void drawMTank(Graphics g){ switch (getDirection()){ case 0://左 g.drawImage(imageArr[0].getImage(),getX(),getY(),getSize(),getSize(),null); break; case 1://上 g.drawImage(imageArr[1].getImage(),getX(),getY(),getSize(),getSize(),null); break; case 2://右 g.drawImage(imageArr[2].getImage(),getX(),getY(),getSize(),getSize(),null); break; case 3://下 g.drawImage(imageArr[3].getImage(),getX(),getY(),getSize(),getSize(),null); break; } move(); } }
5.ObjectMove:移动对象基类
- 封装通用属性:x, y, speedX, speedY, size, direction。
- 提供基本的 move() 方法。
- 所有可移动对象(如坦克)都继承此类
package tankgame; public class ObjectMove { private int x,y; private int speedX,speedY; private int size; private int direction; public ObjectMove(int x,int y){ this.x = x; this.y = y; } public ObjectMove(){ } public void move(){ x += speedX; y += speedY; } public int getY() { return y; } public void setY(int y) { this.y = y; } public int getX() { return x; } public void setX(int x) { this.x = x; } public int getSpeedX() { return speedX; } public void setSpeedX(int speedX) { this.speedX = speedX; } public int getSpeedY() { return speedY; } public void setSpeedY(int speedY) { this.speedY = speedY; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public int getDirection() { return direction; } public void setDirection(int direction) { this.direction = direction; } }
6.TankGameThread:主线程逻辑(游戏循环)
- 实现 Runnable 接口,作为独立线程运行。
- 使用双缓冲技术避免画面闪烁。
- 每隔 40ms 清屏并重绘地图、主坦克、敌方坦克。
- 构造函数中加载地图资源(砖墙、铁墙、水、草丛)
- run():主循环,负责持续刷新画面。
- drawMap(Graphics g):根据二维数组 map 绘制地图元素。
package tankgame; import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; import java.util.ArrayList; //控制游戏画面的主进程 public class TankGameThread implements Runnable { private MTank mTank; private Graphics g;//游戏界的画笔 //地图数据 private int[][] map; private Image[] imageMap = new Image[4]; private ArrayList<EnemyTank> enemyTankList; public TankGameThread(MTank mTank, Graphics g, int[][] map, ArrayList<EnemyTank> enemyTankList) { this.mTank = mTank; this.g = g; this.map = map; this.enemyTankList = enemyTankList; for (int i = 1; i <= 4; i++) { imageMap[i - 1] = new ImageIcon("image//" + i + ".jpg").getImage(); } } public void run() { //创建缓冲图片 BufferedImage bufferedImage = new BufferedImage(1100, 1000, BufferedImage.TYPE_INT_RGB); //获取缓冲去画笔 Graphics buffG = bufferedImage.getGraphics(); while (true) { try { Thread.sleep(40); } catch (InterruptedException e) { throw new RuntimeException(e); } buffG.setColor(Color.BLACK); buffG.fillRect(0, 0, bufferedImage.getWidth(), bufferedImage.getHeight()); //绘制地图 drawMap(buffG); mTank.drawMTank(buffG); //遍历敌人坦克 for (int i = 0; i < enemyTankList.size(); i++) { EnemyTank eTank = enemyTankList.get(i); eTank.drawMTank(buffG); System.out.println(eTank.getX() + " eTank " + eTank.getY()); } //把换成图片显示窗体上 g.drawImage(bufferedImage, 0, 0, null); } } private void drawMap(Graphics g) { for (int i = 0; i < map.length; i++) { for (int j = 0; j < map[0].length; j++) { if (map[i][j] == 1) { g.drawImage(imageMap[0], j * 100, i * 100, 100, 100, null); } else if (map[i][j] == 2) { g.drawImage(imageMap[1], j * 100, i * 100, 100, 100, null); } else if (map[i][j] == 3) { g.drawImage(imageMap[2], j * 100, i * 100, 100, 100, null); } else if (map[i][j] == 4) { g.drawImage(imageMap[3], j * 100, i * 100, 100, 100, null); } } } } }
7.TimeThread类:定时生成敌人坦克
- 每隔 3 秒创建一个新的敌方坦克实例并加入列表。
- 用于模拟敌人动态生成逻辑
package tankgame; import java.util.ArrayList; public class TimeThread { private int[][] map; private ArrayList<EnemyTank> enemyTankList; public TimeThread(int[][] map, ArrayList<EnemyTank> enemyTankList) { this.map = map; this.enemyTankList = enemyTankList; } public void run() { while(true) { try { Thread.sleep(3000); } catch (InterruptedException e) { throw new RuntimeException(e); } //创建EnemyTank EnemyTank eTank = new EnemyTank(500,0,map); enemyTankList.add(eTank); } } }