用java写个小游戏 坦克大战

1. 窗体绘制

JFrame是指一个计算机语言-java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭。

 

1.1 创建窗口

  • 创建类继承JFrame

  • 创建启动方法launch()

    • setTitle()设置标题

    • setSize(width, height); 设置宽高

    • setLocationRelativeTo(null); 设置位置,null为居中

    • setResizable(); 是否可拉伸

    • setVisible(); 是否可见

    • setDefaultCloseOperation(3); 1,2,3 (点差号,关闭程序)

package com.qf;

import javax.swing.JFrame;

public class GamePanel extends JFrame {

private static final long serialVersionUID = 7471238471259660459L;

//窗口宽高
int width = 800;
int height = 610;

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");
    • 设置初始纵坐标为150

      • int y = 150;
  • 在paint()方法,添加此图片

    • g.drawImage(select, 160, y, null);
  • 在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;
  • 重写paint()方法

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

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

    //创建和容器一样大小的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);
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 AttackCool() {
			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) {
			
			while(count%100 ==1 && enemyCount<10 ) {
				//添加敌方坦克
				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.png"));
				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);
		if(num <4) {  //4%设计概率,可自行调节发射频率
			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.gamePanal.playerList;
		for(Tank t : tanks) {
			if (this.getRec().intersects(t.getRec())) {
				this.gamePanal.playerList.remove(t);   // 玩家坦克消失
				this.gamePanal.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, GamePanal 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);
	}

}
  • 在面板中添加围墙集合,在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));
	//绘制围墙
	for(Wall wall : wallList) {
		wall.paintSelf(gImage);
	}

 

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

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

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

	//子弹和围墙碰撞,添加到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.gamePanal.width) {
			this.gamePanal.removeList.add(this);
		}
		
		if(y<0 || y+height > this.gamePanal.height) {
			this.gamePanal.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, GamePanal 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>();

//创建一个基地
Base base = new Base("images/base.png", 365, 560, this);

//在lunch方法添加
baseList.add(base);

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

6.2 子弹与基地的碰撞测试

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

    • 该方法添加到go方法中

	//与基地碰撞方法
	public void hitBase() {
		List<Base> bases = this.gamePanal.baseList;
		for(Base base : bases) {
			if (this.getRec().intersects(base.getRec())) {
				this.gamePanal.baseList.remove(base);   // 基地消失
				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(); //子弹撞墙
		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);
	}

 

1. 窗体绘制

JFrame是指一个计算机语言-java的GUI程序的基本思路是以JFrame为基础,它是屏幕上window的对象,能够最大化、最小化、关闭。

 

1.1 创建窗口

  • 创建类继承JFrame

  • 创建启动方法launch()

    • setTitle()设置标题

    • setSize(width, height); 设置宽高

    • setLocationRelativeTo(null); 设置位置,null为居中

    • setResizable(); 是否可拉伸

    • setVisible(); 是否可见

    • setDefaultCloseOperation(3); 1,2,3 (点差号,关闭程序)

package com.qf;

import javax.swing.JFrame;

public class GamePanel extends JFrame {

private static final long serialVersionUID = 7471238471259660459L;

//窗口宽高
int width = 800;
int height = 610;

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");
    • 设置初始纵坐标为150

      //初始纵坐标为150
      int y = 150;
  • 在paint()方法,添加此图片

    • g.drawImage(select, 160, y, null);
  • 在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;
  • 重写paint()方法

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

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

    //创建和容器一样大小的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);

     

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

方向枚举类:

package com.qf;

public enum Direction {

	UP,LEFT,RIGHT,DOWN
	
}

 

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:
					playerOne.keyPressed(e); //玩家按
			}
			
		}
		
		@Override
		public void keyReleased(KeyEvent e) {
			playerOne.keyReleased(e); //玩家松
		}
		
	}
	
	public static void main(String[] args) {
		GamePanel gp = new GamePanel();
		gp.launch();
	}

}

 

 

posted @ 2021-11-03 15:41  苏unique  阅读(1250)  评论(0)    收藏  举报