基于Java awt和Swing的坦克游戏项目

该坦克游戏项目用了多线程进行画面更新和敌人生成,并涉及了地图绘制、碰撞检测、键盘控制等常见游戏开发功能。

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);
            }
        }
    }
    

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值