Java坦克大战
画图基础
先定义画板类, 用于画图
// 定义画板类画板,用于画图
class MyPanel extends JPanel{
// Graphics g是一只画笔
// Graphics 提供了绘画方法
public void paint(Graphics g){
super.paint(g);
g.drawOval(0,0,100,100);
}
}
定义窗口类,用于显示画板
// public类继承JFrame,用于定义窗口
public class Painting extends JFrame {
public static void main(String[] args) {
new Painting();
}
private MyPanel myPanel = null;
public Painting(){
myPanel = new MyPanel();
// 窗口加入画布
this.add(myPanel);
this.setSize(500,500);
// 设置窗口退出方式
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 显示窗口
this.setVisible(true);
}
}
paint方法被调用情况
- 组件第一次在屏幕显示的时候
- 窗口大小改变的时候
- repaint()方法被调用的时候
Graphics类常用方法
// 画直线
drawLine(int x1, int y1, int x2, int y2)
// 画矩形边框
drawRect(int x, int y, int width, int height)
// 画椭圆边框
drawOval(int x, int y, int width, int height)
// 填充矩形
fillRect(int x, int y, int width, int height)
// 填充椭圆
fillOval(int x, int y, int width, int height)
// 画图片
drawImage(Image img, int x, int y, ImageObserver observer)
g.drawImage(imgae, 0 , 0,889,500,this);
// 获得图片资源
Image image = Toolkit.getDefaultToolkit().getImage(MyPanel.class.getResource("/vertin.png"));
// 画字符串
drawString(String str, int x, int y)
// 设置画笔颜色
setColor(Color c)
// 设置字体
setFont(Font font)
setFont(new Font("宋体",Font.BOLD,20))
// 设置背景色
setBackground(Color c)
Java事件处理
事件源:产生事件的对象,比如按钮,窗口,文本框等
事件监听器:负责监听事件源所发生的事件,当事件发生时,事件监听器会自动调用相应的方法来处理事件
事件:事件源上发生的事情,比如单击,双击,键盘按下,鼠标移动等
事件发生时,事件源创建对象交给事件监听器,事件监听器调用相应的方法处理事件
事件处理流程:
- 组件.addXXXListener(监听器对象)
- 监听器对象实现接口
- 重写接口中的方法
事件监听器接口
- 鼠标事件监听器接口:MouseListener
- 键盘事件监听器接口:KeyListener
- 窗口事件监听器接口:WindowListener
事件监听器适配器
- 鼠标事件监听器适配器:MouseAdapter
- 键盘事件监听器适配器:KeyAdapter
坦克大战
MyPanel类,用于完成游戏界面绘制
package TankGame;
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
public class MyPanel extends JPanel implements KeyListener {
public Player player = null;
static Vector<RedEnemy> enemies = new Vector<>();
private int enemiesNum = 6;
private int points = 0;
private int startMode = 0;
private String saveAddress = "d:\\save.dat";
public int getPoints() {
return points;
}
public void setPoints(int points) {
this.points = points;
}
public static Vector<RedEnemy> getEnemies() {
return enemies;
}
public static void setEnemies(Vector<RedEnemy> enemies) {
MyPanel.enemies = enemies;
}
public MyPanel(){
player = new Player(500, 500);
for (int i = 0; i < enemiesNum; ++i) {
RedEnemy e = new RedEnemy(10 + 100 * i, 10);
enemies.add(e);
}
}
public MyPanel(int startMode){
File file = new File(saveAddress);
if(startMode == 1 && file.exists()){
try {
ObjectInputStream oi = new ObjectInputStream(new FileInputStream(saveAddress));
this.points = oi.readInt();
player = (Player)oi.readObject();
int N = oi.readInt();
for(int i = 0;i < N;++i){
RedEnemy e = (RedEnemy) oi.readObject();
enemies.add(e);
}
} catch (IOException | ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
}
}else if(startMode == 0){
player = new Player(500, 500);
for (int i = 0; i < enemiesNum; ++i) {
RedEnemy e = new RedEnemy(10 + 100 * i, 10);
enemies.add(e);
}
}
}
@Override
public void paint(Graphics g) {
super.paint(g);
g.fillRect(0,0,1000,750);
g.setColor(Color.CYAN);
g.setFont(new Font("宋体",Font.BOLD,20));
g.drawString("当前得分:" + points, 800, 700);
// 绘制坦克
if(player.isAlived())
drawTank(player.getX(),player.getY(),g,player.getDir(),0);
synchronized (player.getBullets()){
// 绘制子弹
Vector<Bullet> bullets = player.getBullets();
for(Bullet b : bullets){
switch (b.getType()){
case 0:
g.setColor(Color.yellow);
break;
case 1:
g.setColor(Color.RED);
break;
}
g.fillOval(b.getX(),b.getY(),10,10);
}
}
synchronized (enemies) {
for (Tank e : enemies) {
drawTank(e.getX(), e.getY(), g, e.getDir(), 1);
}
}
}
public void drawTank(int x,int y,Graphics g,int dir, int type){
// type决定颜色
switch (type){
case 0:// 玩家坦克
g.setColor(Color.cyan);
break;
case 1:// 敌人坦克
g.setColor(Color.magenta);
break;
default:
g.setColor(Color.GRAY);
}
// dir决定方向 0上,1左,2右,3下
switch (dir){
case 0:
g.fill3DRect(x,y,10,60,false);
g.fill3DRect(x + 30,y,10,60,false);
g.fill3DRect(x + 10,y + 10,20,40,false);
g.fillOval(x + 10,y + 20,20,20);
g.drawLine(x + 20,y + 30,x + 20,y);
break;
case 1:
g.fill3DRect(x,y,60,10,false);
g.fill3DRect(x,y + 30,60,10,false);
g.fill3DRect(x + 10,y + 10,40,20,false);
g.fillOval(x + 20,y + 10,20,20);
g.drawLine(x + 30,y + 20,x ,y + 20);
break;
case 2:
g.fill3DRect(x,y,60,10,false);
g.fill3DRect(x,y + 30,60,10,false);
g.fill3DRect(x + 10,y + 10,40,20,false);
g.fillOval(x + 20,y + 10,20,20);
g.drawLine(x + 30,y + 20,x + 60,y + 20);
break;
case 3:
g.fill3DRect(x,y,10,60,false);
g.fill3DRect(x + 30,y,10,60,false);
g.fill3DRect(x + 10,y + 10,20,40,false);
g.fillOval(x + 10,y + 20,20,20);
g.drawLine(x + 20,y + 30,x + 20,y + 60);
}
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
if(!player.isAlived())
return;
if(e.getKeyCode() == KeyEvent.VK_J){
//System.out.println("射击!");
player.shoot();
}
//dir决定方向 0上,1左,2右,3下
if(e.getKeyCode() == KeyEvent.VK_LEFT || e.getKeyCode() == KeyEvent.VK_A){
player.setDir(1);
player.moveLeft();
}
if(e.getKeyCode() == KeyEvent.VK_RIGHT || e.getKeyCode() == KeyEvent.VK_D){
player.setDir(2);
player.moveRight();
}
if(e.getKeyCode() == KeyEvent.VK_UP || e.getKeyCode() == KeyEvent.VK_W){
player.setDir(0);
player.moveUp();
}
if(e.getKeyCode() == KeyEvent.VK_DOWN || e.getKeyCode() == KeyEvent.VK_S){
player.setDir(3);
player.moveDown();
}
this.repaint();
}
@Override
public void keyReleased(KeyEvent e) {
}
}
TankGame类,用于创建坦克大战游戏的窗口,并让用户在控制台选择是新游戏还是继续上一局游戏
package TankGame;
import javax.swing.*;
import java.util.Scanner;
public class TankGame extends JFrame {
MyPanel myPanel = null;
Timer timer = null;
Checker checker = null;
Saver saver = null;
int startMode = 0;
public TankGame(int startMode){
myPanel = new MyPanel(startMode);
timer = new Timer(myPanel.player.getBullets(),myPanel);
checker = new Checker(myPanel.player.getBullets(),myPanel);
saver = new Saver(myPanel);
saver.setDaemon(true);
timer.setDaemon(true);
checker.setDaemon(true);
timer.start();
checker.start();
saver.start();
// 窗口加入画布
this.add(myPanel);
this.setSize(1000,750);
// 设置窗口退出方式
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addKeyListener(myPanel);
// 显示窗口
this.setVisible(true);
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
//while(true){
System.out.println("选择你想要开始的方式:");
System.out.println("1. 新游戏");
System.out.println("2. 上一次自动存档游戏");
int input = scanner.nextInt();
switch (input){
case 1:
new TankGame();
break;
case 2:
new TankGame(1);
break;
default:
new TankGame();
break;
}
//}
}
public TankGame(){
myPanel = new MyPanel();
timer = new Timer(myPanel.player.getBullets(),myPanel);
checker = new Checker(myPanel.player.getBullets(),myPanel);
saver = new Saver(myPanel);
saver.setDaemon(true);
timer.setDaemon(true);
checker.setDaemon(true);
timer.start();
checker.start();
saver.start();
// 窗口加入画布
this.add(myPanel);
this.setSize(1000,750);
// 设置窗口退出方式
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.addKeyListener(myPanel);
// 显示窗口
this.setVisible(true);
}
}
Tank类,用于实现坦克有关的所有行为,包括坦克的移动、发射子弹、判断是否碰撞等
package TankGame;
import java.io.Serializable;
import java.util.Vector;
public class Tank implements Collied , Serializable {
private int x;
private int y;
private int dir = 3; dir决定方向 0上,1左,2右,3下
private int speed = 3;
private int shootType = 0;
private boolean isAlived = true;
private boolean restricted = false;
public boolean isRestricted() {
return restricted;
}
public void setRestrict(boolean restricted) {
this.restricted = restricted;
}
public boolean isAlived() {
return isAlived;
}
public void setAlived(boolean alived) {
isAlived = alived;
}
public boolean AinB(Tank t1, Tank t2){
if(t1.getX() < t2.getX() || t1.getY() < t2.getY()){
return false;
}
int t1x = t1.getX() + (t1.dir == 1 || t1.dir == 2 ? 0 : 20);
int t1y = t1.getY() + (t1.dir == 1 || t1.dir == 2 ? 20 : 0);
if(t2.dir == 0 || t2.dir == 3){
return ((t1x - t2.getX() < 40 && t1y - t2.getY() < 60));
}else{
return t1x - t2.getX() < 60 && t1y - t2.getY() < 40;
}
}
@Override
public boolean onCollision(Object o1, Object o2) {
if(o1 instanceof Tank && o2 instanceof Tank){
Tank t1 = (Tank) o1;
Tank t2 = (Tank) o2;
return AinB(t1,t2) || AinB(t2,t1);
}
return false;
}
public int getShootType() {
return shootType;
}
public void setShootType(int shootType) {
this.shootType = shootType;
}
private static Vector<Bullet> bullets= new Vector<>();
public static Vector<Bullet> getBullets() {
return bullets;
}
public void shoot(){
//bullets.add(new Bullet(x,y,0,dir,10));
switch (dir){
case 0:
bullets.add(new Bullet(x + 15,y,shootType,dir,10));
break;
case 1:
bullets.add(new Bullet(x ,y + 15,shootType,dir,10));
break;
case 2:
bullets.add(new Bullet(x + 60,y + 15,shootType,dir,10));
break;
case 3:
bullets.add(new Bullet(x + 15,y + 60,shootType,dir,10));
break;
}
}
dir决定方向 0上,1左,2右,3下
public void moveLeft(){
x -= speed;
if(x < 0 ){
dir = 2;
x += speed;
}
if(restricted){
x += (int)(1.5 * speed);
}
}
public void moveRight(){
x += speed;
if(x >= 930){
dir = 1;
x -= speed;
}
if(restricted){
x -= (int)(1.5 * speed);
}
}
public void moveUp(){
y -= speed;
if(y < 0 ){
dir = 3;
y += speed;
}
if(restricted){
y += (int)(1.5 * speed);
}
}
public void moveDown(){
y += speed;
if(y >= 680 ){
dir = 0;
y -= speed;
}
if(restricted){
y -= (int)(1.5 * speed);
}
}
public int getDir() {
return dir;
}
public void setDir(int dir) {
this.dir = dir;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public Tank(int x, int y) {
this.x = x;
this.y = y;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}
Player类,作为Tank类的子类,用于定义一些玩家坦克特有的属性和方法
package TankGame;
public class Player extends Tank{
public Player(int x,int y){
super(x,y);
setSpeed(5);
}
}
RedEnemy类,作为Tank类的子类,用于定义一些敌方坦克特有的属性和方法
package TankGame;
public class RedEnemy extends Tank{
public RedEnemy(int x, int y) {
super(x, y);
setShootType(1);
}
public int moveTime = 500;
public int shootTime = 1000;
public final int MOVETIME = 500;
public final int SHOOTTIME = 1000;
}
Bullet类,用于定义坦克发射的子弹的相关属性和方法
package TankGame;
public class Bullet implements Collied{
private int x;
private int y;
private int type;
private int dir;
private int speed;
@Override
public boolean onCollision(Object o1, Object o2) {
if(o2 instanceof Tank){
Tank t = (Tank) o2;
Bullet b = (Bullet) o1;
if(b.getX() < t.getX() || b.getY() < t.getY()) return false;
if(t.getDir() == 0 || t.getDir() == 3){
return (b.getX() - t.getX() < 40 && b.getY() - t.getY() < 60);
}else{
return b.getX() - t.getX() < 60 && b.getY() - t.getY() < 40;
}
}
return false;
}
public int getSpeed() {
return speed;
}
public void setSpeed(int speed) {
this.speed = speed;
}
public Bullet(int x, int y, int type, int dir, int speed) {
this.x = x;
this.y = y;
this.type = type;
this.dir = dir; // dir决定方向 0上,1左,2右,3下
this.speed = speed;
}
public Bullet(int x, int y) {
this.x = x;
this.y = y;
}
public int getDir() {
return dir;
}
public void setDir(int dir) {
this.dir = dir;
}
public Bullet(int x, int y, int type, int dir) {
this.dir = dir;
this.type = type;
this.y = y;
this.x = x;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public void moveLeft(){
x -= speed;
}
public void moveRight(){
x += speed;
}
public void moveUp(){
y -= speed;
}
public void moveDown(){
y += speed;
}
}
Checker类,用于实现碰撞检测、游戏资源回收等功能
package TankGame;
import java.util.Vector;
public class Checker extends Thread{
private Vector<Bullet> bullets = null;
private MyPanel panel = null;
private Vector<Bullet> removeBulletArr = new Vector<>();
private Vector<RedEnemy> removeEnemiesArr = new Vector<>();
private Vector<RedEnemy> enemies;
public Checker(Vector<Bullet> bullets) {
this.bullets = bullets;
}
public Checker(Vector<Bullet> bullets,MyPanel panel) {
this.bullets = bullets;
this.panel = panel;
this.enemies = panel.getEnemies();
}
@Override
public void run() {
while(true){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
//碰撞判断
synchronized (bullets){
//System.out.print("A");
synchronized (enemies){
//System.out.println("B");
//检查碰撞
for(Bullet b : bullets){
if(b.getType() != 0 && b.onCollision(b,panel.player)){
panel.player.setAlived(false);
//panel.setPoints(panel.getPoints());
}
if(b.getType() == 1) continue;
for(RedEnemy e : enemies) {
if (!e.isAlived()) continue;
if (b.onCollision(b, e)) {
removeBulletArr.add(b);
e.setAlived(false);
removeEnemiesArr.add(e);
panel.setPoints(panel.getPoints() + 10);
}
}
}
}
}
synchronized (enemies){
boolean plCanMove = true;
for(int i = 0;i < enemies.size();++i){
RedEnemy e1 = enemies.get(i);
boolean e1CanMove = true;
for(int j = 0;j < enemies.size();++j) {
if(j == i) continue;
RedEnemy e2 = enemies.get(j);
if (e1.onCollision(e1, e2)) {
e1.setRestrict(true);
e2.setRestrict(true);
e1CanMove = false;
}
}
Tank t = panel.player;
if (e1.onCollision(e1, t)) {
e1.setRestrict(true);
t.setRestrict(true);
e1CanMove = false;
plCanMove = false;
}
if(e1CanMove){
e1.setRestrict(false);
}
}
if(plCanMove){
panel.player.setRestrict(false);
}
}
synchronized (bullets){
//System.out.println(bullets.size());
for(Bullet b : removeBulletArr){
bullets.remove(b);
}
removeBulletArr.clear();
}
synchronized (enemies){
//System.out.println(bullets.size());
for(RedEnemy e : removeEnemiesArr){
enemies.remove(e);
}
removeEnemiesArr.clear();
}
//无敌
//panel.player.setAlived(true);
//检查子弹是否超出边界
synchronized (bullets){
//System.out.println(bullets.size());
for(Bullet b : bullets){
if(b.getY() <= 0 || b.getY() >= 750 || b.getX() <= 0 || b.getX() >= 1000){
removeBulletArr.add(b);
}
}
for(Bullet b : removeBulletArr){
bullets.remove(b);
}
removeBulletArr.clear();
}
panel.repaint();
}
}
}
Collied类,用于定义一个碰撞检测的类,其中包含一个方法,可以检测两个对象是否碰撞
package TankGame;
public interface Collied {
// 判断是否碰撞的方法
boolean onCollision(Object o1,Object o2);
}
Timer类,用于模拟游戏时间流逝,然后根据时间的流逝更新子弹和坦克的位置
package TankGame;
import java.awt.*;
import java.util.Vector;
public class Timer extends Thread{
private Vector<Bullet> bullets= new Vector<>();
private MyPanel panel = null;
private int deltaTime = 25;
Vector<RedEnemy> enemies = null;
public Timer(Vector<Bullet> bullets) {
this.bullets = bullets;
}
public Timer(Vector<Bullet> bullets,MyPanel panel) {
this.bullets = bullets;
this.panel = panel;
enemies = panel.enemies;
}
public int getDeltaTime() {
return deltaTime;
}
public void setDeltaTime(int deltaTime) {
this.deltaTime = deltaTime;
}
@Override
public void run() {
while(true){// 40帧左右
try {
Thread.sleep(deltaTime);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
// 子弹运动
synchronized (bullets){
for(Bullet b : bullets){
switch (b.getDir()){
dir决定方向 0上,1左,2右,3下
case 0:
b.moveUp();
break;
case 1:
b.moveLeft();
break;
case 2:
b.moveRight();
break;
case 3:
b.moveDown();
break;
}
}
}
// 敌人运动
int nextDir = 0;
synchronized (bullets) {
synchronized (enemies) {
for (RedEnemy t : enemies) {
if (!t.isAlived()) {
continue;
}
t.moveTime -= deltaTime;
t.shootTime -= deltaTime;
if (t.shootTime < 0) {
t.shoot();
t.shootTime = t.SHOOTTIME;
}
if (t.moveTime < 0) {
t.moveTime = t.MOVETIME;
nextDir = (int) (Math.random() * 4);
} else {
nextDir = t.getDir();
}
t.setDir(nextDir);
switch (nextDir) {
case 0:
t.moveUp();
break;
case 1:
t.moveLeft();
break;
case 2:
t.moveRight();
break;
case 3:
t.moveDown();
break;
}
}
//System.out.println("release e");
}
}
panel.repaint();
}
}
}
Saver类,用于保存游戏状态到指定文件中去
package TankGame;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.util.Vector;
public class Saver extends Thread{
private MyPanel panel = null;
private int saveInterval = 10000;
private String saveAddress = "d:\\save.dat";
private Tank player = null;
private Vector<RedEnemy> enemies = null;
public Saver(MyPanel panel) {
this.panel = panel;
this.player = panel.player;
this.enemies = panel.getEnemies();
}
@Override
public void run() {
while(true){
try {
Thread.sleep(saveInterval);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
try {
ObjectOutputStream op = new ObjectOutputStream(new FileOutputStream(saveAddress));
op.writeInt(panel.getPoints());
op.writeObject(player);
op.writeInt(enemies.size());
synchronized (enemies ){
for(int i = 0;i < enemies.size();++i){
op.writeObject(enemies.get(i));
}
}
op.close();
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
}
}
}
}
项目源于视频:https://www.bilibili.com/video/BV1fh411y7R8?spm_id_from=333.788.videopod.episodes&vd_source=16bf0c507e4a78c3ca31a05dff1bee4e&p=569