单机版坦克大战分步实现项目源码

    public void launch() {
        //设置窗口标题
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }
}

1.2 为窗口上色

  • 重写paint()方法,参数g为图形

  • 设置图形颜色

  • 设置图形大小

package com.qf;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JFrame;

public class GamePanel extends JFrame {
    
    private static final long serialVersionUID = 7471238471259660459L;
    
    //窗口宽高
    int width = 800;
    int height = 610;
    
    //获取画笔(为窗口上色)
    @Override
    public void paint(Graphics g) {
        //设置颜色
        g.setColor(Color.GRAY);
        //填充窗口
        g.fillRect(0, 0, width, height);
        
    }
    
    public void launch() {
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }

}

1.3 添加选项文字

  • 设置字体颜色,不要和背景色一样

  • 设置字体,字体大小

  • 添加文字,及文字位置

package com.qf;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;

import javax.swing.JFrame;

public class GamePanel extends JFrame {
    
    private static final long serialVersionUID = 7471238471259660459L;
    
    //窗口宽高
    int width = 800;
    int height = 610;
    
    //获取画笔
    @Override
    public void paint(Graphics g) {
        g.setColor(Color.GRAY);
        g.fillRect(0, 0, width, height);
        
        //添加游戏选项
        g.setColor(Color.BLUE);
        g.setFont(new Font("仿宋", Font.BOLD, 50));
        g.drawString("选择游戏模式", 220, 100);
        g.drawString("单人模式", 220, 200);
        g.drawString("双人模式", 220, 300);
        
    }
    
    public void launch() {
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }

}

1.4 为窗口添加键盘事件

  • 添加内部类,实现KeyAdapter类,重写keyPressed方法,监听按钮

class KeyMonitor extends KeyAdapter {
    
    @Override
    public void keyPressed(KeyEvent e) {
        System.out.println(e.getKeyChar());
    }
        
}
  • 窗口添加键盘监视器

this.addKeyListener(new GamePanel.KeyMonitor());
package com.qf;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;

public class GamePanel extends JFrame {
    
    private static final long serialVersionUID = 7471238471259660459L;
    
    //窗口宽高
    int width = 800;
    int height = 610;
    
    //获取画笔
    @Override
    public void paint(Graphics g) {
        g.setColor(Color.GRAY);
        g.fillRect(0, 0, width, height);
        
        //添加游戏选项
        g.setColor(Color.BLUE);
        g.setFont(new Font("仿宋", Font.BOLD, 50));
        g.drawString("选择游戏模式", 220, 100);
        g.drawString("单人模式", 220, 200);
        g.drawString("双人模式", 220, 300);
        
    }
    
    //添加内部类,实现KeyAdapter类,重写keyPressed方法
    class KeyMonitor extends KeyAdapter {
        
        @Override
        public void keyPressed(KeyEvent e) {
            System.out.println(e.getKeyChar());
        }
        
    }
    
    public void launch() {
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
        
        this.addKeyListener(new GamePanel.KeyMonitor());
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }

}

1.5 键盘控制选择游戏模式

  • 添加指针图片

  • 在项目文件夹下创建文件夹images,关于项目的图片,插件都放在这个文件夹内

  • 创建图片对象

Image select = Toolkit.getDefaultToolkit().getImage("images/mytank_right.gif");
  • int y = 150;

  • g.drawImage(select, 160, y, null);

  • //重绘

  • 设置初始纵坐标为150

  • 在paint()方法,添加此图片

  • 在launch()方法中重绘图形(每隔30毫秒)

        while(true) {
            repaint();   
            try {
                Thread.sleep(25);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
  • 重写键盘事件,1选择单人模式,2选择双人模式

//添加内部类,实现KeyAdapter类,重写keyPressed方法

    class KeyMonitor extends KeyAdapter {
        
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch(key) {
                case KeyEvent.VK_1 :
                    y = 150;
                    break;
                case KeyEvent.VK_2 :
                    y = 250;
                    break;
                case KeyEvent.VK_ENTER :
                    break;
                default:
                    break;
            }
        }
        
    }
  • 定义选择相应模式后,点击回车键,显示响应窗口

  • 定义模式state 0:未选择,1:单人 2:双人

    //模式state 0:未选择,1:单人 2:双人

    int state = 0;

    int a = 0;

  • 重写点击事件

//添加内部类,实现KeyAdapter类,重写keyPressed方法

class KeyMonitor extends KeyAdapter {
        
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch(key) {
                case KeyEvent.VK_1 :
                    a = 1; // 单人
                    y = 150;
                    break;
                case KeyEvent.VK_2 :
                    a = 2; //双人
                    y = 250;
                    break;
                case KeyEvent.VK_ENTER :
                    state = a;
                    break;
                default:
                    break;
            }
        }
        
    }
  • 写入图形判断

       if (state == 0 ) {
            g.drawString("选择游戏模式", 220, 100);
            g.drawString("单人模式", 220, 200);
            g.drawString("双人模式", 220, 300);
            
            //绘制指针
            g.drawImage(select, 160, y, null);
        } else if (state ==1 || state ==2) {
            g.drawString("游戏开始", 220, 100);
            if (state ==1) {
                g.drawString("单人模式", 220, 200);
            } else if(state ==2) {
                g.drawString("双人模式", 220, 200);
            }
        }
  • 整体代码

 package com.qf;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;

public class GamePanel extends JFrame {
    
    private static final long serialVersionUID = 7471238471259660459L;
    
    //指针图片
    Image select = Toolkit.getDefaultToolkit().getImage("images/mytank_right.gif");
    int y =150; //纵坐标
    
    //模式state    0:未选择,1:单人   2:双人
    int state = 0;
    int a = 0;
    
    
    //窗口宽高
    int width = 800;
    int height = 610;
    
    //获取画笔
    @Override
    public void paint(Graphics g) {
        g.setColor(Color.GRAY);
        g.fillRect(0, 0, width, height);
        
        //添加游戏选项
        g.setColor(Color.BLUE);
        g.setFont(new Font("仿宋", Font.BOLD, 50));
        
        if (state == 0 ) {
            g.drawString("选择游戏模式", 220, 100);
            g.drawString("单人模式", 220, 200);
            g.drawString("双人模式", 220, 300);
            
            //绘制指针
            g.drawImage(select, 160, y, null);
        } else if (state ==1 || state ==2) {
            g.drawString("游戏开始", 220, 100);
            if (state ==1) {
                g.drawString("单人模式", 220, 200);
            } else if(state ==2) {
                g.drawString("双人模式", 220, 200);
            }
        }
        
        
    }
    
    //添加内部类,实现KeyAdapter类,重写keyPressed方法
    class KeyMonitor extends KeyAdapter {
        
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch(key) {
                case KeyEvent.VK_1 :
                    a = 1; // 单人
                    y = 150;
                    break;
                case KeyEvent.VK_2 :
                    a = 2; //双人
                    y = 250;
                    break;
                case KeyEvent.VK_ENTER :
                    state = a;
                    break;
                default:
                    break;
            }
        }
        
    }
    
    public void launch() {
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
        
        this.addKeyListener(new GamePanel.KeyMonitor());
        
        //重绘
        while(true) {
            repaint();   
            try {
                Thread.sleep(25);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }

}

1.6 解决闪频

  • //解决闪动问题

Image offScreemImage = null;

  • 创建一个和弹出窗口宽高相同的图片,

  • 获取该图片的图形对象,把所有内容添加到该图片中

//创建和容器一样大小的Image图片

  • 创建一个图片

  • 重写paint()方法

if(offScreemImage == null) {
            offScreemImage = this.createImage(width,height);
        }
        
        //获该图片的图形
        Graphics gImage = offScreemImage.getGraphics();
        
        gImage.setColor(Color.GRAY);
        gImage.fillRect(0, 0, width, height);
        
        //添加游戏选项
        gImage.setColor(Color.BLUE);
        gImage.setFont(new Font("仿宋", Font.BOLD, 50));
        
        if (state == 0 ) {
            gImage.drawString("选择游戏模式", 220, 100);
            gImage.drawString("单人模式", 220, 200);
            gImage.drawString("双人模式", 220, 300);
            
            //绘制指针
            gImage.drawImage(select, 160, y, null);
        } else if (state ==1 || state ==2) {
            gImage.drawString("游戏开始", 220, 100);
            if (state ==1) {
                gImage.drawString("单人模式", 220, 200);
            } else if(state ==2) {
                gImage.drawString("双人模式", 220, 200);
            }
        }
  • g.drawImage(offScreemImage, 0, 0,null);

  • 把该图片写入到窗口中

package com.qf;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

import javax.swing.JFrame;

public class GamePanel extends JFrame {
    
    private static final long serialVersionUID = 7471238471259660459L;
    
    //解决闪动问题
    Image offScreemImage = null;
    
    //指针图片
    Image select = Toolkit.getDefaultToolkit().getImage("images/mytank_right.gif");
    int y =150; //纵坐标
    
    //模式state    0:未选择,1:单人   2:双人
    int state = 0;
    int a = 0;
    
    //窗口宽高
    int width = 800;
    int height = 610;
    
    //获取画笔
    @Override
    public void paint(Graphics g) {
        
        //创建和容器一样大小的Image图片
        if(offScreemImage == null) {
            offScreemImage = this.createImage(width,height);
        }
        
        //获该图片的图形
        Graphics gImage = offScreemImage.getGraphics();
        
        gImage.setColor(Color.GRAY);
        gImage.fillRect(0, 0, width, height);
        
        //添加游戏选项
        gImage.setColor(Color.BLUE);
        gImage.setFont(new Font("仿宋", Font.BOLD, 50));
        
        if (state == 0 ) {
            gImage.drawString("选择游戏模式", 220, 100);
            gImage.drawString("单人模式", 220, 200);
            gImage.drawString("双人模式", 220, 300);
            
            //绘制指针
            gImage.drawImage(select, 160, y, null);
        } else if (state ==1 || state ==2) {
            gImage.drawString("游戏开始", 220, 100);
            if (state ==1) {
                gImage.drawString("单人模式", 220, 200);
            } else if(state ==2) {
                gImage.drawString("双人模式", 220, 200);
            }
        }
        
        g.drawImage(offScreemImage, 0, 0,null);
        
    }
    
    //添加内部类,实现KeyAdapter类,重写keyPressed方法
    class KeyMonitor extends KeyAdapter {
        
        @Override
        public void keyPressed(KeyEvent e) {
            int key = e.getKeyCode();
            switch(key) {
                case KeyEvent.VK_1 :
                    a = 1; // 单人
                    y = 150;
                    break;
                case KeyEvent.VK_2 :
                    a = 2; //双人
                    y = 250;
                    break;
                case KeyEvent.VK_ENTER :
                    state = a;
                    break;
                default:
                    break;
            }
        }
        
    }
    
    public void launch() {
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
        
        this.addKeyListener(new GamePanel.KeyMonitor());
        
        //重绘
        while(true) {
            repaint();   
            try {
                Thread.sleep(25);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }

}

2. 键盘控制坦克移动

2.1 添加游戏类父类

  • 属性

  • 图片

  • 位置(坐标)

  • 面板

  • 方法

  • 构造方法

  • 在图形中构建自己

  • 获取自身矩形(用于比较中弹)

package com.qf;

import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Toolkit;

//游戏父类
public abstract class GameObject {

        //图片
        public Image img;
        
        //位置(坐标) 纵坐标  竖坐标
        public int x;
        public int y;
        //在哪个面板
        public GamePanel gamePanel;
        
        //有参构造器  img参数为字符串,图片路径
        public GameObject(String img, int x, int y, GamePanel gamePanel) {
                super();
                this.img = Toolkit.getDefaultToolkit().getImage(img);
                this.x = x;
                this.y = y;
                this.gamePanel = gamePanel;
        }
        
        //画自己
        public abstract void paintSelf(Graphics g);
        
        //获取自身矩形
        public abstract Rectangle getRec();
        
}

2.2 添加坦克类

  • 尺寸(宽高)

  • 速度

  • 方向

  • 不同方向图片

package com.qf;

import java.awt.Point;
import java.awt.Toolkit;

//坦克父类
public abstract class Tank extends GameObject {
    
    //尺寸
    public int width = 40;
    public int height = 50;
    
    //速度
    public int speed = 3;
    
    //方向
    public Direction direction = Direction.UP;
    
    //四个方向图片
    public String upImg;
    public String leftImg;
    public String rightImg;
    public String downImg;
    
    public Tank(String img, int x, int y, GamePanel gamePanel,
            String upImg, String leftImg, String rightImg, String downImg) {
        super(img, x, y, gamePanel);
        this.upImg = upImg;
        this.leftImg = leftImg;
        this.rightImg = rightImg;
        this.downImg = downImg;
    }
    
}

2.3 添加玩家类

package com.qf;

import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;

//玩家
public class PlayerOne extends Tank {

    public PlayerOne(String img, int x, int y, GamePanel gamePanel, String upImg, String leftImg, String rightImg,
            String downImg) {
        super(img, x, y, gamePanel, upImg, leftImg, rightImg, downImg);
    }

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(img, x, y, null);
    }

    @Override
    public Rectangle getRec() {
        return new Rectangle(x, y, width, height);
    }

}

2.4 在窗口中添加玩家

  • 创建玩家对象

  • //添加玩家一到面板

        PlayerOne playerOne = new PlayerOne("images/mytank_up.gif", 125, 510, 
                        this, 
                        "images/mytank_up.gif", 
                        "images/mytank_left.gif", 
                        "images/mytank_right.gif", 
                        "images/mytank_down.gif");
  • 把玩家对象添加到图形中

    playerOne.paintSelf(g);

package com.qf;

import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;

/*
 * 已解决闪动
 */
public class GamePanel extends JFrame {
    
    private static final long serialVersionUID = -9194828502749643640L;
    
    //解决闪动问题
    Image offScreemImage = null;
    
    //窗口长宽
    int width = 800;
    int height = 610;
    
    //指针图片
    Image select = Toolkit.getDefaultToolkit().getImage("images/mytank.gif");
    int y =150; //纵坐标
    
    //定义游戏模式 0:游戏未开始   1:单人模式   2:双人模式
    int state = 0;
    
    int a = 0; //根据输入的键值,修改a值,再赋值给state
    

    //添加玩家一到面板
    PlayerOne playerOne = new PlayerOne("images/mytank_up.gif", 125, 510, 
            this, 
            "images/mytank_up.gif", 
            "images/mytank_left.gif", 
            "images/mytank_right.gif", 
            "images/mytank_down.gif");
    
    
    //窗口的启动方法
    public void launch() {
        setTitle("千锋坦克大战");
        //设置宽高
        setSize(width, height);
        //居中
        setLocationRelativeTo(null);
        //差掉窗口,结束
        setDefaultCloseOperation(3);
        //窗口不可拉伸
        setResizable(false);
        //可见
        setVisible(true);
        
        //添加键盘监视器
        this.addKeyListener(new GamePanel.KeyMonitor());
        
        //重绘
        while(true) {
            repaint();   
            try {
                Thread.sleep(25);  //间隔时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
        }
        
    }
    
    //画  图形
    @Override
    public void paint(Graphics g) {
        //创建和容器一样大小的Image图片
        if(offScreemImage == null) {
            offScreemImage = this.createImage(width,height);
        }
        
        //获的该图片的画笔
        Graphics gImage = offScreemImage.getGraphics();
        
        
        //把窗口背景色设置为灰色
        gImage.setColor(Color.GRAY);
        //把这个背景色填充整个窗口
        gImage.fillRect(0, 0, width, height);
        
        //设置颜色
        gImage.setColor(Color.BLUE);
        //设置字体
        gImage.setFont(new Font("仿宋", Font.BOLD, 50));
        
        if(state == 0 ) {
            //添加文字
            gImage.drawString("选择游戏模式", 220, 100);
            gImage.drawString("单人游戏", 220, 200);
            gImage.drawString("双人游戏", 220, 300);
            //绘制指针
            gImage.drawImage(select, 160, y, null);
            
        } else if (state == 1 || state == 2) {
            gImage.drawString("游戏开始", 220, 100);
            if(state == 1) {
                gImage.drawString("单人游戏", 220, 200);
            } else {
                gImage.drawString("双人游戏", 220, 200);
            }
            
            //添加玩家
            playerOne.paintSelf(gImage);
        }
        
        g.drawImage(offScreemImage, 0, 0,null);
    }
    
    //键盘监视器,需要添加到窗口中
    class KeyMonitor extends KeyAdapter {
        
        //按下键盘的回调方法
        @Override
        public void keyPressed(KeyEvent e) {
            //返回按钮的值
            int key = e.getKeyChar();
            
            switch(key) {
                case KeyEvent.VK_1 :
                    a = 1;
                    y = 150;
                    break;
                case KeyEvent.VK_2 :
                    a = 2;
                    y = 250;
                    break;
                case KeyEvent.VK_ENTER :
                    state = a;
                    break;
                default:
                    
            }
        }
    }
    
    public static void main(String[] args) {
        GamePanel gp = new GamePanel();
        gp.launch();
    }

}

2.5使用键盘控制坦克移动

  • 不同方向设置不同图片

  • 不同的方向

//移动方法

  • 在坦克类(Tank)中添加上下左右四个方法

    public void leftward() {
        x -=speed;
        direction = Direction.LEFT;
        setImg(leftImg);
    }
    
    public void rightward() {
        x +=speed;
        direction = Direction.RIGHT;
        setImg(rightImg);
    }
    
    public void upward() {
        y -=speed;
        direction = Direction.UP;
        setImg(upImg);
    }
    
    public void downward() {
        y +=speed;
        direction = Direction.DOWN;
        setImg(downImg);
    }
    
    public void setImg(String img) {
        this.image = Toolkit.getDefaultToolkit().getImage(img);
    }
  • 在PlayerOne类中按钮点击,松开方法,及移动方法

  • W上,A左,S下,D右

boolean left, right, up, down;

//键盘按下去方法

public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        switch(key) {
            case KeyEvent.VK_A : left=true; break;
            case KeyEvent.VK_S : down=true; break;
            case KeyEvent.VK_D : right=true; break;
            case KeyEvent.VK_W : up=true; break;
        }
    }
    
    //键盘松开方法
    public void keyReleased(KeyEvent e) {
        int key = e.getKeyCode();
        switch(key) {
            case KeyEvent.VK_A : left=false; break;
            case KeyEvent.VK_S : down=false; break;
            case KeyEvent.VK_D : right=false; break;
            case KeyEvent.VK_W : up=false; break;
        }
    }
  • //移动的方法,每次在绘制的时候移动

 public void move() {
        if(left) {
            leftward();
        } else if (right) {
            rightward();
        } else if (up) {
            upward();
        } else if(down) {
            downward();
        }
    }

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
        
        //每次绘图的时候,根据最新的属性,绘图
        move();
    }
  • 在主窗口类中,添加对A,S,D,W按键的监控

    //键盘监控

    class KeyMonitor extends KeyAdapter {
                
                //点击键盘的回调方法
                @Override
                public void keyPressed(KeyEvent e) {
                        int key = e.getKeyChar();
                        switch(key) {
                                case KeyEvent.VK_1:
                                        y = 150;   
                                        a = 1;
                                        break;
                                case KeyEvent.VK_2:
                                        y = 250;  
                                        a = 2;
                                        break;
                                case KeyEvent.VK_ENTER :  //点击回车,选择相应的模式
                                        state = a;
                                default:
                                        playerOne.keyPressed(e);   //这里
                        }
                }
                
                @Override
                public void keyReleased(KeyEvent e) {  //监听按钮的松开事件
                        playerOne.keyReleased(e);  //这里
                }
        }

3. 坦克发射子弹

3.1 创建子弹类

  • 尺寸

  • 速度

  • 方向

  • 属性

  • //尺寸

        int width = 10;

        int height =10;

        //速度

        int speed = 7;

        //方向,方向和坦克方向一致

        Direction direction;

  • 左移,右移,下移,上移

  • 根据坦克方向不同,移动方向不同

  • //移动方法

  • 方法

 public void leftward() {
        x -=speed;
    }
    
    public void rightward() {
        x +=speed;
    }
    
    public void upward() {
        y -=speed;
    }
    
    public void downward() {
        y +=speed;
    }
    
    //根据方向移动,绘制自己时,调用
    public void go() {
        switch(direction) {
        case LEFT:  leftward(); break;
        case DOWN:  downward(); break;
        case RIGHT: rightward();break;
        case UP:    upward();   break;
        }
    }

全部代码:

 package com.qf;

import java.awt.Graphics;
import java.awt.Rectangle;

public class Bullet extends GameObject {

    //尺寸
    int width = 10;
    int height =10;
    //速度
    int speed = 7;
    //方向,方向和坦克方向一致
    Direction direction;
    
    //移动方法
    public void leftward() {
        x -=speed;
    }
    
    public void rightward() {
        x +=speed;
    }
    
    public void upward() {
        y -=speed;
    }
    
    public void downward() {
        y +=speed;
    }
    
    //绘制自己时,调用
    public void go() {
        switch(direction) {
        case LEFT:  leftward(); break;
        case DOWN:  downward(); break;
        case RIGHT: rightward();break;
        case UP:    upward();   break;
        }
    }
    
    
    
    public Bullet(String imageUrl, int x, int y, GamePanal gamePanal,Direction direction) {
        super(imageUrl, x, y, gamePanal);
        this.direction = direction;
    }

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
        
        //绘制子弹时,可以移动
        go();
    }

    @Override
    public Rectangle getRec() {
        return new Rectangle(x, y, width, height);
    }
    
}

3.2 实现坦克发射子弹

  • 获取坦克的头部坐标

  • 在坦克类中

       public Point getHeadPoint() {
                switch(direction) {
                         case LEFT:        return new Point(x, y+height/2);
                         case DOWN: return new Point(x+width/2, y+height);
                         case RIGHT: return new Point(x+width, y+height/2);
                         case UP: return new Point(x+width/2, y);
                         default: return null;
                }
        }
  • 添加发射方法

  • 根据坦克头坐标,及坦克方向创建子弹

  • 把创建的子弹添加到面板中创建子弹集合中

        //发射子弹

        public void attack() {
                //获取头坐标
                Point point = getHeadPoint();
                //创建子弹
                Bullet bullet = new Bullet("images/bullet.gif", point.x, point.y, 
                                this.gamePanal, this.direction);
                //可以一次按很多次发射,把产生的子弹存储在面板的子弹集合中
                this.gamePanal.bulletList.add(bullet);
        }

//子弹集合 在面板类中定义

List<Bullet> bulletList = new ArrayList<Bullet>();
   //绘制玩家
    playerOne.paintSelf(gImage);
            
    //绘制子弹  在面板类中操作
    for(Bullet bullet : bulletList) {
        bullet.paintSelf(gImage);
    }
  • 当点击空格按钮,就会调用射击方法

  • 在PlayerOne类中添加键盘事件

    //键盘按下去方法

    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();
        switch(key) {
            case KeyEvent.VK_A : left=true; break;
            case KeyEvent.VK_S : down=true; break;
            case KeyEvent.VK_D : right=true; break;
            case KeyEvent.VK_W : up=true; break;
            case KeyEvent.VK_SPACE: attack();           //添加发送子弹方法
        }
    }

3.3 添加子弹冷却时间

  • 在坦克类中添加冷却状态,冷却时间

        //冷却状态

        boolean attackCoolDown = true;

        //冷却时间

        int attackCoolTime = 1000;

  • 创建冷却线程

        //冷却线程

   class AttackCool extends Thread {
        
        public void run() {
            attackCoolDown = false;
            try {
                Thread.sleep(attackCoolTime);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            attackCoolDown = true;
        }
        
    }
  • 修改发射方法,每发射一次,启动冷却线程

        //发射子弹

    public void attack() {
        if (attackCoolDown) {
            //获取头坐标
            Point point = getHeadPoint();
            //创建子弹
            Bullet bullet = new Bullet("images/bullet.gif", point.x, point.y, 
                    this.gamePanal, this.direction);
            //可以一次按很多次发射,把产生的子弹存储在面板的子弹集合中
            this.gamePanal.bulletList.add(bullet);
            
            //启动冷却线程
            new AttackCool().start();
        }
    }

4. 敌方坦克

4.1 随机添加敌方坦克

  • 添加敌方坦克类

        //敌方坦克

public class Bot extends Tank{

    public Bot(String imageUrl, int x, int y, GamePanal gamePanal, String upImg, String leftImg, String rightImg,
            String downImg) {
        super(imageUrl, x, y, gamePanal, upImg, leftImg, rightImg, downImg);
    }

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
    }

    @Override
    public Rectangle getRec() {
        return new Rectangle(x, y, width, height);
    }

}
  • 批量把敌方坦克图片添加到面板上

  • 把敌方坦克的图片添加到项目中(4个方向各一个)

  • 在面板中创建敌方坦克集合

  • 面板每重新加载100次,添加一个敌方坦克,敌方坦克最多有10各

        //重绘次数

        int count =0;

        //敌方坦克个数

        int enemyCount = 0;

  • 在重新绘制循环中,添加敌方坦克

        //重新载入窗口

                while(true) {
                        
                        if(count%100 ==1 && enemyCount<5 ) {
                                //添加敌方坦克
                                Random r = new Random();
                                int rnum = r.nextInt(800);
                                botList.add(new Bot("images/enemy_up.png", rnum, 110, 
                                                this, "images/enemy_up.png", "images/enemy_left.png", 
                                                "images/enemy_right.png", "images/enemy_down.gif"));
                                enemyCount++;
                        }
                        
                        this.repaint();
                        try {
                                Thread.sleep(25);
                        } catch (InterruptedException e) {
                                e.printStackTrace();
                        }
                        
                }

在开始游戏加载面板中显示敌方坦克并对count自增(paint方法)

                if (state ==0) {
                        //220离左边的距离,  100离上面的距离
                        gImage.drawString("选择游戏模式", 220, 100);
                        gImage.drawString("单人模式", 220, 200);
                        gImage.drawString("双人模式", 220, 300);
                        //画入图像
                        gImage.drawImage(select, 140, y, null);
                } else {
                        gImage.drawString("开始游戏", 220, 100);
                        if(state == 1) {
                                gImage.drawString("单人模式", 220, 200);
                        } else if (state == 2) {
                                gImage.drawString("双人模式", 220, 200);
                        }
                        
                        //绘制玩家
                        playerOne.paintSelf(gImage);
                        
                        //绘制子弹
                        for(Bullet bullet : bulletList) {
                                bullet.paintSelf(gImage);
                        }
                        
                        //绘制敌方坦克
                        for(Bot bot : botList) {
                                bot.paintSelf(gImage);
                        }
                        
                        count++;
                }

4.2 敌方坦克随机移动

  • 随机生成方向(Bot)

        //随机生成个数,表示方向

       public Direction getRandomDirection() {
                Random random = new Random();
                int rnum = random.nextInt(4); //0-3
                switch(rnum) {
                        case 0 : return Direction.LEFT;
                        case 1 : return Direction.RIGHT;
                        case 2 : return Direction.UP;
                        case 3 : return Direction.DOWN;
                        default: return null;
                }
                
        }
  • 添加运动方法,每20次,转化一次方向(Bot)

        //每间隔20,转换方向一次

        int moveTime = 20;

        //行走方法

    public void go() {
        if (moveTime >=20) {
            direction = getRandomDirection();
            moveTime=0;
        } else {
            moveTime++;
        }
        
        switch(direction) {
            case LEFT:  leftward(); break;
            case DOWN:  downward(); break;
            case RIGHT: rightward();break;
            case UP:    upward();   break;
        }
        
    }

    //勿忘把重绘方法添加到绘制自己方法中

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
        
        go();
    }

4.3 敌方坦克射击

  • 创建敌方子弹类,直接继承Bullet即可

package com.qf;

import java.awt.Graphics;
import java.awt.Rectangle;

//敌方子弹类
public class EnemyBullet extends Bullet {

    public EnemyBullet(String imageUrl, int x, int y, GamePanal gamePanal, Direction direction) {
        super(imageUrl, x, y, gamePanal, direction);
    }
    

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
        
        //在图形绘制自己时移动
        go();
    }

    @Override
    public Rectangle getRec() {
        return new Rectangle(x, y, width, height);
    }

}
  • 机器人自行发射,把该方法添加到go()方法中

  • 在机器人坦克类中添加攻击发射方法

    //攻击类,添加到go方法中

    public void attack() {
        Point p = getHeadPoint();
        Random random = new Random();
        int num = random.nextInt(100); // [0,99]
        if(num <4) {  //4%设计概率,可自行调节发射频率   0,1,2,3
            this.gamePanal.bulletList.add(new EnemyBullet("images/bullet.gif",
                    p.x, p.y, this.gamePanal, this.direction));
        }
    }

//机器人坦克自行行走

    public void go() {
        
        attack();  // 添加到这里,边走边发射子弹
        
        if (moveTime>=20) {
            direction = getRandomDirection();
            moveTime =0;
        } else {
            moveTime++;
        }
        
        switch(direction) {
            case LEFT : leftward(); break;
            case RIGHT : rightward(); break;
            case DOWN : downward(); break;
            case UP : upward(); break;
        }
        
    }

5. 碰撞检测

5.1 我方子弹(Bullet)和敌方坦克(Bot)碰撞

  • 在面板类中定义要删除/消失的子弹(GamePanal)

  • 在每次重绘时,把删除的子弹从绘制子弹集合中删除

        //碰撞后要删除的子弹

        List<Bullet> removeList = new ArrayList<Bullet>();
        //绘制子弹
        for(Bullet bullet : bulletList) {
                bullet.paintSelf(gImage);
        }
                        
        //去除要删除的子弹
        bulletList.removeAll(removeList);
  • 在绘制每个子弹时,把当前子弹对象和所有计算机坦克比较是否碰撞

  • 该方法要添加到绘制方法中

  • 在子弹类中添加碰撞方法(Bullet)

        //碰撞 添加到paintSelf方法中,每次绘制时,检测

        public void hitBot() {
                List<Bot> bots = this.gamePanal.botList;
                for(Bot bot : bots) {
                        if (this.getRec().intersects(bot.getRec())) {
                                this.gamePanal.botList.remove(bot);   // 坦克消失
                                this.gamePanal.removeList.add(this);  //子弹消失
                                break;
                        }
                }
        }
        @Override
        public void paintSelf(Graphics g) {
                g.drawImage(image, x, y, null);
                
                //在图形绘制自己时移动
                go();
                
                //每次绘制时,检测碰撞
                hitBot();
        }

5.2 敌方子弹和我方坦克碰撞

  • 把玩家都添加到此集合中,在选择游戏模式时添加

  • 不要忘记把原绘制玩家代码删除

  • 添加我方坦克集合(为后续扩展使用)

  • 玩家的绘制,修改成遍历该集合

        //玩家坦克集合

List<Tank> playerList = new ArrayList<Tank>();
        //键盘监控
        class KeyMonitor extends KeyAdapter {
                
                //点击键盘的回调方法
                @Override
                public void keyPressed(KeyEvent e) {
                        int key = e.getKeyChar();
                        switch(key) {
                                case KeyEvent.VK_1:
                                        y = 150;   
                                        a = 1;
                                        break;
                                case KeyEvent.VK_2:
                                        y = 250;  
                                        a = 2;
                                        break;
                                case KeyEvent.VK_ENTER :  //点击回车,选择相应的模式
                                        state = a;
                                        
                                        //添加玩家,双人待做
                                        playerList.add(playerOne);     //----这里添加
                                default:
                                        playerOne.keyPressed(e);   //坦克的移动
                        }
                }
                //绘制玩家
                //        playerOne.paintSelf(gImage);
                for(Tank player : playerList) {
                        player.paintSelf(gImage);
                }
  • 该方法添加到绘制方法中

  • 在敌方子弹类中添加碰撞方法

       //碰撞 添加到paintSelf方法中,每次绘制时,检测

        public void hitPlayer() {
                List<Tank> tanks = this.gamePanel.playerList;
                for(Tank t : tanks) {
                        if (this.getRec().intersects(t.getRec())) {
                                this.gamePanel.playerList.remove(t);   // 玩家坦克消失
                                this.gamePanel.removeList.add(this);  //子弹消失
                                break;
                        }
                }
        }
        @Override
        public void paintSelf(Graphics g) {
                g.drawImage(image, x, y, null);
                
                //在图形绘制自己时移动
                go();
                
                //检测碰撞
                hitPlayer();
        }

5.3 与围墙的碰撞检测

5.3.1面板中添加围墙

  • 添加围墙类

package com.qf;

import java.awt.Graphics;
import java.awt.Rectangle;

public class Wall extends GameObject {
    
    //围墙尺寸
    int length = 60;

    public Wall(String imageUrl, int x, int y, GamePanel gamePanal) {
        super(imageUrl, x, y, gamePanel);
    }

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
    }

    @Override
    public Rectangle getRec() {
        return new Rectangle(x, y, length, length);
    }

}
在面板中添加围墙集合,在lunch方法中添加围墙,然后把集合绘制到面板中
//围墙列表
List<Wall> wallList = new ArrayList<Wall>();
//在lunch方法中

//添加围墙
        for(int i = 0; i<14; i++) {
            wallList.add(new Wall("images/wall.png", i*60, 170, this));
        }
        wallList.add(new Wall("images/wall.png", 305, 560, this));
        wallList.add(new Wall("images/wall.png", 305, 500, this));
        wallList.add(new Wall("images/wall.png", 365, 500, this));
        wallList.add(new Wall("images/wall.png", 425, 500, this));
        wallList.add(new Wall("images/wall.png", 425, 560, this));
    //在paint方法中绘制围墙
    for(Wall wall : wallList) {
        wall.paintSelf(gImage);
    }

5.3.2 添加围墙和子弹碰撞检测

  • 把该方法添加到go()方法中,让敌方子弹也可以和围墙碰撞

  • 子弹类添加和围墙碰撞方法(Bullet)

    //子弹和围墙碰撞,添加到go方法中,让敌方子弹也可以击破围墙

    public void hitWall() {
        List<Wall> walls = this.gamePanal.wallList;
        for(Wall wall : walls) {
            if (this.getRec().intersects(wall.getRec())) {
                this.gamePanal.wallList.remove(wall);   // 围墙消失
                this.gamePanal.removeList.add(this);  //子弹消失
                break;
            }
        }
    }
        public void go() {
                switch(direction) {
                        case LEFT : leftward(); break;
                        case RIGHT : rightward(); break;
                        case DOWN : downward(); break;
                        case UP : upward(); break;
                }
                hitWall();       //---------------添加到这里
        }

5.3.3 坦克和围墙碰撞

  • 当坦克和围墙碰撞时,坦克无法移动

  • 在坦克类中添加围墙碰撞方法

  • 然后在移动类中判断是否会和围墙碰撞,如果碰撞则不移动

        //与围墙碰撞检测

                public boolean hitWall(int x, int y) {
                        List<Wall> wallList = this.gamePanal.wallList;
                        //坦克下一步将要移动后的矩形
                        Rectangle next = new Rectangle(x, y, width, height);
                        
                        for(Wall wall : wallList) {
                                if(next.intersects(wall.getRec())) {
                                        return true;
                                }
                        }
                        return false;
                }
  • 修改所有移动的方法

    //移动方法 左移

  public void leftward() {
            if(!hitWall(x-speed, y)) {
                x -=speed;
            }
            direction = Direction.LEFT;
            setImg(leftImg);
        }
        
         //移动方法   右移
        public void rightward() {
            if(!hitWall(x+speed, y)) {
                x +=speed;
            }
            
            direction = Direction.RIGHT;
            setImg(rightImg);
        }
        
         //移动方法   上移
        public void upward() {
            if(!hitWall(x, y-speed)) {
                y -=speed;
            }
            direction = Direction.UP;
            setImg(upImg);
        }
        
         //移动方法   下移
        public void downward() {
            if(!hitWall(x, y+speed)) {
                y +=speed;
            }
            
            direction = Direction.DOWN;
            setImg(downImg);
        }

5.4 判断坦克边缘的碰撞

  • 该判断添加到移动方法中

  • 坦克类中添加边缘碰撞判断(Tank)

        //判断和边缘的碰撞

        public boolean moveToBorder(int x,int y) {
            if(x<0) {
                return true;
            } else if (x+width > this.gamePanal.width) {
                return true;
            } else if (y <0) {
                return true;
            } else if (y+height > this.gamePanal.height) {
                return true;
            }
            return false;
        }
       //移动方法   左移
        public void leftward() {
            if(!hitWall(x-speed, y) && !moveToBorder(x-speed, y)) {
                x -=speed;
            }
            direction = Direction.LEFT;
            setImg(leftImg);
        }
        
         //移动方法   右移
        public void rightward() {
            if(!hitWall(x+speed, y) && !moveToBorder(x+speed, y)) {
                x +=speed;
            }
            
            direction = Direction.RIGHT;
            setImg(rightImg);
        }
        
         //移动方法   上移
        public void upward() {
            if(!hitWall(x, y-speed)&&!moveToBorder(x, y-speed)) {
                y -=speed;
            }
            direction = Direction.UP;
            setImg(upImg);
        }
        
         //移动方法   下移
        public void downward() {
            if(!hitWall(x, y+speed)&&!moveToBorder(x, y+speed)) {
                y +=speed;
            }
            
            direction = Direction.DOWN;
            setImg(downImg);
        }

5.5判断子弹是否出界

  • 该方法添加到go方法中

  • 判断子弹坐标是否出界,如果出界则删除

        //如果子弹出界,该方法添加在go方法中,敌人子弹也可以删除 删除子弹,节省资源

    public void moveToBorder() {
        if(x<0 || x+width > this.gamePanel.width) {
            this.gamePanel.removeList.add(this);
        }

        if(y<0 || y+height > this.gamePanel.height) {
            this.gamePanel.removeList.add(this);
        }

    }

    //子弹的移动是根据方向来的
    public void go() {
        switch(direction) {
            case LEFT : leftward(); break;
            case RIGHT : rightward(); break;
            case DOWN : downward(); break;
            case UP : upward(); break;
        }
        hitWall(); //子弹撞墙
        moveToBorder(); //子弹出界删除
    }

6. 添加基地

6.1 创建并添加基地

  • 创建基地类

package com.qf;

import java.awt.Graphics;
import java.awt.Rectangle;

//基地类
public class Base extends GameObject {
    
    int length = 60;

    public Base(String imageUrl, int x, int y, gamePanel gamePanal) {
        super(imageUrl, x, y, gamePanal);
    }

    @Override
    public void paintSelf(Graphics g) {
        g.drawImage(image, x, y, null);
        
    }

    @Override
    public Rectangle getRec() {
        return new Rectangle(x, y, length, length);
    }

}
在面板中添加基地
//基地集合
List<Base> baseList = new ArrayList<Base>();

//在lunch方法添加创建一个基地
Base base = new Base("images/base.png", 365, 560, this);
baseList.add(base);

//绘制基地
for(Base base : baseList) {
    base.paintSelf(gImage);
}

6.2 子弹与基地的碰撞测试

  • 该方法添加到go方法中

  • 在子弹类中添加碰撞方法

        //与基地碰撞方法

    public void hitBase() {
        List<Base> bases = this.gamePanel.baseList;
        for(Base base : bases) {
            if (this.getRec().intersects(base.getRec())) {
                this.gamePanel.baseList.remove(base);   // 基地消失
                this.gamePanel.removeList.add(this);  //子弹消失
                break;
            }
        }
    }
    public void go() {
        switch(direction) {
            case LEFT : leftward(); break;
            case RIGHT : rightward(); break;
            case DOWN : downward(); break;
            case UP : upward(); break;
        }
        hitWall(); //子弹撞墙
        moveToBorder(); //子弹出界删除
        hitBase(); //---销毁基地
    }

7. 游戏规则

  • 把所有的敌人干倒

  • 玩家坦克被干掉

  • 基地被倒了

  • 我方胜利

  • 机器赢了

        //判断规则,在每次重绘时

//玩家赢了
if(botList.size() == 0 && enemyCount == 3) {
    state = 3;
}
            
//机器赢了
if((playerList.size()==0&&(state ==1 || state ==2))  
   || baseList.size() ==0) {
    state = 4;
} 

    //获取画笔
    @Override
    public void paint(Graphics g) {
        
        if(offScreemImage==null) {
            offScreemImage = this.createImage(width, height);
        }
        
        Graphics gImage = offScreemImage.getGraphics();
        
        //设置图形的颜色
        gImage.setColor(Color.GRAY);
        //把设置的颜色,填充整个矩形
        gImage.fillRect(0, 0, width, height);
        
        //重新设置字体颜色
        gImage.setColor(Color.BLUE);
        gImage.setFont(new Font("楷体", Font.BOLD, 50));
        
        if (state ==0) {
            //220离左边的距离,  100离上面的距离
            gImage.drawString("选择游戏模式", 220, 100);
            gImage.drawString("单人模式", 220, 200);
            gImage.drawString("双人模式", 220, 300);
            //画入图像
            gImage.drawImage(select, 140, y, null);
        } else if (state == 1 || state ==2){
            gImage.drawString("开始游戏", 220, 100);
            if(state == 1) {
                gImage.drawString("单人模式", 220, 200);
                
            } else if (state == 2) {
                gImage.drawString("双人模式", 220, 200);
            }
            
            //绘制玩家
    //      playerOne.paintSelf(gImage);
            
            //绘制玩家列表
            for(Tank tank : playerList) {
                tank.paintSelf(gImage);
            }
            
            //绘制子弹
            for(Bullet bullet : bulletList) {
                bullet.paintSelf(gImage);
            }
            //删除发生碰撞的子弹
            bulletList.removeAll(removeList);
            
            //绘制机器人坦克
            for (Bot bot : botList) {
                bot.paintSelf(gImage);
            }
            
            //绘制城墙
            for (Wall wall : wallList) {
                wall.paintSelf(gImage);
            }
            
            //绘制基地
            for (Base base : baseList) {
                base.paintSelf(gImage);
            }
            
            count++;
        } else if(state ==3) {
            gImage.drawString("我赢了", 220, 100);
        } else if(state == 4) {
            gImage.drawString("机器赢了", 220, 100);
        }
        
        //把重新绘制的图片放到窗口中(从左上角开始放)
        g.drawImage(offScreemImage, 0, 0, null);
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值